diff --git a/PowerShell-Win.sln b/PowerShell-Win.sln
index f627cb30301..35a5c99e93d 100644
--- a/PowerShell-Win.sln
+++ b/PowerShell-Win.sln
@@ -21,6 +21,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Management.Infras
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.PowerShell.Commands.Diagnostics", "src\Microsoft.PowerShell.Commands.Diagnostics\Microsoft.PowerShell.Commands.Diagnostics.csproj", "{439A24FC-8E0A-48B6-8227-44C297311F49}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.PowerShell.PSReadLine", "src\Microsoft.PowerShell.PSReadLine\Microsoft.PowerShell.PSReadLine.csproj", "{07BFD271-8992-4F34-9091-6CFC3E224A24}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.WSMan.Management", "src\Microsoft.WSMan.Management\Microsoft.WSMan.Management.csproj", "{8F63D134-E413-4181-936D-D82F3F5F1D85}"
EndProject
diff --git a/build.psm1 b/build.psm1
index e013cc03056..dfbded01d20 100644
--- a/build.psm1
+++ b/build.psm1
@@ -521,7 +521,9 @@ Fix steps:
# handle Restore
if ($Restore -or -not (Test-Path "$($Options.Top)/obj/project.assets.json")) {
- $srcProjectDirs = @($Options.Top, "$PSScriptRoot/src/TypeCatalogGen", "$PSScriptRoot/src/ResGen", "$PSScriptRoot/src/Modules/PSGalleryModules.csproj")
+ log "Run dotnet restore"
+
+ $srcProjectDirs = @($Options.Top, "$PSScriptRoot/src/TypeCatalogGen", "$PSScriptRoot/src/ResGen")
$testProjectDirs = Get-ChildItem "$PSScriptRoot/test/*.csproj" -Recurse | ForEach-Object { [System.IO.Path]::GetDirectoryName($_) }
$RestoreArguments = @("--verbosity")
@@ -531,11 +533,7 @@ Fix steps:
$RestoreArguments += "quiet"
}
- ($srcProjectDirs + $testProjectDirs) | ForEach-Object {
- log "Run dotnet restore $_ $RestoreArguments"
-
- Start-NativeExecution { dotnet restore $_ $RestoreArguments }
- }
+ ($srcProjectDirs + $testProjectDirs) | ForEach-Object { Start-NativeExecution { dotnet restore $_ $RestoreArguments } }
}
# handle ResGen
@@ -648,11 +646,17 @@ function Restore-PSModuleToBuild
$CI
)
+ $ProgressPreference = "SilentlyContinue"
log "Restore PowerShell modules to $publishPath"
$modulesDir = Join-Path -Path $publishPath -ChildPath "Modules"
- Copy-PSGalleryModules -Destination $modulesDir
+ # Restore modules from powershellgallery feed
+ Restore-PSModule -Destination $modulesDir -Name @(
+ # PowerShellGet depends on PackageManagement module, so PackageManagement module will be installed with the PowerShellGet module.
+ 'PowerShellGet'
+ 'Microsoft.PowerShell.Archive'
+ ) -SourceLocation "https://www.powershellgallery.com/api/v2/"
if($CI.IsPresent)
{
@@ -671,7 +675,6 @@ function Restore-PSPester
Restore-GitModule -Destination $Destination -Uri 'https://github.com/PowerShell/psl-pester' -Name Pester -CommitSha '1f546b6aaa0893e215e940a14f57c96f56f7eff1'
}
-
function Compress-TestContent {
[CmdletBinding()]
param(
@@ -2352,6 +2355,7 @@ function Start-CrossGen {
"Microsoft.PowerShell.Security.dll",
"Microsoft.PowerShell.CoreCLR.Eventing.dll",
"Microsoft.PowerShell.ConsoleHost.dll",
+ "Microsoft.PowerShell.PSReadLine.dll",
"System.Management.Automation.dll"
)
@@ -2460,56 +2464,102 @@ function Restore-GitModule
}
# Install PowerShell modules such as PackageManagement, PowerShellGet
-function Copy-PSGalleryModules
+function Restore-PSModule
{
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
- [string]$Destination
+ [string[]]$Name,
+
+ [Parameter(Mandatory=$true)]
+ [ValidateNotNullOrEmpty()]
+ [string]$Destination,
+
+ [string]$SourceLocation="https://powershell.myget.org/F/powershellmodule/api/v2/",
+
+ [string]$RequiredVersion
)
- if (!$Destination.EndsWith("Modules")) {
- throw "Installing to an unexpected location"
- }
+ $needRegister = $true
+ $RepositoryName = "mygetpsmodule"
- $cache = dotnet nuget locals global-packages -l
- if ($cache -match "info : global-packages: (.*)") {
- $nugetCache = $matches[1]
- }
- else {
- throw "Can't find nuget global cache"
+ # Check if the PackageManagement works in the base-oS or PowerShellCore
+ $null = Get-PackageProvider -Name NuGet -ForceBootstrap -Verbose:$VerbosePreference
+ $null = Get-PackageProvider -Name PowerShellGet -Verbose:$VerbosePreference
+
+ # Get the existing registered PowerShellGet repositories
+ $psrepos = PowerShellGet\Get-PSRepository
+
+ foreach ($repo in $psrepos)
+ {
+ if(($repo.SourceLocation -eq $SourceLocation) -or ($repo.SourceLocation.TrimEnd("/") -eq $SourceLocation.TrimEnd("/")))
+ {
+ # found a registered repository that matches the source location
+ $needRegister = $false
+ $RepositoryName = $repo.Name
+ break
+ }
}
- $psGalleryProj = [xml](Get-Content -Raw $PSScriptRoot\src\Modules\PSGalleryModules.csproj)
+ if($needRegister)
+ {
+ $regVar = PowerShellGet\Get-PSRepository -Name $RepositoryName -ErrorAction SilentlyContinue
+ if($regVar)
+ {
+ PowerShellGet\UnRegister-PSRepository -Name $RepositoryName
+ }
- foreach ($m in $psGalleryProj.Project.ItemGroup.PackageReference) {
- $name = $m.Include
- $version = $m.Version
- log "Name='$Name', Version='$version', Destination='$Destination'"
+ log "Registering PSRepository with name: $RepositoryName and sourcelocation: $SourceLocation"
+ PowerShellGet\Register-PSRepository -Name $RepositoryName -SourceLocation $SourceLocation -ErrorVariable ev -verbose
+ if($ev)
+ {
+ throw ("Failed to register repository '{0}'" -f $RepositoryName)
+ }
- # Remove the build revision from the src (nuget drops it).
- $srcVer = if ($version -match "(\d+.\d+.\d+).\d+") {
- $matches[1]
- } else {
- $version
+ $regVar = PowerShellGet\Get-PSRepository -Name $RepositoryName
+ if(-not $regVar)
+ {
+ throw ("'{0}' is not registered" -f $RepositoryName)
}
- #
- # Remove semantic version in the destination directory
- $destVer = if ($version -match "(\d+.\d+.\d+)-.+") {
- $matches[1]
- } else {
- $version
+ }
+
+ log ("Name='{0}', Destination='{1}', Repository='{2}'" -f ($Name -join ','), $Destination, $RepositoryName)
+
+ # do not output progress
+ $ProgressPreference = "SilentlyContinue"
+ $Name | ForEach-Object {
+
+ $command = @{
+ Name=$_
+ Path = $Destination
+ Repository =$RepositoryName
+ }
+
+ if($RequiredVersion)
+ {
+ $command.Add("RequiredVersion", $RequiredVersion)
}
- # Nuget seems to always use lowercase in the cache
- $src = "$nugetCache/$($name.ToLower())/$srcVer"
- $dest = "$Destination/$name/$destVer"
+ # pull down the module
+ log "running save-module $_"
+ PowerShellGet\Save-Module @command -Force
+
+ # Remove PSGetModuleInfo.xml file
+ Find-Module -Name $_ -Repository $RepositoryName -IncludeDependencies | ForEach-Object {
+ Remove-Item -Path $Destination\$($_.Name)\*\PSGetModuleInfo.xml -Force
+ }
+ }
- Remove-Item -Force -ErrorAction Ignore -Recurse "$Destination/$name"
- New-Item -Path $dest -ItemType Directory -Force -ErrorAction Stop > $null
- $dontCopy = '*.nupkg', '*.nupkg.sha512', '*.nuspec', 'System.Runtime.InteropServices.RuntimeInformation.dll'
- Copy-Item -Exclude $dontCopy -Recurse $src/* $dest
+ # Clean up
+ if($needRegister)
+ {
+ $regVar = PowerShellGet\Get-PSRepository -Name $RepositoryName -ErrorAction SilentlyContinue
+ if($regVar)
+ {
+ log "Unregistering PSRepository with name: $RepositoryName"
+ PowerShellGet\UnRegister-PSRepository -Name $RepositoryName
+ }
}
}
diff --git a/docs/building/internals.md b/docs/building/internals.md
index 9373df9056e..9880df8dd99 100644
--- a/docs/building/internals.md
+++ b/docs/building/internals.md
@@ -16,7 +16,7 @@ We are calling `dotnet` tool build for `$Top` directory
### Dummy dependencies
We use dummy dependencies between projects to leverage `dotnet` build functionality.
-For example, `src\powershell-win-core\powershell-win-core.csproj` has dependency on `Microsoft.PowerShell.Commands.Diagnostics.csproj`,
+For example, `src\powershell-win-core\powershell-win-core.csproj` has dependency on `Microsoft.PowerShell.PSReadLine`,
but in reality, there is no build dependency.
Dummy dependencies allows us to build just `$Top` folder, instead of building several folders.
diff --git a/docs/testing-guidelines/CodeCoverageAnalysis.md b/docs/testing-guidelines/CodeCoverageAnalysis.md
index c1b9dae918f..3766827c575 100644
--- a/docs/testing-guidelines/CodeCoverageAnalysis.md
+++ b/docs/testing-guidelines/CodeCoverageAnalysis.md
@@ -19,6 +19,7 @@ The following table shows the status for the above commit, dated 06/18/2017
| Microsoft.PowerShell.CoreCLR.AssemblyLoadContext | 97.65% |
| Microsoft.PowerShell.CoreCLR.Eventing | 29.91% |
| Microsoft.PowerShell.LocalAccounts | 86.35% |
+| Microsoft.PowerShell.PSReadLine | 10.18% |
| Microsoft.PowerShell.Security | 44.44% |
| Microsoft.WSMan.Management | 4.91% |
| System.Management.Automation | 50.42% |
@@ -51,6 +52,10 @@ The following table shows the status for the above commit, dated 06/18/2017
- [ ] Add tests for ETW events. [#4156](https://github.com/PowerShell/PowerShell/issues/4156)
+### Microsoft.PowerShell.PSReadLine
+
+- [ ] We need tests from PSReadline repo or ignore coverage data for this module. (This will be filtered out.)
+
### Microsoft.PowerShell.Security
- [ ] Add tests for *-Acl cmdlets. [4157] (https://github.com/PowerShell/PowerShell/issues/4157)
diff --git a/nuget.config b/nuget.config
index 74cb0168d2f..ca674f6c218 100644
--- a/nuget.config
+++ b/nuget.config
@@ -5,6 +5,5 @@
-
diff --git a/src/Microsoft.PowerShell.PSReadLine/AssemblyInfo.cs b/src/Microsoft.PowerShell.PSReadLine/AssemblyInfo.cs
new file mode 100644
index 00000000000..f77c6653060
--- /dev/null
+++ b/src/Microsoft.PowerShell.PSReadLine/AssemblyInfo.cs
@@ -0,0 +1,19 @@
+/********************************************************************++
+Copyright (c) Microsoft Corporation. All rights reserved.
+--********************************************************************/
+
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTrademark("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("36053fb0-0bd0-4a1c-951c-b2ec109deca3")]
diff --git a/src/Microsoft.PowerShell.PSReadLine/BasicEditing.cs b/src/Microsoft.PowerShell.PSReadLine/BasicEditing.cs
new file mode 100644
index 00000000000..377aa154cd1
--- /dev/null
+++ b/src/Microsoft.PowerShell.PSReadLine/BasicEditing.cs
@@ -0,0 +1,579 @@
+/********************************************************************++
+Copyright (c) Microsoft Corporation. All rights reserved.
+--********************************************************************/
+
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+using System.Linq;
+using System.Management.Automation;
+using System.Management.Automation.Language;
+using System.Management.Automation.Runspaces;
+using Microsoft.PowerShell.Internal;
+
+namespace Microsoft.PowerShell
+{
+ public partial class PSConsoleReadLine
+ {
+ ///
+ /// Insert the key
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void SelfInsert(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ if (!key.HasValue)
+ {
+ return;
+ }
+
+ if (arg is int)
+ {
+ var count = (int)arg;
+ if (count <= 0)
+ return;
+ if (count > 1)
+ {
+ var toInsert = new string(key.Value.KeyChar, count);
+ if (_singleton._visualSelectionCommandCount > 0)
+ {
+ int start, length;
+ _singleton.GetRegion(out start, out length);
+ Replace(start, length, toInsert);
+ }
+ else
+ {
+ Insert(toInsert);
+ }
+ return;
+ }
+ }
+
+ if (_singleton._visualSelectionCommandCount > 0)
+ {
+ int start, length;
+ _singleton.GetRegion(out start, out length);
+ Replace(start, length, new string(key.Value.KeyChar, 1));
+ }
+ else
+ {
+ Insert(key.Value.KeyChar);
+ }
+
+ }
+
+ ///
+ /// Reverts all of the input to the current input.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void RevertLine(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ if (_singleton._statusIsErrorMessage)
+ {
+ // After an edit, clear the error message
+ _singleton.ClearStatusMessage(render: false);
+ }
+
+ while (_singleton._undoEditIndex > 0)
+ {
+ _singleton._edits[_singleton._undoEditIndex - 1].Undo();
+ _singleton._undoEditIndex--;
+ }
+ _singleton.Render();
+ }
+
+ ///
+ /// Cancel the current input, leaving the input on the screen,
+ /// but returns back to the host so the prompt is evaluated again.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void CancelLine(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ _singleton.ClearStatusMessage(false);
+ _singleton._current = _singleton._buffer.Length;
+ // We want to display ^C to show the line was canceled. Instead of appending ^C
+ // (or (char)3), we append 2 spaces so we don't affect tokenization too much, e.g.
+ // changing a keyword to a command.
+ _singleton._buffer.Append(" ");
+ _singleton.ReallyRender();
+
+ // Now that we've rendered with this extra spaces, go back and replace the spaces
+ // with ^C colored in red (so it stands out.)
+ var coordinates = _singleton.ConvertOffsetToCoordinates(_singleton._current);
+ var console = _singleton._console;
+ var consoleBuffer = _singleton._consoleBuffer;
+ int i = (coordinates.Y - _singleton._initialY) * console.BufferWidth + coordinates.X;
+ consoleBuffer[i].UnicodeChar = '^';
+ consoleBuffer[i].ForegroundColor = ConsoleColor.Red;
+ consoleBuffer[i].BackgroundColor = console.BackgroundColor;
+ consoleBuffer[i+1].UnicodeChar = 'C';
+ consoleBuffer[i+1].ForegroundColor = ConsoleColor.Red;
+ consoleBuffer[i+1].BackgroundColor = console.BackgroundColor;
+ console.WriteBufferLines(consoleBuffer, ref _singleton._initialY);
+
+ var y = coordinates.Y + 1;
+ _singleton.PlaceCursor(0, ref y);
+ _singleton._buffer.Clear(); // Clear so we don't actually run the input
+ _singleton._current = 0; // If Render is called, _current must be correct.
+ _singleton._currentHistoryIndex = _singleton._history.Count;
+ _singleton._inputAccepted = true;
+ }
+
+ ///
+ /// Like ForwardKillLine - deletes text from the point to the end of the line,
+ /// but does not put the deleted text in the kill ring.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void ForwardDeleteLine(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ var current = _singleton._current;
+ var buffer = _singleton._buffer;
+ if (buffer.Length > 0 && current < buffer.Length)
+ {
+ int length = buffer.Length - current;
+ var str = buffer.ToString(current, length);
+ _singleton.SaveEditItem(EditItemDelete.Create(str, current));
+ buffer.Remove(current, length);
+ _singleton.Render();
+ }
+ }
+
+ ///
+ /// Like BackwardKillLine - deletes text from the point to the start of the line,
+ /// but does not put the deleted text in the kill ring.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void BackwardDeleteLine(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ if (_singleton._current > 0)
+ {
+ _singleton._clipboard = _singleton._buffer.ToString(0, _singleton._current);
+ _singleton.SaveEditItem(EditItemDelete.Create(_singleton._clipboard, 0));
+ _singleton._buffer.Remove(0, _singleton._current);
+ _singleton._current = 0;
+ _singleton.Render();
+ }
+ }
+
+ ///
+ /// Delete the character before the cursor.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void BackwardDeleteChar(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ if (_singleton._visualSelectionCommandCount > 0)
+ {
+ int start, length;
+ _singleton.GetRegion(out start, out length);
+ Delete(start, length);
+ return;
+ }
+
+ if (_singleton._buffer.Length > 0 && _singleton._current > 0)
+ {
+ int qty = (arg is int) ? (int) arg : 1;
+ qty = Math.Min(qty, _singleton._current);
+
+ int startDeleteIndex = _singleton._current - qty;
+ _singleton.SaveEditItem(
+ EditItemDelete.Create(
+ _singleton._buffer.ToString(startDeleteIndex, qty),
+ startDeleteIndex,
+ BackwardDeleteChar,
+ arg)
+ );
+ _singleton.SaveToClipboard(startDeleteIndex, qty);
+ _singleton._buffer.Remove(startDeleteIndex, qty);
+ _singleton._current = startDeleteIndex;
+ _singleton.Render();
+ }
+ else
+ {
+ Ding();
+ }
+ }
+
+ private void DeleteCharImpl(int qty, bool orExit)
+ {
+ qty = Math.Min(qty, _singleton._buffer.Length + 1 + ViEndOfLineFactor - _singleton._current);
+
+ if (_visualSelectionCommandCount > 0)
+ {
+ int start, length;
+ GetRegion(out start, out length);
+ Delete(start, length);
+ return;
+ }
+
+ if (_buffer.Length > 0)
+ {
+ if (_current < _buffer.Length)
+ {
+ SaveEditItem(EditItemDelete.Create(_buffer.ToString(_current, qty), _current, DeleteChar, qty));
+ SaveToClipboard(_current, qty);
+ _buffer.Remove(_current, qty);
+ if (_current >= _buffer.Length)
+ {
+ _current = Math.Max(0, _buffer.Length - 1);
+ }
+ Render();
+ }
+ }
+ else if (orExit)
+ {
+ throw new ExitException();
+ }
+ }
+
+ ///
+ /// Delete the character under the cursor.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void DeleteChar(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ int qty = (arg is int) ? (int)arg : 1;
+
+ _singleton.DeleteCharImpl(qty, orExit: false);
+ }
+
+ ///
+ /// Delete the character under the cursor, or if the line is empty, exit the process
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void DeleteCharOrExit(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ _singleton.DeleteCharImpl(1, orExit: true);
+ }
+
+ private bool AcceptLineImpl(bool validate)
+ {
+ ParseInput();
+ if (_parseErrors.Any(e => e.IncompleteInput))
+ {
+ Insert('\n');
+ return false;
+ }
+
+ // If text was pasted, for performance reasons we skip rendering for some time,
+ // but if input is accepted, we won't have another chance to render.
+ //
+ // Also - if there was an emphasis, we want to clear that before accepting
+ // and that requires rendering.
+ bool renderNeeded = _emphasisStart >= 0 || _queuedKeys.Count > 0;
+
+ _emphasisStart = -1;
+ _emphasisLength = 0;
+
+ var insertionPoint = _current;
+ // Make sure cursor is at the end before writing the line
+ _current = _buffer.Length;
+
+ if (renderNeeded)
+ {
+ ReallyRender();
+ }
+
+ // Only run validation if we haven't before. If we have and status line shows an error,
+ // treat that as a -Force and accept the input so it is added to history, and PowerShell
+ // can report an error as it normally does.
+ if (validate && !_statusIsErrorMessage)
+ {
+ var errorMessage = Validate(_ast);
+ if (!string.IsNullOrWhiteSpace(errorMessage))
+ {
+ // If there are more keys, assume the user pasted with a right click and
+ // we should insert a newline even though validation failed.
+ if (_queuedKeys.Count > 0)
+ {
+ // Validation may have moved the cursor. Because there are queued
+ // keys, we need to move the cursor back to the correct place, and
+ // ignore where validation put the cursor because the queued keys
+ // will be inserted in the wrong place.
+ SetCursorPosition(insertionPoint);
+ Insert('\n');
+ }
+ _statusLinePrompt = "";
+ _statusBuffer.Append(errorMessage);
+ _statusIsErrorMessage = true;
+ Render();
+ return false;
+ }
+ }
+
+ if (_statusIsErrorMessage)
+ {
+ ClearStatusMessage(render: true);
+ }
+
+ var coordinates = ConvertOffsetToCoordinates(_current);
+ var y = coordinates.Y + 1;
+ PlaceCursor(0, ref y);
+ _inputAccepted = true;
+ return true;
+ }
+
+ class CommandValidationVisitor : AstVisitor
+ {
+ private readonly Ast _rootAst;
+ internal string detectedError;
+
+ internal CommandValidationVisitor(Ast rootAst)
+ {
+ _rootAst = rootAst;
+ }
+
+ public override AstVisitAction VisitCommand(CommandAst commandAst)
+ {
+ var commandName = commandAst.GetCommandName();
+ if (commandName != null)
+ {
+ if (_singleton._engineIntrinsics != null)
+ {
+ var commandInfo = _singleton._engineIntrinsics.InvokeCommand.GetCommand(commandName, CommandTypes.All);
+ if (commandInfo == null && !_singleton.UnresolvedCommandCouldSucceed(commandName, _rootAst))
+ {
+ _singleton._current = commandAst.CommandElements[0].Extent.EndOffset;
+ detectedError = string.Format(CultureInfo.CurrentCulture, PSReadLineResources.CommandNotFoundError, commandName);
+ return AstVisitAction.StopVisit;
+ }
+ }
+
+ if (commandAst.CommandElements.Any(e => e is ScriptBlockExpressionAst))
+ {
+ if (_singleton._options.CommandsToValidateScriptBlockArguments == null ||
+ !_singleton._options.CommandsToValidateScriptBlockArguments.Contains(commandName))
+ {
+ return AstVisitAction.SkipChildren;
+ }
+ }
+ }
+
+ if (_singleton._options.CommandValidationHandler != null)
+ {
+ try
+ {
+ _singleton._options.CommandValidationHandler(commandAst);
+ }
+ catch (Exception e)
+ {
+ detectedError = e.Message;
+ }
+ }
+
+ return !string.IsNullOrWhiteSpace(detectedError)
+ ? AstVisitAction.StopVisit
+ : AstVisitAction.Continue;
+ }
+ }
+
+ private string Validate(Ast rootAst)
+ {
+ if (_parseErrors != null && _parseErrors.Length > 0)
+ {
+ // Move the cursor to the point of error
+ _current = _parseErrors[0].Extent.EndOffset;
+ return _parseErrors[0].Message;
+ }
+
+ var validationVisitor = new CommandValidationVisitor(rootAst);
+ rootAst.Visit(validationVisitor);
+ if (!string.IsNullOrWhiteSpace(validationVisitor.detectedError))
+ {
+ return validationVisitor.detectedError;
+ }
+
+ return null;
+ }
+
+ private bool UnresolvedCommandCouldSucceed(string commandName, Ast rootAst)
+ {
+ // This is a little hacky, but we check for a few things where part of the current
+ // command defines/imports new commands that PowerShell might not yet know about.
+ // There is little reason to go to great lengths at being correct here, validation
+ // is just a small usability tweak to avoid cluttering up history - PowerShell
+ // will report errors for stuff we actually let through.
+
+ // Do we define a function matching the command name?
+ var fnDefns = rootAst.FindAll(ast => ast is FunctionDefinitionAst, true).OfType();
+ if (fnDefns.Any(fnDefnAst => fnDefnAst.Name.Equals(commandName, StringComparison.OrdinalIgnoreCase)))
+ {
+ return true;
+ }
+
+ var cmdAsts = rootAst.FindAll(ast => ast is CommandAst, true).OfType();
+ foreach (var cmdAst in cmdAsts)
+ {
+ // If we dot source something, we can't in general know what is being
+ // dot sourced so just assume the unresolved command will work.
+ // If we use the invocation operator, allow that because an expression
+ // is being invoked and it's reasonable to just allow it.
+ if (cmdAst.InvocationOperator != TokenKind.Unknown)
+ {
+ return true;
+ }
+
+ // Are we importing a module or being tricky with Invoke-Expression? Let those through.
+ var candidateCommand = cmdAst.GetCommandName();
+ if (candidateCommand.Equals("Import-Module", StringComparison.OrdinalIgnoreCase)
+ || candidateCommand.Equals("ipmo", StringComparison.OrdinalIgnoreCase)
+ || candidateCommand.Equals("Invoke-Expression", StringComparison.OrdinalIgnoreCase)
+ || candidateCommand.Equals("iex", StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+ }
+
+ if (commandName.Length == 1)
+ {
+ switch (commandName[0])
+ {
+ // The following are debugger commands that should be accepted if we're debugging
+ // because the console host will interpret these commands directly.
+ case 's': case 'v': case 'o': case 'c': case 'q': case 'k': case 'l':
+ case 'S': case 'V': case 'O': case 'C': case 'Q': case 'K': case 'L':
+ case '?': case 'h': case 'H':
+ // Ideally we would check $PSDebugContext, but it is set at function
+ // scope, and because we're in a module, we can't find that variable
+ // (arguably a PowerShell issue.)
+ // NestedPromptLevel is good enough though - it's rare to be in a nested.
+ var nestedPromptLevel = _engineIntrinsics.SessionState.PSVariable.GetValue("NestedPromptLevel");
+ if (nestedPromptLevel is int)
+ {
+ return ((int)nestedPromptLevel) > 0;
+ }
+ break;
+ }
+ }
+
+ return false;
+ }
+
+ static bool StaticParameterBindingSupported(CommandInfo commandInfo)
+ {
+ var aliasInfo = commandInfo as AliasInfo;
+
+ if (aliasInfo != null)
+ {
+ commandInfo = aliasInfo.ResolvedCommand;
+ }
+
+ return (commandInfo is ExternalScriptInfo)
+ || (commandInfo is CmdletInfo)
+ || (commandInfo is FunctionInfo);
+ }
+
+ ///
+ /// Attempt to execute the current input. If the current input is incomplete (for
+ /// example there is a missing closing parenthesis, bracket, or quote, then the
+ /// continuation prompt is displayed on the next line and PSReadline waits for
+ /// keys to edit the current input.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void AcceptLine(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ _singleton.AcceptLineImpl(false);
+ }
+
+ ///
+ /// Attempt to execute the current input. If the current input is incomplete (for
+ /// example there is a missing closing parenthesis, bracket, or quote, then the
+ /// continuation prompt is displayed on the next line and PSReadline waits for
+ /// keys to edit the current input.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void ValidateAndAcceptLine(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ _singleton.AcceptLineImpl(true);
+ }
+
+ ///
+ /// Attempt to execute the current input. If it can be executed (like AcceptLine),
+ /// then recall the next item from history the next time Readline is called.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void AcceptAndGetNext(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ if (_singleton.AcceptLineImpl(false))
+ {
+ if (_singleton._currentHistoryIndex < (_singleton._history.Count - 1))
+ {
+ _singleton._getNextHistoryIndex = _singleton._currentHistoryIndex + 1;
+ }
+ else
+ {
+ Ding();
+ }
+ }
+ }
+
+ ///
+ /// The continuation prompt is displayed on the next line and PSReadline waits for
+ /// keys to edit the current input. This is useful to enter multi-line input as
+ /// a single command even when a single line is complete input by itself.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void AddLine(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ Insert('\n');
+ }
+
+ ///
+ /// A new empty line is created above the current line regardless of where the cursor
+ /// is on the current line. The cursor moves to the beginning of the new line.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void InsertLineAbove(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ // Move the current position to the beginning of the current line and only the current line.
+ if (_singleton.LineIsMultiLine())
+ {
+ int i = Math.Max(0, _singleton._current - 1);
+ for (; i > 0; i--)
+ {
+ if (_singleton._buffer[i] == '\n')
+ {
+ i += 1;
+ break;
+ }
+ }
+
+ _singleton._current = i;
+ }
+ else
+ {
+ _singleton._current = 0;
+ }
+
+ Insert('\n');
+ PreviousLine();
+ }
+
+ ///
+ /// A new empty line is created below the current line regardless of where the cursor
+ /// is on the current line. The cursor moves to the beginning of the new line.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void InsertLineBelow(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ // Move the current position to the end of the current line and only the current line.
+ if (_singleton.LineIsMultiLine())
+ {
+ int i = _singleton._current;
+ for (; i < _singleton._buffer.Length; i++)
+ {
+ if (_singleton._buffer[i] == '\n')
+ {
+ break;
+ }
+ }
+
+ _singleton._current = i;
+ }
+ else
+ {
+ _singleton._current = _singleton._buffer.Length;
+ }
+
+ Insert('\n');
+ }
+ }
+}
diff --git a/src/Microsoft.PowerShell.PSReadLine/Changes.txt b/src/Microsoft.PowerShell.PSReadLine/Changes.txt
new file mode 100644
index 00000000000..31aa77d0c26
--- /dev/null
+++ b/src/Microsoft.PowerShell.PSReadLine/Changes.txt
@@ -0,0 +1,293 @@
+### Version 1.2
+
+New features:
+* Vi editing mode
+
+New functions:
+* InsertLineAbove
+ - A new empty line is created above the current line regardless of where the cursor
+ is on the current line. The cursor moves to the beginning of the new line.
+* InsertLineBelow
+ - A new empty line is created below the current line regardless of where the cursor
+ is on the current line. The cursor moves to the beginning of the new line.
+
+New key bindings:
+* Ctrl+Enter bound to InsertLineAbove in Windows mode
+* Ctrl+Shift+Enter bound to InsertLineBelow in Windows mode
+
+Bug fixes:
+* Home the first line of multi-line input fixed
+* CaptureScreen captures colors colors correctly instead of guessing
+* CaptureScreen scrolls the screen to ensure the selected line is visible
+* CaptureScreen allows j/k for up/down (for the vi fans)
+* Fixed uncommon syntax coloring bug with bare words that start with a variable
+* Added sample handler for F7 emulation
+* Fixed sample handler for Set-StrictMode error
+* Improved error message when errors occur in custom handlers
+
+### Version 1.1 (shipped w/ Windows 10)
+
+Breaking change:
+* Namespace PSConsoleUtilities has been renamed to Microsoft.PowerShell
+* Default history file location changed to match Windows 10
+
+### Version 1.0.0.13
+
+New features:
+* Enter now does some extra validation before accepting the input. If there are any parse
+ errors or a command is not found, an error message is displayed, but you can continue editing,
+ the erroneous line will not be added to history, and the error message will be cleared after
+ you make an edit.
+ You can press Enter a second time to force accepting the line if you choose.
+
+ If you don't like the new behavior for Enter, you can revert to the old behavior with:
+ Set-PSReadlineKeyHandler -Key Enter -Function AcceptLine
+
+Bug fixes:
+* Occasional exception with AcceptAndGetNext (Ctrl+O) followed by something other than Enter
+* Event handlers that register for the engine event PowerShell.OnIdle should work now.
+* ClearScreen now scrolls the screen to preserve as much output as possible
+* Fix exception on error input during rendering after certain keywords like process, begin, or end.
+* Fix exception after undo from menu completion
+* Support CancelLine (Ctrl+C) and Abort (Ctrl+G in emacs) to cancel DigitArgument
+* History recall now ignores command lines from other currently running sessions, but you can
+ still find command lines from those sessions when searching history.
+* Color any non-whitespace prompt character when there is an error, not just non-characters
+* ScrollDisplayToCursor works a little better now.
+
+New functions:
+* ValidateAndAcceptLine
+ Validate the command line (by making sure there are no parse errors, commands all exist,
+ and possibly other sorts of validation), display an error if any errors, but don't add
+ the command to history and clear the error after an edit.
+* DeleteCharOrExit
+ - emulate Ctrl+D in bash properly by exiting the process if the command line is empty
+* ScrollDisplayUpLine, ScrollDisplayDownLine
+ Scroll the screen up or down by a line instead of by the screen
+
+New key bindings:
+* Ctrl+D bound to DeleteCharOrExit in Emacs mode
+* Ctrl+N/Ctrl+P bound to NextHistory/PreviousHistory in Emacs mode
+* Ctrl+PageUp bound to ScrollDisplayUpLine
+* Ctrl+PageDown bound to ScrollDisplayDownLine
+* Alt+F7 bound to ClearHistory in Windows mode
+
+New options:
+* Set-PSReadlineOption
+ -ErrorForegroundColor
+ -ErrorBackgroundColor
+ Colors used when ValidateAndAcceptLine reports an error
+
+ -CommandValidationHandler
+ A delegate that is called from ValidateAndAcceptLine that can perform custom validation
+ and also fix the command line, e.g. to correct common typos.
+
+New cmdlet:
+* Remove-PSReadlineKeyHandler
+ This will remove a key binding for previously bound keys.
+
+Breaking change:
+* Demo mode removed
+ - Trying to bind functions EnableDemoMode or DisableDemoMode will result in an error.
+
+### Version 1.0.0.12
+
+New features:
+* Multi-line editing is now practical. Home/End go to the start/end of the current line or
+ start/end of the input in a reasonable way. Up/Down arrows will move across lines if the
+ input has multiple lines and you are not in the middle of recalling history.
+
+Bug fixes:
+* Color the prompt character if there is an error for any non-alphanumeric character
+* Fix an issue related to undo (which was commonly hit via Escape) and using history search
+* Fix a bug where PowerShell events are not written to the console until PSReadline returns
+* Fixed so PowerTab now works with PSReadline
+* Highlight from history search is cleared before accepting a line now.
+* Fixed MenuComplete so it clears the menu (which only happened on some systems)
+
+New functions:
+* NextLine
+* PreviousLine
+ - These functions are added for completeness, neither is particularly useful as the usual
+ bindings for UpArrow/DownArrow are smart enough to recall history or change lines
+ depending on the context.
+
+New key bindings:
+* F8/Shift+F8 bound to HistorySearchBackward/HistorySearchForward in Windows mode
+
+### Version 1.0.0.11
+
+Bug fixes:
+* Fixed MenuComplete to actually work
+
+### Version 1.0.0.10
+
+New features:
+* Added binding Ctrl+SpaceBar to MenuComplete in Windows and Emacs modes
+ - If you want to old behavior of Ctrl+Spacebar, bind it to PossibleCompletions
+* Added binding Alt+. (YankLastArg) to Windows mode
+* Added diagnostics when an exception occurs to help reporting bugs
+
+Bug fixes:
+* SaveHistoryPath option fixed
+* Fix ShowKeyBindings to not write blank lines
+* Fixed bug with undo
+
+### Version 1.0.0.9
+
+New features:
+* MenuComplete - like an interactive show completion
+* Automatically save history
+ - at process exit
+ - incrementally and shared across sessions
+ - don't save
+ See parameters HistorySaveStyle and HistorySavePath to Set-PSReadlineOption
+* Added sample custom binding for quickly changing directories in SamplePSReadlineProfile.ps1
+
+Bug fixes:
+* Items loaded from history work with RevertLine
+* Recalling current line after up arrow works again
+
+### Version 1.0.0.8
+
+New features:
+* SamplePSReadlineProfile.ps1 added with examples of custom key bindings
+* Word movement takes DigitArgument
+* HistoryNoDuplicates now works a little differently
+ - Duplicates are saved (it was a dubious memory optimization anyway)
+ - Recall will recall the most recently executed item instead of the first
+* When at the last word, NextWord/ForwardWord now move to the end of line instead
+ of the last character of the word.
+* HistorySearchBackward/HistorySearchForward changes behavior slightly:
+ - use emphasis like InteractiveHistorySearch
+ - cursor always moves to end like PreviousHistory/NextHistory
+* New api GetSelectionState to get the current selection (if any).
+* New functions:
+ - SelectBackwardsLine
+ - SelectLine
+ - SelectAll
+ - CopyOrCancelLine
+* New key bindings in Windows mode:
+ - Alt+0 through Alt+9 and Alt+-: DigitArgument
+ - Ctrl+R/Ctrl+S for interactive history search
+
+Bug fixes:
+* Backspace after a failed interactive history search (Ctrl+R) caused searching
+ to fail if you typed anything other than backspace.
+
+### Version 1.0.0.7
+
+New features:
+* CaptureScreen - copies selected portion of screen to clipboard in text and rtf
+* InvokePrompt - re-evaluate the prompt while preserving the current input
+* New functions to scroll the screen w/o using the mouse:
+ - ScrollScreenUp
+ - ScrollScreenDown
+ - ScrollScreenTop
+ - ScrollScreenToCursor
+* Many small bug fixes
+
+### Version 1.0.0.6
+
+New features:
+* CharacterSearch/CharacterSearchBackward
+* AcceptAndGetNext (Ctrl+O in bash)
+* Get-PSReadlineKeyHandler now returns unbound functions
+* Get-PSReadlineKeyHandler has 2 new parameters: -Bound and -Unbound
+* Set-PSReadlineKeyHandler parameter -LongDescription is now -Description
+ (not breaking because I left an alias)
+* WhatIsKey - display binding for a key
+* ShowKeyBindings - show all bound keys
+* Keyboard selection of text for cut/copy/delete. New functions:
+ - Cut
+ - Copy
+ - KillRegion
+ - SelectBackwardChar
+ - SelectForwardChar
+ - SelectBackwardWord
+ - SelectForwardWord
+ - SelectNextWord
+ - SelectShellForwardWord
+ - SelectShellBackwardWord
+
+Breaking change:
+* The properties in the output of Get-PSReadlineKeyHandler have changed.
+ This is unlikely to break anyone though.
+
+### Version 1.0.0.5
+
+New features:
+* Delimiter support in *Word functions
+* DigitArgument (Alt-0,Alt-1,Alt-2,...,Alt-9,Alt--) to pass numeric arguments
+* YankLastArg/YankNthArg to extract arguments from previous command lines
+* History search is now case insensitive with an option to make it case sensitive
+
+Bugs fixed:
+* Shift+Backspace works like Backspace
+* Ctrl+R with long search lines no longer causes big problems
+
+Behavior change:
+* Word functions now use delimiters. The previous behavior is available
+ via a Shell*Word function, e.g. instead of KillWord, use ShellKillWord.
+
+### Version 1.0.0.4
+
+New features:
+* Interactive history search (Ctrl+R)
+* Brace matching function added (GotoBrace)
+* Clear screen (Ctrl+L)
+
+Bugs fixed:
+* When showing possible completions, truncate at newline
+* Prompt before showing a large number of completions
+* Undo after paste works now
+* Long pause after clicking on X to close powershell is now fixed
+
+### Version 1.0.0.3
+
+Bugs fixed:
+* Removed CLR 4.5 dependency
+* Fix bug where console paste didn't display everything in some cases
+
+### Version 1.0.0.2
+
+New features:
+* Add a "demo mode" that shows keys pressed
+* Add ETW event source for demo mode, key logger, macro recorder etc.
+* Undo/redo
+* Get-PSReadlineOption cmdlet
+* Make specifying key handlers for builtins simpler
+* Current un-entered line is saved and recalled when cycling through history
+* Support syntax coloring of member names
+
+Bugs fixed:
+* Speed up pasting from the console
+* Fix coloring of some operators
+* Fix coloring in some command arguments
+* Ctrl-C is handled a little better
+
+Breaking changes:
+* CLR 4.5 is now required.
+* SetBufferState is gone because it doesn't support Undo/Redo
+
+### Version 1.0.0.1
+
+New features:
+* History imported when module is loaded
+* Ctrl+End/Ctrl+Home bindings emulate cmd
+* Arbitrary two key chords
+* Key handlers passed the invoking key and an optional argument
+* Made Ding public for custom handlers
+
+Bugs fixed:
+* Alternate keyboards now supported
+* Ctrl-C now properly emulates cmd
+
+Breaking changes:
+* MinimumHistoryCommandLength parameter removed from Set-PSReadlineOption
+ - Can use this instead:
+ Set-PSReadlineOption -AddToHistoryHandler { $args[0].Length -gt 3 }
+
+### Version 1.0.0.0
+
+Initial release
diff --git a/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs b/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs
new file mode 100644
index 00000000000..7146e28a0b9
--- /dev/null
+++ b/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs
@@ -0,0 +1,812 @@
+/********************************************************************++
+Copyright (c) Microsoft Corporation. All rights reserved.
+--********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Diagnostics.CodeAnalysis;
+using System.Management.Automation;
+using System.Management.Automation.Language;
+using System.Reflection;
+using System.Linq;
+
+namespace Microsoft.PowerShell
+{
+
+#pragma warning disable 1591
+
+ public enum TokenClassification
+ {
+ None,
+ Comment,
+ Keyword,
+ String,
+ Operator,
+ Variable,
+ Command,
+ Parameter,
+ Type,
+ Number,
+ Member,
+ }
+
+ public enum EditMode
+ {
+ Windows,
+ Emacs,
+ Vi,
+ }
+
+ public enum BellStyle
+ {
+ None,
+ Visual,
+ Audible
+ }
+
+ #region vi
+ public enum ViModeStyle
+ {
+ None,
+ Prompt,
+ Cursor
+ }
+
+ public enum ViMode
+ {
+ Insert,
+ Command
+ }
+ #endregion vi
+
+ public enum HistorySaveStyle
+ {
+ SaveIncrementally,
+ SaveAtExit,
+ SaveNothing
+ }
+
+ public class PSConsoleReadlineOptions
+ {
+#if UNIX // TODO: why different schemes for LINUX?
+ public const ConsoleColor DefaultCommentForegroundColor = ConsoleColor.Magenta;
+ public const ConsoleColor DefaultOperatorForegroundColor = ConsoleColor.Gray;
+ public const ConsoleColor DefaultParameterForegroundColor = ConsoleColor.Green;
+#else
+ public const ConsoleColor DefaultCommentForegroundColor = ConsoleColor.DarkGreen;
+ public const ConsoleColor DefaultOperatorForegroundColor = ConsoleColor.DarkGray;
+ public const ConsoleColor DefaultParameterForegroundColor = ConsoleColor.DarkGray;
+#endif
+ public const ConsoleColor DefaultKeywordForegroundColor = ConsoleColor.Green;
+ public const ConsoleColor DefaultStringForegroundColor = ConsoleColor.DarkCyan;
+ public const ConsoleColor DefaultVariableForegroundColor = ConsoleColor.Green;
+ public const ConsoleColor DefaultCommandForegroundColor = ConsoleColor.Yellow;
+ public const ConsoleColor DefaultTypeForegroundColor = ConsoleColor.Gray;
+ public const ConsoleColor DefaultNumberForegroundColor = ConsoleColor.White;
+ public const ConsoleColor DefaultMemberForegroundColor = ConsoleColor.Gray;
+ public const ConsoleColor DefaultEmphasisForegroundColor = ConsoleColor.Cyan;
+ public const ConsoleColor DefaultErrorForegroundColor = ConsoleColor.Red;
+
+ public const EditMode DefaultEditMode =
+#if UNIX
+ EditMode.Emacs;
+#else
+ EditMode.Windows;
+#endif
+
+ public const string DefaultContinuationPrompt = ">> ";
+
+ ///
+ /// The maximum number of commands to store in the history.
+ ///
+ public const int DefaultMaximumHistoryCount = 4096;
+
+ ///
+ /// The maximum number of items to store in the kill ring.
+ ///
+ public const int DefaultMaximumKillRingCount = 10;
+
+ ///
+ /// In Emacs, when searching history, the cursor doesn't move.
+ /// In 4NT, the cursor moves to the end. This option allows
+ /// for either behavior.
+ ///
+ public const bool DefaultHistorySearchCursorMovesToEnd = false;
+
+ ///
+ /// When displaying possible completions, either display
+ /// tooltips or display just the completions.
+ ///
+ public const bool DefaultShowToolTips = false;
+
+ ///
+ /// When ringing the bell, what frequency do we use?
+ ///
+ public const int DefaultDingTone = 1221;
+
+ public const int DefaultDingDuration = 50;
+
+ public const int DefaultCompletionQueryItems = 100;
+
+ // Default includes all characters PowerShell treats like a dash - em dash, en dash, horizontal bar
+ public const string DefaultWordDelimiters = @";:,.[]{}()/\|^&*-=+'""" + "\u2013\u2014\u2015";
+
+ ///
+ /// When ringing the bell, what should be done?
+ ///
+ public const BellStyle DefaultBellStyle = BellStyle.Audible;
+
+ public const bool DefaultHistorySearchCaseSensitive = false;
+
+ public const HistorySaveStyle DefaultHistorySaveStyle = HistorySaveStyle.SaveIncrementally;
+
+ public PSConsoleReadlineOptions(string hostName)
+ {
+ ResetColors();
+ EditMode = DefaultEditMode;
+ ContinuationPrompt = DefaultContinuationPrompt;
+ ContinuationPromptBackgroundColor = Console.BackgroundColor;
+ ContinuationPromptForegroundColor = Console.ForegroundColor;
+ ExtraPromptLineCount = DefaultExtraPromptLineCount;
+ AddToHistoryHandler = null;
+ HistoryNoDuplicates = DefaultHistoryNoDuplicates;
+ MaximumHistoryCount = DefaultMaximumHistoryCount;
+ MaximumKillRingCount = DefaultMaximumKillRingCount;
+ HistorySearchCursorMovesToEnd = DefaultHistorySearchCursorMovesToEnd;
+ ShowToolTips = DefaultShowToolTips;
+ DingDuration = DefaultDingDuration;
+ DingTone = DefaultDingTone;
+ BellStyle = DefaultBellStyle;
+ CompletionQueryItems = DefaultCompletionQueryItems;
+ WordDelimiters = DefaultWordDelimiters;
+ HistorySearchCaseSensitive = DefaultHistorySearchCaseSensitive;
+ HistorySaveStyle = DefaultHistorySaveStyle;
+
+ string historyFileName = hostName + "_history.txt";
+#if UNIX
+ // PSReadline does not have access to Utils.CorePSPlatform. Must set PSReadline path separately
+ string historyPath = System.Environment.GetEnvironmentVariable("XDG_DATA_HOME");
+
+ if (!String.IsNullOrEmpty(historyPath))
+ {
+ historyPath = System.IO.Path.Combine(historyPath, "powershell", "PSReadLine", historyFileName);
+ HistorySavePath = historyPath;
+ }
+ else
+ {
+ // History is data, so it goes into .local/share/powershell folder
+ HistorySavePath = System.IO.Path.Combine(Environment.GetEnvironmentVariable("HOME"),
+ ".local",
+ "share",
+ "powershell",
+ "PSReadLine",
+ historyFileName);
+ }
+#else
+ HistorySavePath = System.IO.Path.Combine(Environment.GetEnvironmentVariable("APPDATA"),
+ @"Microsoft\Windows\PowerShell\PSReadline\",
+ historyFileName);
+#endif
+ CommandValidationHandler = null;
+ CommandsToValidateScriptBlockArguments = new HashSet(StringComparer.OrdinalIgnoreCase)
+ {
+ "ForEach-Object", "%",
+ "Invoke-Command", "icm",
+ "Measure-Command",
+ "New-Module", "nmo",
+ "Register-EngineEvent",
+ "Register-ObjectEvent",
+ "Register-WMIEvent",
+ "Set-PSBreakpoint", "sbp",
+ "Start-Job", "sajb",
+ "Trace-Command", "trcm",
+ "Use-Transaction",
+ "Where-Object", "?", "where",
+ };
+ }
+
+ public EditMode EditMode { get; set; }
+
+ public string ContinuationPrompt { get; set; }
+ public ConsoleColor ContinuationPromptForegroundColor { get; set; }
+ public ConsoleColor ContinuationPromptBackgroundColor { get; set; }
+
+ ///
+ /// Prompts are typically 1 line, but sometimes they may span lines. This
+ /// count is used to make sure we can display the full prompt after showing
+ /// ambiguous completions
+ ///
+ public int ExtraPromptLineCount { get; set; }
+ public const int DefaultExtraPromptLineCount = 0;
+
+ ///
+ /// This handler is called before adding a command line to history.
+ /// The return value indicates if the command line should be added
+ /// to history or not.
+ ///
+ public Func AddToHistoryHandler { get; set; }
+
+ ///
+ /// This handler is called from ValidateAndAcceptLine.
+ /// If an exception is thrown, validation fails and the error is reported.
+ ///
+ public Action CommandValidationHandler { get; set; }
+
+ ///
+ /// Most commands do not accept script blocks, but for those that do,
+ /// we want to validate commands in the script block arguments.
+ /// Unfortunately, we can't know how the argument is used. In the worst
+ /// case, for commands like Get-ADUser, the script block actually
+ /// specifies a different language.
+ ///
+ /// Because we can't know ahead of time all of the commands that do
+ /// odd things with script blocks, we create a white-list of commands
+ /// that do invoke the script block - this covers the most useful cases.
+ ///
+ [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
+ public HashSet CommandsToValidateScriptBlockArguments { get; set; }
+
+ ///
+ /// When true, duplicates will not be added to the history.
+ ///
+ public bool HistoryNoDuplicates { get; set; }
+ public const bool DefaultHistoryNoDuplicates = false;
+
+ public int MaximumHistoryCount { get; set; }
+ public int MaximumKillRingCount { get; set; }
+ public bool HistorySearchCursorMovesToEnd { get; set; }
+ public bool ShowToolTips { get; set; }
+ public int DingTone { get; set; }
+ public int CompletionQueryItems { get; set; }
+ public string WordDelimiters { get; set; }
+
+ ///
+ /// When ringing the bell, how long (in ms)?
+ ///
+ public int DingDuration { get; set; }
+ public BellStyle BellStyle { get; set; }
+
+ public bool HistorySearchCaseSensitive { get; set; }
+ internal StringComparison HistoryStringComparison
+ {
+ get { return HistorySearchCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase; }
+ }
+
+ #region vi
+ public ViModeStyle ViModeIndicator { get; set; }
+ #endregion vi
+
+ ///
+ /// The path to the saved history.
+ ///
+ public string HistorySavePath { get; set; }
+ public HistorySaveStyle HistorySaveStyle { get; set; }
+
+ public ConsoleColor DefaultTokenForegroundColor { get; set; }
+ public ConsoleColor CommentForegroundColor { get; set; }
+ public ConsoleColor KeywordForegroundColor { get; set; }
+ public ConsoleColor StringForegroundColor { get; set; }
+ public ConsoleColor OperatorForegroundColor { get; set; }
+ public ConsoleColor VariableForegroundColor { get; set; }
+ public ConsoleColor CommandForegroundColor { get; set; }
+ public ConsoleColor ParameterForegroundColor { get; set; }
+ public ConsoleColor TypeForegroundColor { get; set; }
+ public ConsoleColor NumberForegroundColor { get; set; }
+ public ConsoleColor MemberForegroundColor { get; set; }
+ public ConsoleColor DefaultTokenBackgroundColor { get; set; }
+ public ConsoleColor CommentBackgroundColor { get; set; }
+ public ConsoleColor KeywordBackgroundColor { get; set; }
+ public ConsoleColor StringBackgroundColor { get; set; }
+ public ConsoleColor OperatorBackgroundColor { get; set; }
+ public ConsoleColor VariableBackgroundColor { get; set; }
+ public ConsoleColor CommandBackgroundColor { get; set; }
+ public ConsoleColor ParameterBackgroundColor { get; set; }
+ public ConsoleColor TypeBackgroundColor { get; set; }
+ public ConsoleColor NumberBackgroundColor { get; set; }
+ public ConsoleColor MemberBackgroundColor { get; set; }
+ public ConsoleColor EmphasisForegroundColor { get; set; }
+ public ConsoleColor EmphasisBackgroundColor { get; set; }
+ public ConsoleColor ErrorForegroundColor { get; set; }
+ public ConsoleColor ErrorBackgroundColor { get; set; }
+
+ internal void ResetColors()
+ {
+ DefaultTokenForegroundColor = Console.ForegroundColor;
+ CommentForegroundColor = DefaultCommentForegroundColor;
+ KeywordForegroundColor = DefaultKeywordForegroundColor;
+ StringForegroundColor = DefaultStringForegroundColor;
+ OperatorForegroundColor = DefaultOperatorForegroundColor;
+ VariableForegroundColor = DefaultVariableForegroundColor;
+ CommandForegroundColor = DefaultCommandForegroundColor;
+ ParameterForegroundColor = DefaultParameterForegroundColor;
+ TypeForegroundColor = DefaultTypeForegroundColor;
+ NumberForegroundColor = DefaultNumberForegroundColor;
+ MemberForegroundColor = DefaultNumberForegroundColor;
+ EmphasisForegroundColor = DefaultEmphasisForegroundColor;
+ ErrorForegroundColor = DefaultErrorForegroundColor;
+ DefaultTokenBackgroundColor = Console.BackgroundColor;
+ CommentBackgroundColor = Console.BackgroundColor;
+ KeywordBackgroundColor = Console.BackgroundColor;
+ StringBackgroundColor = Console.BackgroundColor;
+ OperatorBackgroundColor = Console.BackgroundColor;
+ VariableBackgroundColor = Console.BackgroundColor;
+ CommandBackgroundColor = Console.BackgroundColor;
+ ParameterBackgroundColor = Console.BackgroundColor;
+ TypeBackgroundColor = Console.BackgroundColor;
+ NumberBackgroundColor = Console.BackgroundColor;
+ MemberBackgroundColor = Console.BackgroundColor;
+ EmphasisBackgroundColor = Console.BackgroundColor;
+ ErrorBackgroundColor = Console.BackgroundColor;
+ }
+
+ internal void SetForegroundColor(TokenClassification tokenKind, ConsoleColor color)
+ {
+ switch (tokenKind)
+ {
+ case TokenClassification.None: DefaultTokenForegroundColor = color; break;
+ case TokenClassification.Comment: CommentForegroundColor = color; break;
+ case TokenClassification.Keyword: KeywordForegroundColor = color; break;
+ case TokenClassification.String: StringForegroundColor = color; break;
+ case TokenClassification.Operator: OperatorForegroundColor = color; break;
+ case TokenClassification.Variable: VariableForegroundColor = color; break;
+ case TokenClassification.Command: CommandForegroundColor = color; break;
+ case TokenClassification.Parameter: ParameterForegroundColor = color; break;
+ case TokenClassification.Type: TypeForegroundColor = color; break;
+ case TokenClassification.Number: NumberForegroundColor = color; break;
+ case TokenClassification.Member: MemberForegroundColor = color; break;
+ }
+ }
+
+ internal void SetBackgroundColor(TokenClassification tokenKind, ConsoleColor color)
+ {
+ switch (tokenKind)
+ {
+ case TokenClassification.None: DefaultTokenBackgroundColor = color; break;
+ case TokenClassification.Comment: CommentBackgroundColor = color; break;
+ case TokenClassification.Keyword: KeywordBackgroundColor = color; break;
+ case TokenClassification.String: StringBackgroundColor = color; break;
+ case TokenClassification.Operator: OperatorBackgroundColor = color; break;
+ case TokenClassification.Variable: VariableBackgroundColor = color; break;
+ case TokenClassification.Command: CommandBackgroundColor = color; break;
+ case TokenClassification.Parameter: ParameterBackgroundColor = color; break;
+ case TokenClassification.Type: TypeBackgroundColor = color; break;
+ case TokenClassification.Number: NumberBackgroundColor = color; break;
+ case TokenClassification.Member: MemberBackgroundColor = color; break;
+ }
+ }
+ }
+
+ [Cmdlet(VerbsCommon.Get, "PSReadlineOption", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=528808")]
+ [OutputType(typeof(PSConsoleReadlineOptions))]
+ public class GetPSReadlineOption : PSCmdlet
+ {
+ [ExcludeFromCodeCoverage]
+ protected override void EndProcessing()
+ {
+ WriteObject(PSConsoleReadLine.GetOptions());
+ }
+ }
+
+ [Cmdlet(VerbsCommon.Set, "PSReadlineOption", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=528811")]
+ public class SetPSReadlineOption : PSCmdlet
+ {
+ [Parameter(ParameterSetName = "OptionsSet")]
+ public EditMode EditMode
+ {
+ get { return _editMode.GetValueOrDefault(); }
+ set { _editMode = value; }
+ }
+ internal EditMode? _editMode;
+
+ [Parameter(ParameterSetName = "OptionsSet")]
+ [AllowEmptyString]
+ public string ContinuationPrompt { get; set; }
+
+ [Parameter(ParameterSetName = "OptionsSet")]
+ public ConsoleColor ContinuationPromptForegroundColor
+ {
+ get { return _continuationPromptForegroundColor.GetValueOrDefault(); }
+ set { _continuationPromptForegroundColor = value; }
+ }
+ internal ConsoleColor? _continuationPromptForegroundColor;
+
+ [Parameter(ParameterSetName = "OptionsSet")]
+ public ConsoleColor ContinuationPromptBackgroundColor
+ {
+ get { return _continuationPromptBackgroundColor.GetValueOrDefault(); }
+ set { _continuationPromptBackgroundColor = value; }
+ }
+ internal ConsoleColor? _continuationPromptBackgroundColor;
+
+ [Parameter(ParameterSetName = "OptionsSet")]
+ public ConsoleColor EmphasisForegroundColor
+ {
+ get { return _emphasisForegroundColor.GetValueOrDefault(); }
+ set { _emphasisForegroundColor = value; }
+ }
+ internal ConsoleColor? _emphasisForegroundColor;
+
+ [Parameter(ParameterSetName = "OptionsSet")]
+ public ConsoleColor EmphasisBackgroundColor
+ {
+ get { return _emphasisBackgroundColor.GetValueOrDefault(); }
+ set { _emphasisBackgroundColor = value; }
+ }
+ internal ConsoleColor? _emphasisBackgroundColor;
+
+ [Parameter(ParameterSetName = "OptionsSet")]
+ public ConsoleColor ErrorForegroundColor
+ {
+ get { return _errorForegroundColor.GetValueOrDefault(); }
+ set { _errorForegroundColor = value; }
+ }
+ internal ConsoleColor? _errorForegroundColor;
+
+ [Parameter(ParameterSetName = "OptionsSet")]
+ public ConsoleColor ErrorBackgroundColor
+ {
+ get { return _errorBackgroundColor.GetValueOrDefault(); }
+ set { _errorBackgroundColor = value; }
+ }
+ internal ConsoleColor? _errorBackgroundColor;
+
+ [Parameter(ParameterSetName = "OptionsSet")]
+ public SwitchParameter HistoryNoDuplicates
+ {
+ get { return _historyNoDuplicates.GetValueOrDefault(); }
+ set { _historyNoDuplicates = value; }
+ }
+ internal SwitchParameter? _historyNoDuplicates;
+
+ [Parameter(ParameterSetName = "OptionsSet")]
+ [AllowNull]
+ public Func AddToHistoryHandler
+ {
+ get { return _addToHistoryHandler; }
+ set
+ {
+ _addToHistoryHandler = value;
+ _addToHistoryHandlerSpecified = true;
+ }
+ }
+ private Func _addToHistoryHandler;
+ internal bool _addToHistoryHandlerSpecified;
+
+ [Parameter(ParameterSetName = "OptionsSet")]
+ [AllowNull]
+ public Action CommandValidationHandler
+ {
+ get { return _commandValidationHandler; }
+ set
+ {
+ _commandValidationHandler = value;
+ _commandValidationHandlerSpecified = true;
+ }
+ }
+ private Action _commandValidationHandler;
+ internal bool _commandValidationHandlerSpecified;
+
+ [Parameter(ParameterSetName = "OptionsSet")]
+ public SwitchParameter HistorySearchCursorMovesToEnd
+ {
+ get { return _historySearchCursorMovesToEnd.GetValueOrDefault(); }
+ set { _historySearchCursorMovesToEnd = value; }
+ }
+ internal SwitchParameter? _historySearchCursorMovesToEnd;
+
+ [Parameter(ParameterSetName = "OptionsSet")]
+ public int MaximumHistoryCount
+ {
+ get { return _maximumHistoryCount.GetValueOrDefault(); }
+ set { _maximumHistoryCount = value; }
+ }
+ internal int? _maximumHistoryCount;
+
+ [Parameter(ParameterSetName = "OptionsSet")]
+ public int MaximumKillRingCount
+ {
+ get { return _maximumKillRingCount.GetValueOrDefault(); }
+ set { _maximumKillRingCount = value; }
+ }
+ internal int? _maximumKillRingCount;
+
+ [Parameter(ParameterSetName = "OptionsSet")]
+ public SwitchParameter ResetTokenColors
+ {
+ get { return _resetTokenColors.GetValueOrDefault(); }
+ set { _resetTokenColors = value; }
+ }
+ internal SwitchParameter? _resetTokenColors;
+
+ [Parameter(ParameterSetName = "OptionsSet")]
+ public SwitchParameter ShowToolTips
+ {
+ get { return _showToolTips.GetValueOrDefault(); }
+ set { _showToolTips = value; }
+ }
+ internal SwitchParameter? _showToolTips;
+
+ [Parameter(ParameterSetName = "OptionsSet")]
+ public int ExtraPromptLineCount
+ {
+ get { return _extraPromptLineCount.GetValueOrDefault(); }
+ set { _extraPromptLineCount = value; }
+ }
+ internal int? _extraPromptLineCount;
+
+ [Parameter(ParameterSetName = "OptionsSet")]
+ public int DingTone
+ {
+ get { return _dingTone.GetValueOrDefault(); }
+ set { _dingTone = value; }
+ }
+ internal int? _dingTone;
+
+ [Parameter(ParameterSetName = "OptionsSet")]
+ public int DingDuration
+ {
+ get { return _dingDuration.GetValueOrDefault(); }
+ set { _dingDuration = value; }
+ }
+ internal int? _dingDuration;
+
+ [Parameter(ParameterSetName = "OptionsSet")]
+ public BellStyle BellStyle
+ {
+ get { return _bellStyle.GetValueOrDefault(); }
+ set { _bellStyle = value; }
+ }
+ internal BellStyle? _bellStyle;
+
+ [Parameter(ParameterSetName = "OptionsSet")]
+ public int CompletionQueryItems
+ {
+ get { return _completionQueryItems.GetValueOrDefault(); }
+ set { _completionQueryItems = value; }
+ }
+ internal int? _completionQueryItems;
+
+ [Parameter(ParameterSetName = "OptionsSet")]
+ public string WordDelimiters { get; set; }
+
+ [Parameter(ParameterSetName = "OptionsSet")]
+ public SwitchParameter HistorySearchCaseSensitive
+ {
+ get { return _historySearchCaseSensitive.GetValueOrDefault(); }
+ set { _historySearchCaseSensitive = value; }
+ }
+ internal SwitchParameter? _historySearchCaseSensitive;
+
+ [Parameter(ParameterSetName = "OptionsSet")]
+ public HistorySaveStyle HistorySaveStyle
+ {
+ get { return _historySaveStyle.GetValueOrDefault(); }
+ set { _historySaveStyle = value; }
+ }
+ internal HistorySaveStyle? _historySaveStyle;
+
+ [Parameter(ParameterSetName = "OptionsSet")]
+ [ValidateNotNullOrEmpty]
+ public string HistorySavePath { get; set; }
+
+ #region vi
+ [Parameter(ParameterSetName = "OptionsSet")]
+ public ViModeStyle ViModeIndicator
+ {
+ get { return _viModeIndicator.GetValueOrDefault(); }
+ set { _viModeIndicator = value; }
+ }
+ internal ViModeStyle? _viModeIndicator;
+ #endregion vi
+
+ [Parameter(ParameterSetName = "ColorSet", Position = 0, Mandatory = true)]
+ public TokenClassification TokenKind
+ {
+ get { return _tokenKind.GetValueOrDefault(); }
+ set { _tokenKind = value; }
+ }
+ internal TokenClassification? _tokenKind;
+
+ [Parameter(ParameterSetName = "ColorSet", Position = 1)]
+ public ConsoleColor ForegroundColor
+ {
+ get { return _foregroundColor.GetValueOrDefault(); }
+ set { _foregroundColor = value; }
+ }
+ internal ConsoleColor? _foregroundColor;
+
+ [Parameter(ParameterSetName = "ColorSet", Position = 2)]
+ public ConsoleColor BackgroundColor
+ {
+ get { return _backgroundColor.GetValueOrDefault(); }
+ set { _backgroundColor = value; }
+ }
+ internal ConsoleColor? _backgroundColor;
+
+ [ExcludeFromCodeCoverage]
+ protected override void EndProcessing()
+ {
+ PSConsoleReadLine.SetOptions(this);
+ }
+ }
+
+ public class ChangePSReadlineKeyHandlerCommandBase : PSCmdlet
+ {
+ [Parameter(Position = 0, Mandatory = true)]
+ [Alias("Key")]
+ [ValidateNotNullOrEmpty]
+ [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
+ public string[] Chord { get; set; }
+
+ [Parameter]
+ public ViMode ViMode { get; set; }
+
+ [ExcludeFromCodeCoverage]
+ protected IDisposable UseRequestedDispatchTables()
+ {
+ bool inViMode = PSConsoleReadLine.GetOptions().EditMode == EditMode.Vi;
+ bool viModeParamPresent = MyInvocation.BoundParameters.ContainsKey("ViMode");
+
+ if (inViMode || viModeParamPresent)
+ {
+ if (!inViMode)
+ {
+ // "-ViMode" must have been specified explicitly. Well, okay... we can
+ // modify the Vi tables... but isn't that an odd thing to do from
+ // not-vi mode?
+ WriteWarning(PSReadLineResources.NotInViMode);
+ }
+
+ if (ViMode == ViMode.Command)
+ return PSConsoleReadLine.UseViCommandModeTables();
+ else // default if -ViMode not specified, invalid, or "Insert"
+ return PSConsoleReadLine.UseViInsertModeTables();
+ }
+
+ return null;
+ }
+ }
+
+ [Cmdlet(VerbsCommon.Set, "PSReadlineKeyHandler", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=528810")]
+ public class SetPSReadlineKeyHandlerCommand : ChangePSReadlineKeyHandlerCommandBase, IDynamicParameters
+ {
+ [Parameter(Position = 1, Mandatory = true, ParameterSetName = "ScriptBlock")]
+ [ValidateNotNull]
+ public ScriptBlock ScriptBlock { get; set; }
+
+ [Parameter(ParameterSetName = "ScriptBlock")]
+ public string BriefDescription { get; set; }
+
+ [Parameter(ParameterSetName = "ScriptBlock")]
+ [Alias("LongDescription")] // Alias to stay comptible with previous releases
+ public string Description { get; set; }
+
+ private const string FunctionParameter = "Function";
+ private const string FunctionParameterSet = "Function";
+
+ [ExcludeFromCodeCoverage]
+ protected override void EndProcessing()
+ {
+ using (UseRequestedDispatchTables())
+ {
+ if (ParameterSetName.Equals(FunctionParameterSet))
+ {
+ var function = (string)_dynamicParameters.Value[FunctionParameter].Value;
+ MethodInfo mi = typeof (PSConsoleReadLine).GetMethod(function,
+ BindingFlags.Public | BindingFlags.Static | BindingFlags.IgnoreCase);
+
+ string functionName = mi.Name;
+
+ var keyHandler = (Action)
+ mi.CreateDelegate(typeof (Action));
+
+ string longDescription = PSReadLineResources.ResourceManager.GetString(
+ functionName + "Description");
+
+ PSConsoleReadLine.SetKeyHandler(Chord, keyHandler, functionName, longDescription);
+ }
+ else
+ {
+ PSConsoleReadLine.SetKeyHandler(Chord, ScriptBlock, BriefDescription, Description);
+ }
+ }
+ }
+
+ private readonly Lazy _dynamicParameters =
+ new Lazy(CreateDynamicParametersResult);
+
+ private static RuntimeDefinedParameterDictionary CreateDynamicParametersResult()
+ {
+ var bindableFunctions = (typeof(PSConsoleReadLine).GetMethods(BindingFlags.Public | BindingFlags.Static))
+ .Where(method =>
+ {
+ var parameters = method.GetParameters();
+ return parameters.Length == 2
+ && parameters[0].ParameterType == typeof(ConsoleKeyInfo?)
+ && parameters[1].ParameterType == typeof(object);
+ })
+ .Select(method => method.Name)
+ .OrderBy(name => name);
+
+ var attributes = new Collection
+ {
+ new ParameterAttribute
+ {
+ Position = 1,
+ Mandatory = true,
+ ParameterSetName = FunctionParameterSet
+ },
+ new ValidateSetAttribute(bindableFunctions.ToArray())
+ };
+ var parameter = new RuntimeDefinedParameter(FunctionParameter, typeof(string), attributes);
+ var result = new RuntimeDefinedParameterDictionary {{FunctionParameter, parameter}};
+ return result;
+ }
+
+ public object GetDynamicParameters()
+ {
+ return _dynamicParameters.Value;
+ }
+ }
+
+ [Cmdlet(VerbsCommon.Get, "PSReadlineKeyHandler", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=528807")]
+ [OutputType(typeof(KeyHandler))]
+ public class GetKeyHandlerCommand : PSCmdlet
+ {
+ [Parameter]
+ public SwitchParameter Bound
+ {
+ get { return _bound.GetValueOrDefault(); }
+ set { _bound = value; }
+ }
+ private SwitchParameter? _bound;
+
+ [Parameter]
+ public SwitchParameter Unbound
+ {
+ get { return _unbound.GetValueOrDefault(); }
+ set { _unbound = value; }
+ }
+ private SwitchParameter? _unbound;
+
+ [ExcludeFromCodeCoverage]
+ protected override void EndProcessing()
+ {
+ bool bound = true;
+ bool unbound = true;
+ if (_bound.HasValue && _unbound.HasValue)
+ {
+ bound = _bound.Value.IsPresent;
+ unbound = _unbound.Value.IsPresent;
+ }
+ else if (_bound.HasValue)
+ {
+ bound = _bound.Value.IsPresent;
+ unbound = false;
+ }
+ else if (_unbound.HasValue)
+ {
+ bound = false;
+ unbound = _unbound.Value.IsPresent;
+ }
+ WriteObject(PSConsoleReadLine.GetKeyHandlers(bound, unbound), true);
+ }
+ }
+
+ [Cmdlet(VerbsCommon.Remove, "PSReadlineKeyHandler", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=528809")]
+ public class RemoveKeyHandlerCommand : ChangePSReadlineKeyHandlerCommandBase
+ {
+ [ExcludeFromCodeCoverage]
+ protected override void EndProcessing()
+ {
+ using (UseRequestedDispatchTables())
+ {
+ PSConsoleReadLine.RemoveKeyHandler(Chord);
+ }
+ }
+ }
+
+#pragma warning restore 1591
+
+}
diff --git a/src/Microsoft.PowerShell.PSReadLine/Completion.cs b/src/Microsoft.PowerShell.PSReadLine/Completion.cs
new file mode 100644
index 00000000000..aadee01d815
--- /dev/null
+++ b/src/Microsoft.PowerShell.PSReadLine/Completion.cs
@@ -0,0 +1,556 @@
+/********************************************************************++
+Copyright (c) Microsoft Corporation. All rights reserved.
+--********************************************************************/
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+using System.Linq;
+using System.Management.Automation;
+using System.Management.Automation.Runspaces;
+using System.Text;
+using Microsoft.PowerShell.Internal;
+
+namespace Microsoft.PowerShell
+{
+ public partial class PSConsoleReadLine
+ {
+ // Tab completion state
+ private int _tabCommandCount;
+ private CommandCompletion _tabCompletions;
+ private Runspace _runspace;
+
+ // String helper for directory paths
+ private static string DirectorySeparatorString = System.IO.Path.DirectorySeparatorChar.ToString();
+
+ // Stub helper method so completion can be mocked
+ [ExcludeFromCodeCoverage]
+ CommandCompletion IPSConsoleReadLineMockableMethods.CompleteInput(string input, int cursorIndex, Hashtable options, System.Management.Automation.PowerShell powershell)
+ {
+ return CalloutUsingDefaultConsoleMode(
+ () => CommandCompletion.CompleteInput(input, cursorIndex, options, powershell));
+ }
+
+ ///
+ /// Attempt to complete the text surrounding the cursor with the next
+ /// available completion.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void TabCompleteNext(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ _singleton.Complete(forward: true);
+ }
+
+ ///
+ /// Attempt to complete the text surrounding the cursor with the previous
+ /// available completion.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void TabCompletePrevious(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ _singleton.Complete(forward: false);
+ }
+
+ private static bool IsSingleQuote(char c)
+ {
+ return c == '\'' || c == (char)8216 || c == (char)8217 || c == (char)8218;
+ }
+
+ private static bool IsDoubleQuote(char c)
+ {
+ return c == '"' || c == (char)8220 || c == (char)8221;
+ }
+
+ private static bool IsQuoted(string s)
+ {
+ if (s.Length >= 2)
+ {
+ var first = s[0];
+ var last = s[s.Length - 1];
+
+ return ((IsSingleQuote(first) && IsSingleQuote(last))
+ ||
+ (IsDoubleQuote(first) && IsDoubleQuote(last)));
+ }
+ return false;
+ }
+
+ private static string GetUnquotedText(string s, bool consistentQuoting)
+ {
+ if (!consistentQuoting && IsQuoted(s))
+ {
+ s = s.Substring(1, s.Length - 2);
+ }
+ return s;
+ }
+
+ ///
+ /// Attempt to perform completion on the text surrounding the cursor.
+ /// If there are multiple possible completions, the longest unambiguous
+ /// prefix is used for completion. If trying to complete the longest
+ /// unambiguous completion, a list of possible completions is displayed.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void Complete(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ _singleton.CompleteImpl(key, arg, false);
+ }
+
+ ///
+ /// Attempt to perform completion on the text surrounding the cursor.
+ /// If there are multiple possible completions, the longest unambiguous
+ /// prefix is used for completion. If trying to complete the longest
+ /// unambiguous completion, a list of possible completions is displayed.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void MenuComplete(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ _singleton.CompleteImpl(key, arg, true);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ private void CompleteImpl(ConsoleKeyInfo? key, object arg, bool menuSelect)
+ {
+ var completions = GetCompletions();
+ if (completions == null || completions.CompletionMatches.Count == 0)
+ return;
+
+ if (_tabCommandCount > 0)
+ {
+ if (completions.CompletionMatches.Count == 1)
+ {
+ Ding();
+ }
+ else
+ {
+ PossibleCompletionsImpl(completions, menuSelect);
+ }
+ return;
+ }
+
+ if (completions.CompletionMatches.Count == 1)
+ {
+ // We want to add a backslash for directory completion if possible. This
+ // is mostly only needed if we have a single completion - if there are multiple
+ // completions, then we'll be showing the possible completions where it's very
+ // unlikely that we would add a trailing backslash.
+
+ DoReplacementForCompletion(completions.CompletionMatches[0], completions);
+ return;
+ }
+
+ if (menuSelect)
+ {
+ PossibleCompletionsImpl(completions, true);
+ return;
+ }
+
+ // Find the longest unambiguous prefix. This might be the empty
+ // string, in which case we don't want to remove any of the users input,
+ // instead we'll immediately show possible completions.
+ // For the purposes of unambiguous prefix, we'll ignore quotes if
+ // some completions aren't quoted.
+ var firstResult = completions.CompletionMatches[0];
+ int quotedCompletions = completions.CompletionMatches.Count(match => IsQuoted(match.CompletionText));
+ bool consistentQuoting =
+ quotedCompletions == 0 ||
+ (quotedCompletions == completions.CompletionMatches.Count &&
+ quotedCompletions == completions.CompletionMatches.Count(
+ m => m.CompletionText[0] == firstResult.CompletionText[0]));
+
+ bool ambiguous = false;
+ var replacementText = GetUnquotedText(firstResult.CompletionText, consistentQuoting);
+ foreach (var match in completions.CompletionMatches.Skip(1))
+ {
+ var matchText = GetUnquotedText(match.CompletionText, consistentQuoting);
+ for (int i = 0; i < replacementText.Length; i++)
+ {
+ if (i == matchText.Length
+ || char.ToLowerInvariant(replacementText[i]) != char.ToLowerInvariant(matchText[i]))
+ {
+ ambiguous = true;
+ replacementText = replacementText.Substring(0, i);
+ break;
+ }
+ }
+ if (replacementText.Length == 0)
+ {
+ break;
+ }
+ }
+
+ if (replacementText.Length > 0)
+ {
+ Replace(completions.ReplacementIndex, completions.ReplacementLength, replacementText);
+ completions.ReplacementLength = replacementText.Length;
+
+ if (ambiguous)
+ {
+ Ding();
+ }
+ }
+ else
+ {
+ // No common prefix, don't wait for a second tab, just show the possible completions
+ // right away.
+ PossibleCompletionsImpl(completions, false);
+ }
+
+ _tabCommandCount += 1;
+ }
+
+ private CommandCompletion GetCompletions()
+ {
+ if (_tabCommandCount == 0)
+ {
+ try
+ {
+ _tabCompletions = null;
+
+ // Could use the overload that takes an AST as it's faster (we've already parsed the
+ // input for coloring) but that overload is a little more complicated in passing in the
+ // cursor position.
+ System.Management.Automation.PowerShell ps;
+ if (!_mockableMethods.RunspaceIsRemote(_runspace))
+ {
+ ps = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace);
+ }
+ else
+ {
+ ps = System.Management.Automation.PowerShell.Create();
+ ps.Runspace = _runspace;
+ }
+ _tabCompletions = _mockableMethods.CompleteInput(_buffer.ToString(), _current, null, ps);
+
+ if (_tabCompletions.CompletionMatches.Count == 0)
+ return null;
+ }
+ catch (Exception)
+ {
+ }
+ }
+
+ return _tabCompletions;
+ }
+
+ private void Complete(bool forward)
+ {
+ var completions = GetCompletions();
+ if (completions == null)
+ return;
+
+ completions.CurrentMatchIndex += forward ? 1 : -1;
+ if (completions.CurrentMatchIndex < 0)
+ {
+ completions.CurrentMatchIndex = completions.CompletionMatches.Count - 1;
+ }
+ else if (completions.CurrentMatchIndex == completions.CompletionMatches.Count)
+ {
+ completions.CurrentMatchIndex = 0;
+ }
+
+ var completionResult = completions.CompletionMatches[completions.CurrentMatchIndex];
+ DoReplacementForCompletion(completionResult, completions);
+ _tabCommandCount += 1;
+ }
+
+ private void DoReplacementForCompletion(CompletionResult completionResult, CommandCompletion completions)
+ {
+ var replacementText = completionResult.CompletionText;
+ int cursorAdjustment = 0;
+ if (completionResult.ResultType == CompletionResultType.ProviderContainer)
+ {
+ replacementText = GetReplacementTextForDirectory(replacementText, ref cursorAdjustment);
+ }
+ Replace(completions.ReplacementIndex, completions.ReplacementLength, replacementText);
+ if (cursorAdjustment != 0)
+ {
+ _current += cursorAdjustment;
+ PlaceCursor();
+ }
+ completions.ReplacementLength = replacementText.Length;
+ }
+
+ private static string GetReplacementTextForDirectory(string replacementText, ref int cursorAdjustment)
+ {
+ if (!replacementText.EndsWith(DirectorySeparatorString , StringComparison.Ordinal))
+ {
+ if (replacementText.EndsWith(String.Format("{0}\'", DirectorySeparatorString), StringComparison.Ordinal) ||
+ replacementText.EndsWith(String.Format("{0}\"", DirectorySeparatorString), StringComparison.Ordinal))
+ {
+ cursorAdjustment = -1;
+ }
+ else if (replacementText.EndsWith("'", StringComparison.Ordinal) ||
+ replacementText.EndsWith("\"", StringComparison.Ordinal))
+ {
+ var len = replacementText.Length;
+ replacementText = replacementText.Substring(0, len - 1) + System.IO.Path.DirectorySeparatorChar + replacementText[len - 1];
+ cursorAdjustment = -1;
+ }
+ else
+ {
+ replacementText = replacementText + System.IO.Path.DirectorySeparatorChar;
+ }
+ }
+ return replacementText;
+ }
+
+ private static void InvertSelectedCompletion(BufferChar[] buffer, int selectedItem, int menuColumnWidth, int menuRows)
+ {
+ var selectedX = selectedItem / menuRows;
+ var selectedY = selectedItem - (selectedX * menuRows);
+ var start = selectedY * _singleton._console.BufferWidth + selectedX * menuColumnWidth;
+ for (int i = 0; i < menuColumnWidth; i++)
+ {
+ buffer[i + start].Inverse = true;
+ }
+ }
+
+ ///
+ /// Display the list of possible completions.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void PossibleCompletions(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ var completions = _singleton.GetCompletions();
+ _singleton.PossibleCompletionsImpl(completions, menuSelect: false);
+ }
+
+ private static string HandleNewlinesForPossibleCompletions(string s)
+ {
+ s = s.Trim();
+ var newlineIndex = s.IndexOfAny(new []{'\r', '\n'});
+ if (newlineIndex >= 0)
+ {
+ s = s.Substring(0, newlineIndex) + "...";
+ }
+ return s;
+ }
+
+ private void PossibleCompletionsImpl(CommandCompletion completions, bool menuSelect)
+ {
+ if (completions == null || completions.CompletionMatches.Count == 0)
+ {
+ Ding();
+ return;
+ }
+
+ if (completions.CompletionMatches.Count >= _options.CompletionQueryItems)
+ {
+ if (!PromptYesOrNo(string.Format(CultureInfo.CurrentCulture, PSReadLineResources.DisplayAllPossibilities, completions.CompletionMatches.Count)))
+ {
+ return;
+ }
+ }
+
+ var matches = completions.CompletionMatches;
+ var minColWidth = matches.Max(c => c.ListItemText.Length);
+ minColWidth += 2;
+ var menuColumnWidth = minColWidth;
+
+ int displayRows;
+ var bufferWidth = _console.BufferWidth;
+ ConsoleBufferBuilder cb;
+ if (Options.ShowToolTips)
+ {
+ const string seperator = "- ";
+ var maxTooltipWidth = bufferWidth - minColWidth - seperator.Length;
+
+ displayRows = matches.Count;
+ cb = new ConsoleBufferBuilder(displayRows * bufferWidth, _console);
+ for (int index = 0; index < matches.Count; index++)
+ {
+ var match = matches[index];
+ var listItemText = HandleNewlinesForPossibleCompletions(match.ListItemText);
+ cb.Append(listItemText);
+ var spacesNeeded = minColWidth - listItemText.Length;
+ if (spacesNeeded > 0)
+ {
+ cb.Append(' ', spacesNeeded);
+ }
+ cb.Append(seperator);
+ var toolTip = HandleNewlinesForPossibleCompletions(match.ToolTip);
+ toolTip = toolTip.Length <= maxTooltipWidth
+ ? toolTip
+ : toolTip.Substring(0, maxTooltipWidth);
+ cb.Append(toolTip);
+
+ // Make sure we always write out exactly 1 buffer width
+ spacesNeeded = (bufferWidth * (index + 1)) - cb.Length;
+ if (spacesNeeded > 0)
+ {
+ cb.Append(' ', spacesNeeded);
+ }
+ }
+ menuColumnWidth = bufferWidth;
+ }
+ else
+ {
+ var screenColumns = bufferWidth;
+ var displayColumns = Math.Max(1, screenColumns / minColWidth);
+ displayRows = (completions.CompletionMatches.Count + displayColumns - 1) / displayColumns;
+ cb = new ConsoleBufferBuilder(displayRows * bufferWidth, _console);
+ for (var row = 0; row < displayRows; row++)
+ {
+ for (var col = 0; col < displayColumns; col++)
+ {
+ var index = row + (displayRows * col);
+ if (index >= matches.Count)
+ break;
+ var match = matches[index];
+ var item = HandleNewlinesForPossibleCompletions(match.ListItemText);
+ cb.Append(item);
+ cb.Append(' ', minColWidth - item.Length);
+ }
+
+ // Make sure we always write out exactly 1 buffer width
+ var spacesNeeded = (bufferWidth * (row + 1)) - cb.Length;
+ if (spacesNeeded > 0)
+ {
+ cb.Append(' ', spacesNeeded);
+ }
+ }
+ }
+
+ var menuBuffer = cb.ToArray();
+
+ if (menuSelect)
+ {
+ // Make sure the menu and line can appear on the screen at the same time,
+ // if not, we'll skip the menu.
+
+ var endBufferCoords = ConvertOffsetToCoordinates(_buffer.Length);
+ var bufferLines = endBufferCoords.Y - _initialY + 1;
+ if ((bufferLines + displayRows) > _console.WindowHeight)
+ {
+ menuSelect = false;
+ }
+ }
+
+ if (menuSelect)
+ {
+ RemoveEditsAfterUndo();
+ var undoPoint = _edits.Count;
+
+ int selectedItem = 0;
+ bool undo = false;
+
+ DoReplacementForCompletion(matches[0], completions);
+
+ // Recompute end of buffer coordinates as the replacement could have
+ // added a line.
+ var endBufferCoords = ConvertOffsetToCoordinates(_buffer.Length);
+ var menuAreaTop = endBufferCoords.Y + 1;
+ var previousMenuTop = menuAreaTop;
+
+ InvertSelectedCompletion(menuBuffer, selectedItem, menuColumnWidth, displayRows);
+ _console.WriteBufferLines(menuBuffer, ref menuAreaTop);
+
+ // Showing the menu may have scrolled the screen or moved the cursor, update initialY to reflect that.
+ _initialY -= (previousMenuTop - menuAreaTop);
+ PlaceCursor();
+ previousMenuTop = menuAreaTop;
+
+ int previousItem = selectedItem;
+
+ bool processingKeys = true;
+ while (processingKeys)
+ {
+ var nextKey = ReadKey();
+ if (nextKey == Keys.RightArrow)
+ {
+ selectedItem = Math.Min(selectedItem + displayRows, matches.Count - 1);
+ }
+ else if (nextKey == Keys.LeftArrow)
+ {
+ selectedItem = Math.Max(selectedItem - displayRows, 0);
+ }
+ else if (nextKey == Keys.DownArrow)
+ {
+ selectedItem = Math.Min(selectedItem + 1, matches.Count - 1);
+ }
+ else if (nextKey == Keys.UpArrow)
+ {
+ selectedItem = Math.Max(selectedItem - 1, 0);
+ }
+ else if (nextKey == Keys.Tab)
+ {
+ selectedItem = (selectedItem + 1) % matches.Count;
+ }
+ else if (nextKey == Keys.ShiftTab)
+ {
+ selectedItem = (selectedItem - 1) % matches.Count;
+ if (selectedItem < 0)
+ {
+ selectedItem += matches.Count;
+ }
+ }
+ else if (nextKey == Keys.CtrlG || nextKey == Keys.Escape)
+ {
+ undo = true;
+ processingKeys = false;
+ }
+ else
+ {
+ PrependQueuedKeys(nextKey);
+ processingKeys = false;
+ }
+
+ if (selectedItem != previousItem)
+ {
+ DoReplacementForCompletion(matches[selectedItem], completions);
+
+ endBufferCoords = ConvertOffsetToCoordinates(_buffer.Length);
+ menuAreaTop = endBufferCoords.Y + 1;
+
+ InvertSelectedCompletion(menuBuffer, previousItem, menuColumnWidth, displayRows);
+ InvertSelectedCompletion(menuBuffer, selectedItem, menuColumnWidth, displayRows);
+ _console.WriteBufferLines(menuBuffer, ref menuAreaTop);
+ previousItem = selectedItem;
+
+ if (previousMenuTop > menuAreaTop)
+ {
+ WriteBlankLines(previousMenuTop - menuAreaTop, menuAreaTop + displayRows);
+ }
+ }
+ }
+
+ WriteBlankLines(displayRows, menuAreaTop);
+
+ var lastInsert = ((GroupedEdit)_edits[_edits.Count - 1])._groupedEditItems[1];
+ Debug.Assert(lastInsert is EditItemInsertString, "The only edits possible here are pairs of Delete/Insert");
+ var firstDelete = ((GroupedEdit)_edits[undoPoint])._groupedEditItems[0];
+ Debug.Assert(firstDelete is EditItemDelete, "The only edits possible here are pairs of Delete/Insert");
+
+ var groupEditCount = _edits.Count - undoPoint;
+ _edits.RemoveRange(undoPoint, groupEditCount);
+ _undoEditIndex = undoPoint;
+
+ if (undo)
+ {
+ // Pretend it never happened.
+ lastInsert.Undo();
+ firstDelete.Undo();
+ Render();
+ }
+ else
+ {
+ // Leave one edit instead of possibly many to undo
+ SaveEditItem(GroupedEdit.Create(new List { firstDelete, lastInsert }));
+ }
+ }
+ else
+ {
+ var endBufferCoords = ConvertOffsetToCoordinates(_buffer.Length);
+ var menuAreaTop = endBufferCoords.Y + 1;
+
+ _console.WriteBufferLines(menuBuffer, ref menuAreaTop);
+ _initialY = menuAreaTop + displayRows;
+ Render();
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.PowerShell.PSReadLine/Completion.vi.cs b/src/Microsoft.PowerShell.PSReadLine/Completion.vi.cs
new file mode 100644
index 00000000000..2e5eb7458de
--- /dev/null
+++ b/src/Microsoft.PowerShell.PSReadLine/Completion.vi.cs
@@ -0,0 +1,43 @@
+/********************************************************************++
+Copyright (c) Microsoft Corporation. All rights reserved.
+--********************************************************************/
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Management.Automation;
+using System.Management.Automation.Runspaces;
+using System.Text;
+
+namespace Microsoft.PowerShell
+{
+ public partial class PSConsoleReadLine
+ {
+ ///
+ /// Ends the current edit group, if needed, and invokes TabCompleteNext.
+ ///
+ public static void ViTabCompleteNext(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ if (_singleton._editGroupStart >= 0)
+ {
+ _singleton._groupUndoHelper.EndGroup();
+ }
+ TabCompleteNext(key, arg);
+ }
+
+ ///
+ /// Ends the current edit group, if needed, and invokes TabCompletePrevious.
+ ///
+ public static void ViTabCompletePrevious(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ if (_singleton._editGroupStart >= 0)
+ {
+ _singleton._groupUndoHelper.EndGroup();
+ }
+ TabCompletePrevious(key, arg);
+ }
+ }
+}
diff --git a/src/Microsoft.PowerShell.PSReadLine/ConsoleBufferBuilder.cs b/src/Microsoft.PowerShell.PSReadLine/ConsoleBufferBuilder.cs
new file mode 100644
index 00000000000..df5244c4f81
--- /dev/null
+++ b/src/Microsoft.PowerShell.PSReadLine/ConsoleBufferBuilder.cs
@@ -0,0 +1,59 @@
+/********************************************************************++
+Copyright (c) Microsoft Corporation. All rights reserved.
+--********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using Microsoft.PowerShell.Internal;
+
+namespace Microsoft.PowerShell
+{
+ internal class ConsoleBufferBuilder
+ {
+ private List buffer;
+ private IConsole _console;
+
+ public ConsoleBufferBuilder(int capacity, IConsole console)
+ {
+ buffer = new List(capacity);
+ _console = console;
+ }
+
+ BufferChar NewCharInfo(char c)
+ {
+ return new BufferChar
+ {
+ UnicodeChar = c,
+ BackgroundColor = _console.BackgroundColor,
+ ForegroundColor = _console.ForegroundColor
+ };
+ }
+
+ public void Append(string s)
+ {
+ foreach (char c in s)
+ {
+ buffer.Add(NewCharInfo(c));
+ }
+ }
+
+ public void Append(char c, int count)
+ {
+ while (count-- > 0)
+ {
+ buffer.Add(NewCharInfo(c));
+ }
+ }
+
+ public BufferChar[] ToArray()
+ {
+ return buffer.ToArray();
+ }
+
+ public int Length
+ {
+ get { return buffer.Count; }
+ }
+ }
+}
+
diff --git a/src/Microsoft.PowerShell.PSReadLine/ConsoleKeyChordConverter.cs b/src/Microsoft.PowerShell.PSReadLine/ConsoleKeyChordConverter.cs
new file mode 100644
index 00000000000..1af6c7aecf4
--- /dev/null
+++ b/src/Microsoft.PowerShell.PSReadLine/ConsoleKeyChordConverter.cs
@@ -0,0 +1,341 @@
+/********************************************************************++
+Copyright (c) Microsoft Corporation. All rights reserved.
+--********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Runtime.InteropServices;
+using Microsoft.PowerShell.Internal;
+
+namespace Microsoft.PowerShell
+{
+ ///
+ /// A helper class for converting strings to ConsoleKey chords.
+ ///
+ public static class ConsoleKeyChordConverter
+ {
+ ///
+ /// Converts a string to a sequence of ConsoleKeyInfo.
+ /// Sequences are separated by ','. Modifiers
+ /// appear before the modified key and are separated by '+'.
+ /// Examples:
+ /// Ctrl+X
+ /// Ctrl+D,M
+ /// Escape,X
+ ///
+ public static ConsoleKeyInfo[] Convert(string chord)
+ {
+ if (string.IsNullOrEmpty(chord))
+ {
+ throw new ArgumentNullException("chord");
+ }
+
+ var tokens = chord.Split(new[] {','});
+
+ if (tokens.Length > 2)
+ {
+ throw new ArgumentException(PSReadLineResources.ChordWithTooManyKeys);
+ }
+
+ var result = new ConsoleKeyInfo[tokens.Length];
+ for (int i = 0; i < tokens.Length; i++)
+ {
+ result[i] = ConvertOneSequence(tokens[i]);
+ }
+
+ return result;
+ }
+
+ private static ConsoleKeyInfo ConvertOneSequence(string sequence)
+ {
+ Stack tokens = null;
+ ConsoleModifiers modifiers = 0;
+ ConsoleKey key = 0;
+ char keyChar = '\u0000';
+
+ bool valid = !String.IsNullOrEmpty(sequence);
+
+ if (valid)
+ {
+ tokens = new Stack(
+ (sequence.Split(new[] {'+'})
+ .Select(
+ part => part.ToLowerInvariant().Trim())));
+ }
+
+ while (valid && tokens.Count > 0)
+ {
+ string token = tokens.Pop();
+
+ // sequence was something silly like "shift++"
+ if (token == String.Empty)
+ {
+ valid = false;
+ break;
+ }
+
+ // key should be first token to be popped
+ if (key == 0)
+ {
+ // the keyChar is this token, if not a special key like "Fxx" etc.
+ if (token.Length == 1)
+ {
+ keyChar = token[0];
+ }
+
+ // Enum.TryParse accepts arbitrary integers. We shouldn't,
+ // but single digits need to map to the correct key, e.g.
+ // ConsoleKey.D1
+ long tokenAsLong;
+ if (long.TryParse(token, out tokenAsLong))
+ {
+ if (tokenAsLong >= 0 && tokenAsLong <= 9)
+ {
+ token = "D" + token;
+ }
+ else
+ {
+ throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, PSReadLineResources.UnrecognizedKey, token));
+ }
+ }
+ // try simple parse for ConsoleKey enum name
+ valid = Enum.TryParse(token, ignoreCase: true, result: out key);
+
+ // doesn't map to ConsoleKey so convert to virtual key from char
+ if (!valid && token.Length == 1)
+ {
+ string failReason;
+ valid = TryParseCharLiteral(token[0], ref modifiers, ref key, out failReason);
+
+ if (!valid)
+ {
+ throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, PSReadLineResources.CantTranslateKey, token[0], failReason));
+ }
+ }
+
+ if (!valid)
+ {
+ throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, PSReadLineResources.UnrecognizedKey, token));
+ }
+ }
+ else
+ {
+ // now, parse modifier(s)
+ ConsoleModifiers modifier;
+
+ // courtesy translation
+ if (token == "ctrl")
+ {
+ token = "control";
+ }
+
+ if (Enum.TryParse(token, ignoreCase: true, result: out modifier))
+ {
+ // modifier already set?
+ if ((modifiers & modifier) != 0)
+ {
+ // either found duplicate modifier token or shift state
+ // was already implied from char, e.g. char is "}", which is "shift+]"
+ throw new ArgumentException(
+ String.Format(CultureInfo.CurrentCulture, PSReadLineResources.InvalidModifier, modifier, key));
+ }
+ modifiers |= modifier;
+ }
+ else
+ {
+ throw new ArgumentException(
+ String.Format(CultureInfo.CurrentCulture, PSReadLineResources.InvalidModifier, token, key));
+ }
+ }
+ }
+
+ if (!valid)
+ {
+ throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, PSReadLineResources.InvalidSequence, sequence));
+ }
+
+ return new ConsoleKeyInfo(keyChar, key,
+ shift: ((modifiers & ConsoleModifiers.Shift) != 0),
+ alt: ((modifiers & ConsoleModifiers.Alt) != 0),
+ control: ((modifiers & ConsoleModifiers.Control) != 0));
+ }
+
+ private static bool TryParseCharLiteral(char literal, ref ConsoleModifiers modifiers, ref ConsoleKey key, out string failReason)
+ {
+ bool valid = false;
+
+#if UNIX
+ bool isShift;
+ bool isCtrl;
+ key = GetKeyFromCharValue(literal, out isShift, out isCtrl);
+
+ // Failure to get a key for the char just means that the key is not
+ // special (in the ConsoleKey enum), so we return a default key.
+ // Thus this never fails.
+ valid = true;
+ failReason = null;
+
+ if (isShift)
+ {
+ modifiers |= ConsoleModifiers.Shift;
+ }
+ if (isCtrl)
+ {
+ modifiers |= ConsoleModifiers.Control;
+ }
+ // alt is not possible to get
+#else
+ // shift state will be in MSB
+ short virtualKey = NativeMethods.VkKeyScan(literal);
+ int hresult = Marshal.GetLastWin32Error();
+
+ if (virtualKey != 0)
+ {
+ // e.g. "}" = 0x01dd but "]" is 0x00dd, ergo } = shift+].
+ // shift = 1, control = 2, alt = 4, hankaku = 8 (ignored)
+ int state = virtualKey >> 8;
+
+ if ((state & 1) == 1)
+ {
+ modifiers |= ConsoleModifiers.Shift;
+ }
+ if ((state & 2) == 2)
+ {
+ modifiers |= ConsoleModifiers.Control;
+ }
+ if ((state & 4) == 4)
+ {
+ modifiers |= ConsoleModifiers.Alt;
+ }
+
+ virtualKey &= 0xff;
+
+ if (Enum.IsDefined(typeof (ConsoleKey), (int) virtualKey))
+ {
+ failReason = null;
+ key = (ConsoleKey) virtualKey;
+ valid = true;
+ }
+ else
+ {
+ // haven't seen this happen yet, but possible
+ failReason = String.Format(CultureInfo.CurrentCulture, PSReadLineResources.UnrecognizedKey, virtualKey);
+ }
+ }
+ else
+ {
+ Exception e = Marshal.GetExceptionForHR(hresult);
+ failReason = e.Message;
+ }
+#endif
+
+ return valid;
+ }
+
+ // this is borrowed from the CoreFX internal System.IO.StdInReader class
+ // https://github.com/dotnet/corefx/blob/5b2ae6aa485773cd5569f56f446698633c9ad945/src/System.Console/src/System/IO/StdInReader.cs#L222
+ private static ConsoleKey GetKeyFromCharValue(char x, out bool isShift, out bool isCtrl)
+ {
+ isShift = false;
+ isCtrl = false;
+
+ switch (x)
+ {
+ case '\b':
+ return ConsoleKey.Backspace;
+
+ case '\t':
+ return ConsoleKey.Tab;
+
+ case '\n':
+ return ConsoleKey.Enter;
+
+ case (char)(0x1B):
+ return ConsoleKey.Escape;
+
+ case '*':
+ return ConsoleKey.Multiply;
+
+ case '+':
+ return ConsoleKey.Add;
+
+ case '-':
+ return ConsoleKey.Subtract;
+
+ case '/':
+ return ConsoleKey.Divide;
+
+ case (char)(0x7F):
+ return ConsoleKey.Delete;
+
+ case ' ':
+ return ConsoleKey.Spacebar;
+
+ default:
+ // 1. Ctrl A to Ctrl Z.
+ if (x >= 1 && x <= 26)
+ {
+ isCtrl = true;
+ return ConsoleKey.A + x - 1;
+ }
+
+ // 2. Numbers from 0 to 9.
+ if (x >= '0' && x <= '9')
+ {
+ return ConsoleKey.D0 + x - '0';
+ }
+
+ //3. A to Z
+ if (x >= 'A' && x <= 'Z')
+ {
+ isShift = true;
+ return ConsoleKey.A + (x - 'A');
+ }
+
+ // 4. a to z.
+ if (x >= 'a' && x <= 'z')
+ {
+ return ConsoleKey.A + (x - 'a');
+ }
+
+ break;
+ }
+
+ return default(ConsoleKey);
+ }
+
+#if !UNIX
+ internal static char GetCharFromConsoleKey(ConsoleKey key, ConsoleModifiers modifiers)
+ {
+ // default for unprintables and unhandled
+ char keyChar = '\u0000';
+
+ // emulate GetKeyboardState bitmap - set high order bit for relevant modifier virtual keys
+ var state = new byte[256];
+ state[NativeMethods.VK_SHIFT] = (byte)(((modifiers & ConsoleModifiers.Shift) != 0) ? 0x80 : 0);
+ state[NativeMethods.VK_CONTROL] = (byte)(((modifiers & ConsoleModifiers.Control) != 0) ? 0x80 : 0);
+ state[NativeMethods.VK_ALT] = (byte)(((modifiers & ConsoleModifiers.Alt) != 0) ? 0x80 : 0);
+
+ // a ConsoleKey enum's value is a virtual key code
+ uint virtualKey = (uint)key;
+
+ // get corresponding scan code
+ uint scanCode = NativeMethods.MapVirtualKey(virtualKey, NativeMethods.MAPVK_VK_TO_VSC);
+
+ // get corresponding character - may be 0, 1 or 2 in length (diacritics)
+ var chars = new char[2];
+ int charCount = NativeMethods.ToUnicode(
+ virtualKey, scanCode, state, chars, chars.Length, NativeMethods.MENU_IS_INACTIVE);
+
+ // TODO: support diacritics (charCount == 2)
+ if (charCount == 1)
+ {
+ keyChar = chars[0];
+ }
+ return keyChar;
+ }
+#endif
+ }
+}
diff --git a/src/Microsoft.PowerShell.PSReadLine/ConsoleLib.cs b/src/Microsoft.PowerShell.PSReadLine/ConsoleLib.cs
new file mode 100644
index 00000000000..177f0779fa7
--- /dev/null
+++ b/src/Microsoft.PowerShell.PSReadLine/ConsoleLib.cs
@@ -0,0 +1,1143 @@
+/********************************************************************++
+Copyright (c) Microsoft Corporation. All rights reserved.
+--********************************************************************/
+
+using System;
+using System.ComponentModel;
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+using System.Runtime.InteropServices;
+using System.Text;
+using Microsoft.Win32.SafeHandles;
+
+namespace Microsoft.PowerShell.Internal
+{
+#pragma warning disable 1591
+
+ internal static class NativeMethods
+ {
+ public const uint MAPVK_VK_TO_VSC = 0x00;
+ public const uint MAPVK_VSC_TO_VK = 0x01;
+ public const uint MAPVK_VK_TO_CHAR = 0x02;
+
+ public const byte VK_SHIFT = 0x10;
+ public const byte VK_CONTROL = 0x11;
+ public const byte VK_ALT = 0x12;
+ public const uint MENU_IS_ACTIVE = 0x01;
+ public const uint MENU_IS_INACTIVE = 0x00; // windows key
+
+ public const uint ENABLE_PROCESSED_INPUT = 0x0001;
+ public const uint ENABLE_LINE_INPUT = 0x0002;
+ public const uint ENABLE_WINDOW_INPUT = 0x0008;
+ public const uint ENABLE_MOUSE_INPUT = 0x0010;
+
+ public const int FontTypeMask = 0x06;
+ public const int TrueTypeFont = 0x04;
+
+ internal static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1); // WinBase.h
+
+ [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
+ public static extern IntPtr GetStdHandle(uint handleId);
+
+ [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
+ public static extern bool GetConsoleMode(IntPtr hConsoleOutput, out uint dwMode);
+
+ [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
+ public static extern bool SetConsoleMode(IntPtr hConsoleOutput, uint dwMode);
+
+ [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
+ public static extern bool ScrollConsoleScreenBuffer(IntPtr hConsoleOutput,
+ ref SMALL_RECT lpScrollRectangle,
+ IntPtr lpClipRectangle,
+ COORD dwDestinationOrigin,
+ ref CHAR_INFO lpFill);
+
+ [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
+ public static extern bool WriteConsole(IntPtr hConsoleOutput, string lpBuffer, uint nNumberOfCharsToWrite, out uint lpNumberOfCharsWritten, IntPtr lpReserved);
+
+ [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
+ public static extern bool SetConsoleCtrlHandler(BreakHandler handlerRoutine, bool add);
+
+ [DllImport("KERNEL32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
+ public static extern bool WriteConsoleOutput(IntPtr consoleOutput, CHAR_INFO[] buffer, COORD bufferSize, COORD bufferCoord, ref SMALL_RECT writeRegion);
+
+ [DllImport("KERNEL32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
+ public static extern bool ReadConsoleOutput(IntPtr consoleOutput, [Out] CHAR_INFO[] buffer, COORD bufferSize, COORD bufferCoord, ref SMALL_RECT readRegion);
+
+ [DllImport("user32.dll", SetLastError = true)]
+ public static extern uint MapVirtualKey(uint uCode, uint uMapType);
+
+ [DllImport("user32.dll", CharSet = CharSet.Unicode)]
+ public static extern int ToUnicode(uint uVirtKey, uint uScanCode, byte[] lpKeyState,
+ [MarshalAs(UnmanagedType.LPArray)] [Out] char[] chars, int charMaxCount, uint flags);
+
+ [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
+ public static extern short VkKeyScan(char @char);
+
+ [DllImport("kernel32.dll", SetLastError = false, CharSet = CharSet.Unicode)]
+ public static extern uint GetConsoleOutputCP();
+
+ [DllImport("User32.dll", SetLastError = false, CharSet = CharSet.Unicode)]
+ public static extern int ReleaseDC(IntPtr hwnd, IntPtr hdc);
+
+ [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
+ public static extern IntPtr GetConsoleWindow();
+
+ [DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
+ public static extern IntPtr GetDC(IntPtr hwnd);
+
+ [DllImport("GDI32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
+ public static extern bool TranslateCharsetInfo(IntPtr src, out CHARSETINFO Cs, uint options);
+
+ [DllImport("GDI32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
+ public static extern bool GetTextMetrics(IntPtr hdc, out TEXTMETRIC tm);
+
+ [DllImport("GDI32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
+ public static extern bool GetCharWidth32(IntPtr hdc, uint first, uint last, out int width);
+
+ [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
+ public static extern IntPtr CreateFile
+ (
+ string fileName,
+ uint desiredAccess,
+ uint ShareModes,
+ IntPtr securityAttributes,
+ uint creationDisposition,
+ uint flagsAndAttributes,
+ IntPtr templateFileWin32Handle
+ );
+
+ [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
+ internal static extern bool GetCurrentConsoleFontEx(IntPtr consoleOutput, bool bMaximumWindow, ref CONSOLE_FONT_INFO_EX consoleFontInfo);
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct COLORREF
+ {
+ internal uint ColorDWORD;
+
+ internal uint R
+ {
+ get { return ColorDWORD & 0xff; }
+ }
+
+ internal uint G
+ {
+ get { return (ColorDWORD >> 8) & 0xff; }
+ }
+
+ internal uint B
+ {
+ get { return (ColorDWORD >> 16) & 0xff; }
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct CONSOLE_SCREEN_BUFFER_INFO_EX
+ {
+ internal int cbSize;
+ internal COORD dwSize;
+ internal COORD dwCursorPosition;
+ internal ushort wAttributes;
+ internal SMALL_RECT srWindow;
+ internal COORD dwMaximumWindowSize;
+ internal ushort wPopupAttributes;
+ internal bool bFullscreenSupported;
+ internal COLORREF Black;
+ internal COLORREF DarkBlue;
+ internal COLORREF DarkGreen;
+ internal COLORREF DarkCyan;
+ internal COLORREF DarkRed;
+ internal COLORREF DarkMagenta;
+ internal COLORREF DarkYellow;
+ internal COLORREF Gray;
+ internal COLORREF DarkGray;
+ internal COLORREF Blue;
+ internal COLORREF Green;
+ internal COLORREF Cyan;
+ internal COLORREF Red;
+ internal COLORREF Magenta;
+ internal COLORREF Yellow;
+ internal COLORREF White;
+ }
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ internal static extern bool GetConsoleScreenBufferInfoEx(IntPtr hConsoleOutput,
+ ref CONSOLE_SCREEN_BUFFER_INFO_EX csbe);
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ internal static extern bool SetConsoleScreenBufferInfoEx(IntPtr hConsoleOutput,
+ ref CONSOLE_SCREEN_BUFFER_INFO_EX csbe);
+ }
+
+ public delegate bool BreakHandler(ConsoleBreakSignal ConsoleBreakSignal);
+
+ public enum ConsoleBreakSignal : uint
+ {
+ CtrlC = 0,
+ CtrlBreak = 1,
+ Close = 2,
+ Logoff = 5,
+ Shutdown = 6,
+ None = 255,
+ }
+
+ internal enum CHAR_INFO_Attributes : ushort
+ {
+ COMMON_LVB_LEADING_BYTE = 0x0100,
+ COMMON_LVB_TRAILING_BYTE = 0x0200
+ }
+
+ public enum StandardHandleId : uint
+ {
+ Error = unchecked((uint)-12),
+ Output = unchecked((uint)-11),
+ Input = unchecked((uint)-10),
+ }
+
+ [Flags]
+ internal enum AccessQualifiers : uint
+ {
+ // From winnt.h
+ GenericRead = 0x80000000,
+ GenericWrite = 0x40000000
+ }
+
+ internal enum CreationDisposition : uint
+ {
+ // From winbase.h
+ CreateNew = 1,
+ CreateAlways = 2,
+ OpenExisting = 3,
+ OpenAlways = 4,
+ TruncateExisting = 5
+ }
+
+ [Flags]
+ internal enum ShareModes : uint
+ {
+ // From winnt.h
+ ShareRead = 0x00000001,
+ ShareWrite = 0x00000002
+ }
+
+ public struct SMALL_RECT
+ {
+ public short Left;
+ public short Top;
+ public short Right;
+ public short Bottom;
+
+ [ExcludeFromCodeCoverage]
+ public override string ToString()
+ {
+ return String.Format(CultureInfo.InvariantCulture, "{0},{1},{2},{3}", Left, Top, Right, Bottom);
+ }
+ }
+
+ internal struct COORD
+ {
+ public short X;
+ public short Y;
+
+ [ExcludeFromCodeCoverage]
+ public override string ToString()
+ {
+ return String.Format(CultureInfo.InvariantCulture, "{0},{1}", X, Y);
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct FONTSIGNATURE
+ {
+ //From public\sdk\inc\wingdi.h
+
+ // fsUsb*: A 128-bit Unicode subset bitfield (USB) identifying up to 126 Unicode subranges
+ internal uint fsUsb0;
+ internal uint fsUsb1;
+ internal uint fsUsb2;
+ internal uint fsUsb3;
+ // fsCsb*: A 64-bit, code-page bitfield (CPB) that identifies a specific character set or code page.
+ internal uint fsCsb0;
+ internal uint fsCsb1;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct CHARSETINFO
+ {
+ //From public\sdk\inc\wingdi.h
+ internal uint ciCharset; // Character set value.
+ internal uint ciACP; // ANSI code-page identifier.
+ internal FONTSIGNATURE fs;
+ }
+
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
+ internal struct TEXTMETRIC
+ {
+ //From public\sdk\inc\wingdi.h
+ public int tmHeight;
+ public int tmAscent;
+ public int tmDescent;
+ public int tmInternalLeading;
+ public int tmExternalLeading;
+ public int tmAveCharWidth;
+ public int tmMaxCharWidth;
+ public int tmWeight;
+ public int tmOverhang;
+ public int tmDigitizedAspectX;
+ public int tmDigitizedAspectY;
+ public char tmFirstChar;
+ public char tmLastChar;
+ public char tmDefaultChar;
+ public char tmBreakChar;
+ public byte tmItalic;
+ public byte tmUnderlined;
+ public byte tmStruckOut;
+ public byte tmPitchAndFamily;
+ public byte tmCharSet;
+ }
+
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
+ internal struct CONSOLE_FONT_INFO_EX
+ {
+ internal int cbSize;
+ internal int nFont;
+ internal short FontWidth;
+ internal short FontHeight;
+ internal int FontFamily;
+ internal int FontWeight;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
+ internal string FontFace;
+ }
+
+ public struct BufferChar
+ {
+ public char UnicodeChar;
+ public ConsoleColor ForegroundColor;
+ public ConsoleColor BackgroundColor;
+#if !UNIX
+ public bool IsLeadByte;
+ public bool IsTrailByte;
+#endif
+ public bool Inverse;
+
+#if !UNIX
+ public CHAR_INFO ToCharInfo()
+ {
+ int fg = (int) ForegroundColor;
+ int bg = (int) BackgroundColor;
+
+ if (fg < 0 || fg > 0xf || bg < 0 || bg > 0xf)
+ throw new InvalidOperationException();
+
+ if (Inverse)
+ {
+ // TODO: check $host.UI.SupportsVirtualTerminal and maybe set Inverse instead (it does look weird)
+ fg = fg ^ 7;
+ bg = bg ^ 7;
+ }
+ ushort attrs = (ushort)(fg | (bg << 4));
+ if (IsLeadByte)
+ attrs |= (ushort)CHAR_INFO_Attributes.COMMON_LVB_LEADING_BYTE;
+ if (IsTrailByte)
+ attrs |= (ushort)CHAR_INFO_Attributes.COMMON_LVB_TRAILING_BYTE;
+ CHAR_INFO result = new CHAR_INFO
+ {
+ UnicodeChar = UnicodeChar,
+ Attributes = attrs,
+ };
+
+ return result;
+ }
+
+ public static BufferChar FromCharInfo(CHAR_INFO charInfo)
+ {
+ BufferChar result = new BufferChar
+ {
+ UnicodeChar = (char) charInfo.UnicodeChar,
+ ForegroundColor = (ConsoleColor)(charInfo.Attributes & 0xf),
+ BackgroundColor = (ConsoleColor)((charInfo.Attributes & 0x00f0) >> 4),
+ };
+ return result;
+ }
+#endif
+ }
+
+ public struct CHAR_INFO
+ {
+ public ushort UnicodeChar;
+ public ushort Attributes;
+ }
+
+ internal static class ConsoleKeyInfoExtension
+ {
+#if !UNIX
+ private static bool _toUnicodeApiAvailable = true;
+
+ static ConsoleKeyInfoExtension()
+ {
+ try
+ {
+ var chars = new char[2];
+ NativeMethods.ToUnicode(13, 28, null, chars, chars.Length, 0);
+ }
+ catch (System.EntryPointNotFoundException)
+ {
+ _toUnicodeApiAvailable = false; // api not available on NanoServer
+ }
+ }
+#endif
+
+ public static string ToGestureString(this ConsoleKeyInfo key)
+ {
+ var mods = key.Modifiers;
+
+ var sb = new StringBuilder();
+ if (key.Modifiers.HasFlag(ConsoleModifiers.Control))
+ {
+ sb.Append("Ctrl");
+ }
+ if (key.Modifiers.HasFlag(ConsoleModifiers.Alt))
+ {
+ if (sb.Length > 0)
+ sb.Append("+");
+ sb.Append("Alt");
+ }
+
+ char c = key.KeyChar;
+#if !UNIX
+ if (_toUnicodeApiAvailable)
+ {
+ // Windows cannot use KeyChar as some chords (like Ctrl+[) show up as control characters.
+ c = ConsoleKeyChordConverter.GetCharFromConsoleKey(key.Key,
+ (mods & ConsoleModifiers.Shift) != 0 ? ConsoleModifiers.Shift : 0);
+ }
+#endif
+
+ if (char.IsControl(c) || char.IsWhiteSpace(c))
+ {
+ if (key.Modifiers.HasFlag(ConsoleModifiers.Shift))
+ {
+ if (sb.Length > 0)
+ sb.Append("+");
+ sb.Append("Shift");
+ }
+ if (sb.Length > 0)
+ sb.Append("+");
+ sb.Append(key.Key);
+ }
+ else
+ {
+ if (sb.Length > 0)
+ sb.Append("+");
+ sb.Append(c);
+ }
+ return sb.ToString();
+ }
+ }
+
+#if !UNIX
+ internal class ConhostConsole : IConsole
+ {
+ [SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")]
+ private IntPtr _hwnd = (IntPtr)0;
+ [SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")]
+ private IntPtr _hDC = (IntPtr)0;
+ private uint _codePage;
+ private bool _istmInitialized = false;
+ private TEXTMETRIC _tm = new TEXTMETRIC();
+ private bool _trueTypeInUse = false;
+ private static bool _getCurrentConsoleFontExApiAvailable = true;
+
+ private readonly Lazy _outputHandle = new Lazy(() =>
+ {
+ // We use CreateFile here instead of GetStdWin32Handle, as GetStdWin32Handle will return redirected handles
+ var handle = NativeMethods.CreateFile(
+ "CONOUT$",
+ (UInt32)(AccessQualifiers.GenericRead | AccessQualifiers.GenericWrite),
+ (UInt32)ShareModes.ShareWrite,
+ (IntPtr)0,
+ (UInt32)CreationDisposition.OpenExisting,
+ 0,
+ (IntPtr)0);
+
+ if (handle == NativeMethods.INVALID_HANDLE_VALUE)
+ {
+ int err = Marshal.GetLastWin32Error();
+ Win32Exception innerException = new Win32Exception(err);
+ throw new Exception("Failed to retrieve the input console handle.", innerException);
+ }
+
+ return new SafeFileHandle(handle, true);
+ }
+ );
+
+ private readonly Lazy _inputHandle = new Lazy(() =>
+ {
+ // We use CreateFile here instead of GetStdWin32Handle, as GetStdWin32Handle will return redirected handles
+ var handle = NativeMethods.CreateFile(
+ "CONIN$",
+ (UInt32)(AccessQualifiers.GenericRead | AccessQualifiers.GenericWrite),
+ (UInt32)ShareModes.ShareWrite,
+ (IntPtr)0,
+ (UInt32)CreationDisposition.OpenExisting,
+ 0,
+ (IntPtr)0);
+
+ if (handle == NativeMethods.INVALID_HANDLE_VALUE)
+ {
+ int err = Marshal.GetLastWin32Error();
+ Win32Exception innerException = new Win32Exception(err);
+ throw new Exception("Failed to retrieve the input console handle.", innerException);
+ }
+
+ return new SafeFileHandle(handle, true);
+ });
+
+ public object GetConsoleInputMode()
+ {
+ uint mode;
+ var handle = _inputHandle.Value.DangerousGetHandle();
+ NativeMethods.GetConsoleMode(handle, out mode);
+ return mode;
+ }
+
+ public void SetConsoleInputMode(object modeObj)
+ {
+ if (modeObj is uint)
+ {
+ // Clear a couple flags so we can actually receive certain keys:
+ // ENABLE_PROCESSED_INPUT - enables Ctrl+C
+ // ENABLE_LINE_INPUT - enables Ctrl+S
+ // Also clear a couple flags so we don't mask the input that we ignore:
+ // ENABLE_MOUSE_INPUT - mouse events
+ // ENABLE_WINDOW_INPUT - window resize events
+ var mode = (uint)modeObj &
+ ~(NativeMethods.ENABLE_PROCESSED_INPUT |
+ NativeMethods.ENABLE_LINE_INPUT |
+ NativeMethods.ENABLE_WINDOW_INPUT |
+ NativeMethods.ENABLE_MOUSE_INPUT);
+
+ var handle = _inputHandle.Value.DangerousGetHandle();
+ NativeMethods.SetConsoleMode(handle, mode);
+ }
+ }
+
+ public void RestoreConsoleInputMode(object modeObj)
+ {
+ if (modeObj is uint)
+ {
+ var handle = _inputHandle.Value.DangerousGetHandle();
+ NativeMethods.SetConsoleMode(handle, (uint)modeObj);
+ }
+ }
+
+ public ConsoleKeyInfo ReadKey()
+ {
+ return Console.ReadKey(true);
+ }
+
+ public bool KeyAvailable
+ {
+ get { return Console.KeyAvailable; }
+ }
+
+ public int CursorLeft
+ {
+ get { return Console.CursorLeft; }
+ set { Console.CursorLeft = value; }
+ }
+
+ public int CursorTop
+ {
+ get { return Console.CursorTop; }
+ set { Console.CursorTop = value; }
+ }
+
+ public int CursorSize
+ {
+ get { return Console.CursorSize; }
+ set { Console.CursorSize = value; }
+ }
+
+ public int BufferWidth
+ {
+ get { return Console.BufferWidth; }
+ set { Console.BufferWidth = value; }
+ }
+
+ public int BufferHeight
+ {
+ get { return Console.BufferHeight; }
+ set { Console.BufferHeight = value; }
+ }
+
+ public int WindowWidth
+ {
+ get { return Console.WindowWidth; }
+ set { Console.WindowWidth = value; }
+ }
+
+ public int WindowHeight
+ {
+ get { return Console.WindowHeight; }
+ set { Console.WindowHeight = value; }
+ }
+
+ public int WindowTop
+ {
+ get { return Console.WindowTop; }
+ set { Console.WindowTop = value; }
+ }
+
+ public ConsoleColor BackgroundColor
+ {
+ get { return Console.BackgroundColor; }
+ set { Console.BackgroundColor = value; }
+ }
+
+ public ConsoleColor ForegroundColor
+ {
+ get { return Console.ForegroundColor; }
+ set { Console.ForegroundColor = value; }
+ }
+
+ public void SetWindowPosition(int left, int top)
+ {
+ Console.SetWindowPosition(left, top);
+ }
+
+ public void SetCursorPosition(int left, int top)
+ {
+ Console.SetCursorPosition(left, top);
+ }
+
+ public void Write(string value)
+ {
+ Console.Write(value);
+ }
+
+ public void WriteLine(string value)
+ {
+ Console.WriteLine(value);
+ }
+
+ public void WriteBufferLines(BufferChar[] buffer, ref int top)
+ {
+ WriteBufferLines(buffer, ref top, true);
+ }
+
+ public void WriteBufferLines(BufferChar[] buffer, ref int top, bool ensureBottomLineVisible)
+ {
+ int bufferWidth = Console.BufferWidth;
+ int bufferLineCount = buffer.Length / bufferWidth;
+ if ((top + bufferLineCount) > Console.BufferHeight)
+ {
+ var scrollCount = (top + bufferLineCount) - Console.BufferHeight;
+ ScrollBuffer(scrollCount);
+ top -= scrollCount;
+ }
+
+ var handle = NativeMethods.GetStdHandle((uint) StandardHandleId.Output);
+ var bufferSize = new COORD
+ {
+ X = (short) bufferWidth,
+ Y = (short) bufferLineCount
+ };
+ var bufferCoord = new COORD {X = 0, Y = 0};
+ var bottom = top + bufferLineCount - 1;
+ var writeRegion = new SMALL_RECT
+ {
+ Top = (short) top,
+ Left = 0,
+ Bottom = (short) bottom,
+ Right = (short) (bufferWidth - 1)
+ };
+ NativeMethods.WriteConsoleOutput(handle, ToCharInfoBuffer(buffer),
+ bufferSize, bufferCoord, ref writeRegion);
+
+ // Now make sure the bottom line is visible
+ if (ensureBottomLineVisible &&
+ (bottom >= (Console.WindowTop + Console.WindowHeight)))
+ {
+ Console.CursorTop = bottom;
+ }
+ }
+
+ public void ScrollBuffer(int lines)
+ {
+ var handle = NativeMethods.GetStdHandle((uint) StandardHandleId.Output);
+
+ var scrollRectangle = new SMALL_RECT
+ {
+ Top = (short) lines,
+ Left = 0,
+ Bottom = (short)(Console.BufferHeight - 1),
+ Right = (short)Console.BufferWidth
+ };
+ var destinationOrigin = new COORD {X = 0, Y = 0};
+ var fillChar = new CHAR_INFO
+ {
+ UnicodeChar = ' ',
+ Attributes = (ushort)((int)Console.ForegroundColor | ((int)Console.BackgroundColor << 4))
+ };
+ NativeMethods.ScrollConsoleScreenBuffer(handle, ref scrollRectangle, IntPtr.Zero, destinationOrigin, ref fillChar);
+ }
+
+ public BufferChar[] ReadBufferLines(int top, int count)
+ {
+ var result = new CHAR_INFO[BufferWidth * count];
+ var handle = NativeMethods.GetStdHandle((uint) StandardHandleId.Output);
+
+ var readBufferSize = new COORD {
+ X = (short)BufferWidth,
+ Y = (short)count};
+ var readBufferCoord = new COORD {X = 0, Y = 0};
+ var readRegion = new SMALL_RECT
+ {
+ Top = (short)top,
+ Left = 0,
+ Bottom = (short)(top + count),
+ Right = (short)(BufferWidth - 1)
+ };
+ NativeMethods.ReadConsoleOutput(handle, result,
+ readBufferSize, readBufferCoord, ref readRegion);
+
+ return ToBufferCharBuffer(result);
+ }
+
+ [SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods",
+ Justification = "Then the API we pass the handle to will return an error if it is invalid. They are not exposed.")]
+ internal static CONSOLE_FONT_INFO_EX GetConsoleFontInfo(SafeFileHandle consoleHandle)
+ {
+
+ CONSOLE_FONT_INFO_EX fontInfo = new CONSOLE_FONT_INFO_EX();
+ fontInfo.cbSize = Marshal.SizeOf(fontInfo);
+ bool result = true;
+ if (_getCurrentConsoleFontExApiAvailable)
+ {
+ try
+ {
+ result = NativeMethods.GetCurrentConsoleFontEx(consoleHandle.DangerousGetHandle(), false, ref fontInfo);
+ }
+ catch (System.EntryPointNotFoundException)
+ {
+ _getCurrentConsoleFontExApiAvailable = false; // api not available on NanoServer
+ }
+ }
+
+ if (result == false)
+ {
+ _getCurrentConsoleFontExApiAvailable = false; // api not supported on ServerCore
+ }
+ return fontInfo;
+ }
+
+ public int LengthInBufferCells(char c)
+ {
+ if (!IsCJKOutputCodePage() || !_trueTypeInUse)
+ return 1;
+
+ return LengthInBufferCellsFE(c);
+ }
+
+ internal static bool IsAnyDBCSCharSet(uint charSet)
+ {
+ const uint SHIFTJIS_CHARSET = 128;
+ const uint HANGUL_CHARSET = 129;
+ const uint CHINESEBIG5_CHARSET = 136;
+ const uint GB2312_CHARSET = 134;
+ return charSet == SHIFTJIS_CHARSET || charSet == HANGUL_CHARSET ||
+ charSet == CHINESEBIG5_CHARSET || charSet == GB2312_CHARSET;
+ }
+
+ internal uint CodePageToCharSet()
+ {
+ CHARSETINFO csi;
+ const uint TCI_SRCCODEPAGE = 2;
+ const uint OEM_CHARSET = 255;
+ if (!NativeMethods.TranslateCharsetInfo((IntPtr)_codePage, out csi, TCI_SRCCODEPAGE))
+ {
+ csi.ciCharset = OEM_CHARSET;
+ }
+ return csi.ciCharset;
+ }
+
+ ///
+ /// Check if the output buffer code page is Japanese, Simplified Chinese, Korean, or Traditional Chinese
+ ///
+ /// true if it is CJK code page; otherwise, false.
+ internal bool IsCJKOutputCodePage()
+ {
+ return _codePage == 932 || // Japanese
+ _codePage == 936 || // Simplified Chinese
+ _codePage == 949 || // Korean
+ _codePage == 950; // Traditional Chinese
+ }
+
+ internal bool IsAvailableFarEastCodePage()
+ {
+ uint charSet = CodePageToCharSet();
+ return IsAnyDBCSCharSet(charSet);
+ }
+
+ internal int LengthInBufferCellsFE(char c)
+ {
+ if (0x20 <= c && c <= 0x7e)
+ {
+ /* ASCII */
+ return 1;
+ }
+ else if (0x3041 <= c && c <= 0x3094)
+ {
+ /* Hiragana */
+ return 2;
+ }
+ else if (0x30a1 <= c && c <= 0x30f6)
+ {
+ /* Katakana */
+ return 2;
+ }
+ else if (0x3105 <= c && c <= 0x312c)
+ {
+ /* Bopomofo */
+ return 2;
+ }
+ else if (0x3131 <= c && c <= 0x318e)
+ {
+ /* Hangul Elements */
+ return 2;
+ }
+ else if (0xac00 <= c && c <= 0xd7a3)
+ {
+ /* Korean Hangul Syllables */
+ return 2;
+ }
+ else if (0xff01 <= c && c <= 0xff5e)
+ {
+ /* Fullwidth ASCII variants */
+ return 2;
+ }
+ else if (0xff61 <= c && c <= 0xff9f)
+ {
+ /* Halfwidth Katakana variants */
+ return 1;
+ }
+ else if ((0xffa0 <= c && c <= 0xffbe) ||
+ (0xffc2 <= c && c <= 0xffc7) ||
+ (0xffca <= c && c <= 0xffcf) ||
+ (0xffd2 <= c && c <= 0xffd7) ||
+ (0xffda <= c && c <= 0xffdc))
+ {
+ /* Halfwidth Hangul variants */
+ return 1;
+ }
+ else if (0xffe0 <= c && c <= 0xffe6)
+ {
+ /* Fullwidth symbol variants */
+ return 2;
+ }
+ else if (0x4e00 <= c && c <= 0x9fa5)
+ {
+ /* Han Ideographic */
+ return 2;
+ }
+ else if (0xf900 <= c && c <= 0xfa2d)
+ {
+ /* Han Compatibility Ideographs */
+ return 2;
+ }
+ else
+ {
+ /* Unknown character: need to use GDI*/
+ if (_hDC == (IntPtr)0)
+ {
+ _hwnd = NativeMethods.GetConsoleWindow();
+ if ((IntPtr)0 == _hwnd)
+ {
+ return 1;
+ }
+ _hDC = NativeMethods.GetDC(_hwnd);
+ if ((IntPtr)0 == _hDC)
+ {
+ //Don't throw exception so that output can continue
+ return 1;
+ }
+ }
+ bool result = true;
+ if (!_istmInitialized)
+ {
+ result = NativeMethods.GetTextMetrics(_hDC, out _tm);
+ if (!result)
+ {
+ return 1;
+ }
+ _istmInitialized = true;
+ }
+ int width;
+ result = NativeMethods.GetCharWidth32(_hDC, (uint)c, (uint)c, out width);
+ if (!result)
+ {
+ return 1;
+ }
+ if (width >= _tm.tmMaxCharWidth)
+ {
+ return 2;
+ }
+ }
+ return 1;
+ }
+
+ public void StartRender()
+ {
+ _codePage = NativeMethods.GetConsoleOutputCP();
+ _istmInitialized = false;
+ var consoleHandle = _outputHandle.Value;
+ CONSOLE_FONT_INFO_EX fontInfo = ConhostConsole.GetConsoleFontInfo(consoleHandle);
+ int fontType = fontInfo.FontFamily & NativeMethods.FontTypeMask;
+ _trueTypeInUse = (fontType & NativeMethods.TrueTypeFont) == NativeMethods.TrueTypeFont;
+ }
+
+ public void EndRender()
+ {
+ if (_hwnd != (IntPtr)0 && _hDC != (IntPtr)0)
+ {
+ NativeMethods.ReleaseDC(_hwnd, _hDC);
+ }
+ }
+
+ private CHAR_INFO[] cachedBuffer;
+ private CHAR_INFO[] ToCharInfoBuffer(BufferChar[] buffer)
+ {
+ if (cachedBuffer == null || cachedBuffer.Length != buffer.Length)
+ {
+ cachedBuffer = new CHAR_INFO[buffer.Length];
+ }
+
+ for (int i = 0; i < buffer.Length; i++)
+ {
+ cachedBuffer[i] = buffer[i].ToCharInfo();
+ }
+
+ return cachedBuffer;
+ }
+
+ private BufferChar[] ToBufferCharBuffer(CHAR_INFO[] buffer)
+ {
+ var result = new BufferChar[buffer.Length];
+
+ for (int i = 0; i < buffer.Length; i++)
+ {
+ result[i] = BufferChar.FromCharInfo(buffer[i]);
+ }
+
+ return result;
+ }
+
+ }
+
+#else
+
+ internal class TTYConsole : IConsole
+ {
+ public object GetConsoleInputMode()
+ {
+ return Console.TreatControlCAsInput;
+ }
+
+ public void SetConsoleInputMode(object modeObj)
+ {
+ Console.TreatControlCAsInput = true;
+ }
+
+ public void RestoreConsoleInputMode(object modeObj)
+ {
+ if (modeObj is bool)
+ {
+ Console.TreatControlCAsInput = (bool)modeObj;
+ }
+ }
+
+ public ConsoleKeyInfo ReadKey()
+ {
+ return Console.ReadKey(true);
+ }
+
+ public bool KeyAvailable
+ {
+ get { return Console.KeyAvailable; }
+ }
+
+ public int CursorLeft
+ {
+ get { return Console.CursorLeft; }
+ set { Console.CursorLeft = value; }
+ }
+
+ public int CursorTop
+ {
+ get { return Console.CursorTop; }
+ set { Console.CursorTop = value; }
+ }
+
+ public int CursorSize
+ {
+ get { return Console.CursorSize; }
+ set { Console.CursorSize = value; }
+ }
+
+ public int BufferWidth
+ {
+ get { return Console.BufferWidth; }
+ set { Console.BufferWidth = value; }
+ }
+
+ public int BufferHeight
+ {
+ get { return Console.BufferHeight; }
+ set { Console.BufferHeight = value; }
+ }
+
+ public int WindowWidth
+ {
+ get { return Console.WindowWidth; }
+ set { Console.WindowWidth = value; }
+ }
+
+ public int WindowHeight
+ {
+ get { return Console.WindowHeight; }
+ set { Console.WindowHeight = value; }
+ }
+
+ public int WindowTop
+ {
+ get { return Console.WindowTop; }
+ set { Console.WindowTop = value; }
+ }
+
+ public ConsoleColor BackgroundColor
+ {
+ get { return Console.BackgroundColor; }
+ set { Console.BackgroundColor = value; }
+ }
+
+ public ConsoleColor ForegroundColor
+ {
+ get { return Console.ForegroundColor; }
+ set { Console.ForegroundColor = value; }
+ }
+
+ public void SetWindowPosition(int left, int top)
+ {
+ Console.SetWindowPosition(left, top);
+ }
+
+ public void SetCursorPosition(int left, int top)
+ {
+ Console.SetCursorPosition(left, top);
+ }
+
+ public void Write(string value)
+ {
+ Console.Write(value);
+ }
+
+ public void WriteLine(string value)
+ {
+ Console.WriteLine(value);
+ }
+
+ public void WriteBufferLines(BufferChar[] buffer, ref int top)
+ {
+ WriteBufferLines(buffer, ref top, true);
+ }
+
+ public void WriteBufferLines(BufferChar[] buffer, ref int top, bool ensureBottomLineVisible)
+ {
+ int bufferWidth = Console.BufferWidth;
+ int bufferLineCount = buffer.Length / bufferWidth;
+ if ((top + bufferLineCount) > Console.BufferHeight)
+ {
+ var scrollCount = (top + bufferLineCount) - Console.BufferHeight;
+ ScrollBuffer(scrollCount);
+ top -= scrollCount;
+ }
+
+ ConsoleColor foregroundColor = Console.ForegroundColor;
+ ConsoleColor backgroundColor = Console.BackgroundColor;
+
+ Console.SetCursorPosition(0, (top>=0) ? top : 0);
+
+ var lastForegroundColor = (ConsoleColor)(-1);
+ var lastBackgroundColor = (ConsoleColor)(-1);
+ for (int i = 0; i < buffer.Length; ++i)
+ {
+ // TODO: use escape sequences for better perf
+ var nextForegroundColor = buffer[i].ForegroundColor;
+ var nextBackgroundColor = buffer[i].BackgroundColor;
+ if (nextForegroundColor != lastForegroundColor)
+ {
+ Console.ForegroundColor = lastForegroundColor = nextForegroundColor;
+ }
+ if (nextBackgroundColor != lastBackgroundColor)
+ {
+ Console.BackgroundColor = lastBackgroundColor = nextBackgroundColor;
+ }
+
+ Console.Write(buffer[i].UnicodeChar);
+ }
+
+ if (foregroundColor != lastForegroundColor)
+ {
+ Console.ForegroundColor = foregroundColor;
+ }
+ if (backgroundColor != lastBackgroundColor)
+ {
+ Console.BackgroundColor = backgroundColor;
+ }
+ }
+
+ public void ScrollBuffer(int lines)
+ {
+ for (int i=0; i _edits;
+ public int _undoEditIndex;
+ public bool _saved;
+ public bool _fromDifferentLiveSession;
+ }
+
+ // History state
+ private HistoryQueue _history;
+ private Dictionary _hashedHistory;
+ private int _currentHistoryIndex;
+ private int _getNextHistoryIndex;
+ private int _searchHistoryCommandCount;
+ private int _recallHistoryCommandCount;
+ private string _searchHistoryPrefix;
+ // When cycling through history, the current line (not yet added to history)
+ // is saved here so it can be restored.
+ private readonly HistoryItem _savedCurrentLine;
+
+ private Mutex _historyFileMutex;
+ private long _historyFileLastSavedSize;
+
+ private const string _forwardISearchPrompt = "fwd-i-search: ";
+ private const string _backwardISearchPrompt = "bck-i-search: ";
+ private const string _failedForwardISearchPrompt = "failed-fwd-i-search: ";
+ private const string _failedBackwardISearchPrompt = "failed-bck-i-search: ";
+
+ private string MaybeAddToHistory(string result, List edits, int undoEditIndex, bool readingHistoryFile, bool fromDifferentSession)
+ {
+ bool addToHistory = !string.IsNullOrWhiteSpace(result) && ((Options.AddToHistoryHandler == null) || Options.AddToHistoryHandler(result));
+ if (addToHistory)
+ {
+ _history.Enqueue(new HistoryItem
+ {
+ _line = result,
+ _edits = edits,
+ _undoEditIndex = undoEditIndex,
+ _saved = readingHistoryFile,
+ _fromDifferentLiveSession = fromDifferentSession,
+ });
+ _currentHistoryIndex = _history.Count;
+
+ if (_options.HistorySaveStyle == HistorySaveStyle.SaveIncrementally && !readingHistoryFile)
+ {
+ IncrementalHistoryWrite();
+ }
+ }
+
+ // Clear the saved line unless we used AcceptAndGetNext in which
+ // case we're really still in middle of history and might want
+ // to recall the saved line.
+ if (_getNextHistoryIndex == 0)
+ {
+ _savedCurrentLine._line = null;
+ _savedCurrentLine._edits = null;
+ _savedCurrentLine._undoEditIndex = 0;
+ }
+ return result;
+ }
+
+ private string GetHistorySaveFileMutexName()
+ {
+ // Return a reasonably unique name - it's not too important as there will rarely
+ // be any contention.
+ return "PSReadlineHistoryFile_" + _options.HistorySavePath.GetHashCode();
+ }
+
+ private void IncrementalHistoryWrite()
+ {
+ var i = _currentHistoryIndex - 1;
+ while (i >= 0)
+ {
+ if (_history[i]._saved)
+ {
+ break;
+ }
+ i -= 1;
+ }
+
+ WriteHistoryRange(i + 1, _history.Count - 1, File.AppendText);
+ }
+
+ private void SaveHistoryAtExit()
+ {
+ WriteHistoryRange(0, _history.Count - 1, File.CreateText);
+ }
+
+
+ private int historyErrorReportedCount;
+ private void ReportHistoryFileError(Exception e)
+ {
+ if (historyErrorReportedCount == 2)
+ return;
+
+ historyErrorReportedCount += 1;
+ var fgColor = Console.ForegroundColor;
+ var bgColor = Console.BackgroundColor;
+ Console.ForegroundColor = Options.ErrorForegroundColor;
+ Console.WriteLine(PSReadLineResources.HistoryFileErrorMessage, Options.HistorySavePath, e.Message);
+ if (historyErrorReportedCount == 2)
+ {
+ Console.WriteLine(PSReadLineResources.HistoryFileErrorFinalMessage);
+ }
+ Console.ForegroundColor = fgColor;
+ Console.BackgroundColor = bgColor;
+ }
+
+ private bool WithHistoryFileMutexDo(int timeout, Action action)
+ {
+ int retryCount = 0;
+ do
+ {
+ try
+ {
+ if (_historyFileMutex.WaitOne(timeout))
+ {
+ try
+ {
+ action();
+ }
+ catch (UnauthorizedAccessException uae)
+ {
+ ReportHistoryFileError(uae);
+ return false;
+ }
+ catch (IOException ioe)
+ {
+ ReportHistoryFileError(ioe);
+ return false;
+ }
+ finally
+ {
+ _historyFileMutex.ReleaseMutex();
+ }
+ }
+ }
+ catch (AbandonedMutexException)
+ {
+ retryCount += 1;
+ }
+ } while (retryCount > 0 && retryCount < 3);
+
+ // No errors to report, so consider it a success even if we timed out on the mutex.
+ return true;
+ }
+
+ private void WriteHistoryRange(int start, int end, Func fileOpener)
+ {
+ WithHistoryFileMutexDo(100, () =>
+ {
+ if (!MaybeReadHistoryFile())
+ return;
+
+ bool retry = true;
+ retry_after_creating_directory:
+ try
+ {
+ using (var file = fileOpener(Options.HistorySavePath))
+ {
+ for (var i = start; i <= end; i++)
+ {
+ _history[i]._saved = true;
+ var line = _history[i]._line.Replace("\n", "`\n");
+ file.WriteLine(line);
+ }
+ }
+ var fileInfo = new FileInfo(Options.HistorySavePath);
+ _historyFileLastSavedSize = fileInfo.Length;
+ }
+ catch (DirectoryNotFoundException)
+ {
+ // Try making the directory, but just once
+ if (retry)
+ {
+ retry = false;
+ Directory.CreateDirectory(Path.GetDirectoryName(Options.HistorySavePath));
+ goto retry_after_creating_directory;
+ }
+ }
+ });
+ }
+
+ private bool MaybeReadHistoryFile()
+ {
+ if (Options.HistorySaveStyle == HistorySaveStyle.SaveIncrementally)
+ {
+ return WithHistoryFileMutexDo(1000, () =>
+ {
+ var fileInfo = new FileInfo(Options.HistorySavePath);
+ if (fileInfo.Exists && fileInfo.Length != _historyFileLastSavedSize)
+ {
+ var historyLines = new List();
+ using (var fs = new FileStream(Options.HistorySavePath, FileMode.Open))
+ using (var sr = new StreamReader(fs))
+ {
+ fs.Seek(_historyFileLastSavedSize, SeekOrigin.Begin);
+
+ while (!sr.EndOfStream)
+ {
+ historyLines.Add(sr.ReadLine());
+ }
+ }
+ UpdateHistoryFromFile(historyLines, fromDifferentSession: true);
+
+ _historyFileLastSavedSize = fileInfo.Length;
+ }
+ });
+ }
+
+ // true means no errors, not that we actually read the file
+ return true;
+ }
+
+ private void ReadHistoryFile()
+ {
+ WithHistoryFileMutexDo(1000, () =>
+ {
+ if (!File.Exists(Options.HistorySavePath))
+ {
+ return;
+ }
+
+ var historyLines = File.ReadAllLines(Options.HistorySavePath);
+ UpdateHistoryFromFile(historyLines, fromDifferentSession: false);
+ var fileInfo = new FileInfo(Options.HistorySavePath);
+ _historyFileLastSavedSize = fileInfo.Length;
+ });
+ }
+
+ void UpdateHistoryFromFile(IEnumerable historyLines, bool fromDifferentSession)
+ {
+ var sb = new StringBuilder();
+ foreach (var line in historyLines)
+ {
+ if (line.EndsWith("`", StringComparison.Ordinal))
+ {
+ sb.Append(line, 0, line.Length - 1);
+ sb.Append('\n');
+ }
+ else if (sb.Length > 0)
+ {
+ sb.Append(line);
+ var l = sb.ToString();
+ var editItems = new List {EditItemInsertString.Create(l, 0)};
+ MaybeAddToHistory(l, editItems, 1, /*readingHistoryFile*/ true, fromDifferentSession);
+ sb.Clear();
+ }
+ else
+ {
+ var editItems = new List {EditItemInsertString.Create(line, 0)};
+ MaybeAddToHistory(line, editItems, 1, /*readingHistoryFile*/ true, fromDifferentSession);
+ }
+ }
+ }
+
+ ///
+ /// Add a command to the history - typically used to restore
+ /// history from a previous session.
+ ///
+ public static void AddToHistory(string command)
+ {
+ command = command.Replace("\r\n", "\n");
+ var editItems = new List {EditItemInsertString.Create(command, 0)};
+ _singleton.MaybeAddToHistory(command, editItems, 1, readingHistoryFile: false, fromDifferentSession: false);
+ }
+
+ ///
+ /// Clears history in PSReadline. This does not affect PowerShell history.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void ClearHistory(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ if (_singleton._history != null)
+ {
+ _singleton._history.Clear();
+ }
+ _singleton._currentHistoryIndex = 0;
+ }
+
+ private void UpdateFromHistory(bool moveCursor)
+ {
+ string line;
+ if (_currentHistoryIndex == _history.Count)
+ {
+ line = _savedCurrentLine._line;
+ _edits = _savedCurrentLine._edits;
+ _undoEditIndex = _savedCurrentLine._undoEditIndex;
+ }
+ else
+ {
+ line = _history[_currentHistoryIndex]._line;
+ _edits = new List(_history[_currentHistoryIndex]._edits);
+ _undoEditIndex = _history[_currentHistoryIndex]._undoEditIndex;
+ }
+ _buffer.Clear();
+ _buffer.Append(line);
+ if (moveCursor)
+ {
+ _current = _options.EditMode == EditMode.Vi ? 0 : Math.Max(0, _buffer.Length + ViEndOfLineFactor);
+ }
+ else if (_current > _buffer.Length)
+ {
+ _current = Math.Max(0, _buffer.Length + ViEndOfLineFactor);
+ }
+ Render();
+ }
+
+ private void SaveCurrentLine()
+ {
+ // We're called before any history operation - so it's convenient
+ // to check if we need to load history from another sessions now.
+ MaybeReadHistoryFile();
+
+ if (_savedCurrentLine._line == null)
+ {
+ _savedCurrentLine._line = _buffer.ToString();
+ _savedCurrentLine._edits = _edits;
+ _savedCurrentLine._undoEditIndex = _undoEditIndex;
+ }
+ }
+
+ private void HistoryRecall(int direction)
+ {
+ if (_recallHistoryCommandCount == 0 && LineIsMultiLine())
+ {
+ MoveToLine(direction);
+ return;
+ }
+
+ if (Options.HistoryNoDuplicates && _recallHistoryCommandCount == 0)
+ {
+ _hashedHistory = new Dictionary();
+ }
+
+ int count = Math.Abs(direction);
+ direction = direction < 0 ? -1 : +1;
+ int newHistoryIndex = _currentHistoryIndex;
+ while (count > 0)
+ {
+ newHistoryIndex += direction;
+ if (newHistoryIndex < 0 || newHistoryIndex >= _history.Count)
+ {
+ break;
+ }
+
+ if (_history[newHistoryIndex]._fromDifferentLiveSession)
+ {
+ continue;
+ }
+
+ if (Options.HistoryNoDuplicates)
+ {
+ var line = _history[newHistoryIndex]._line;
+ int index;
+ if (!_hashedHistory.TryGetValue(line, out index))
+ {
+ _hashedHistory.Add(line, newHistoryIndex);
+ --count;
+ }
+ else if (newHistoryIndex == index)
+ {
+ --count;
+ }
+ }
+ else
+ {
+ --count;
+ }
+ }
+ _recallHistoryCommandCount += 1;
+ if (newHistoryIndex >= 0 && newHistoryIndex <= _history.Count)
+ {
+ _currentHistoryIndex = newHistoryIndex;
+ UpdateFromHistory(moveCursor: true);
+ }
+ }
+
+ ///
+ /// Replace the current input with the 'previous' item from PSReadline history.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void PreviousHistory(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ int numericArg;
+ TryGetArgAsInt(arg, out numericArg, -1);
+ if (numericArg > 0)
+ {
+ numericArg = -numericArg;
+ }
+
+ _singleton.SaveCurrentLine();
+ _singleton.HistoryRecall(numericArg);
+ }
+
+ ///
+ /// Replace the current input with the 'next' item from PSReadline history.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void NextHistory(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ int numericArg;
+ TryGetArgAsInt(arg, out numericArg, +1);
+
+ _singleton.SaveCurrentLine();
+ _singleton.HistoryRecall(numericArg);
+ }
+
+ private void HistorySearch(int direction)
+ {
+ if (_searchHistoryCommandCount == 0)
+ {
+ if (LineIsMultiLine())
+ {
+ MoveToLine(direction);
+ return;
+ }
+
+ _searchHistoryPrefix = _buffer.ToString(0, _current);
+ _emphasisStart = 0;
+ _emphasisLength = _current;
+ if (Options.HistoryNoDuplicates)
+ {
+ _hashedHistory = new Dictionary();
+ }
+ }
+ _searchHistoryCommandCount += 1;
+
+ int count = Math.Abs(direction);
+ direction = direction < 0 ? -1 : +1;
+ int newHistoryIndex = _currentHistoryIndex;
+ while (count > 0)
+ {
+ newHistoryIndex += direction;
+ if (newHistoryIndex < 0 || newHistoryIndex >= _history.Count)
+ {
+ break;
+ }
+
+ if (_history[newHistoryIndex]._fromDifferentLiveSession && _searchHistoryPrefix.Length == 0)
+ {
+ continue;
+ }
+
+ var line = newHistoryIndex == _history.Count ? _savedCurrentLine._line : _history[newHistoryIndex]._line;
+ if (line.StartsWith(_searchHistoryPrefix, Options.HistoryStringComparison))
+ {
+ if (Options.HistoryNoDuplicates)
+ {
+ int index;
+ if (!_hashedHistory.TryGetValue(line, out index))
+ {
+ _hashedHistory.Add(line, newHistoryIndex);
+ --count;
+ }
+ else if (index == newHistoryIndex)
+ {
+ --count;
+ }
+ }
+ else
+ {
+ --count;
+ }
+ }
+ }
+
+ if (newHistoryIndex >= 0 && newHistoryIndex <= _history.Count)
+ {
+ _currentHistoryIndex = newHistoryIndex;
+ UpdateFromHistory(moveCursor: true);
+ }
+ }
+
+ ///
+ /// Move to the first item in the history.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void BeginningOfHistory(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ _singleton.SaveCurrentLine();
+ _singleton._currentHistoryIndex = 0;
+ _singleton.UpdateFromHistory(moveCursor: true);
+ }
+
+ ///
+ /// Move to the last item (the current input) in the history.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void EndOfHistory(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ _singleton._currentHistoryIndex = _singleton._history.Count;
+ _singleton.UpdateFromHistory(moveCursor: true);
+ }
+
+ ///
+ /// Replace the current input with the 'previous' item from PSReadline history
+ /// that matches the characters between the start and the input and the cursor.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void HistorySearchBackward(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ int numericArg;
+ TryGetArgAsInt(arg, out numericArg, -1);
+ if (numericArg > 0)
+ {
+ numericArg = -numericArg;
+ }
+
+ _singleton.SaveCurrentLine();
+ _singleton.HistorySearch(numericArg);
+ }
+
+ ///
+ /// Replace the current input with the 'next' item from PSReadline history
+ /// that matches the characters between the start and the input and the cursor.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void HistorySearchForward(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ int numericArg;
+ TryGetArgAsInt(arg, out numericArg, +1);
+
+ _singleton.SaveCurrentLine();
+ _singleton.HistorySearch(numericArg);
+ }
+
+ private void UpdateHistoryDuringInteractiveSearch(string toMatch, int direction, ref int searchFromPoint)
+ {
+ searchFromPoint += direction;
+ for (; searchFromPoint >= 0 && searchFromPoint < _history.Count; searchFromPoint += direction)
+ {
+ var line = _history[searchFromPoint]._line;
+ var startIndex = line.IndexOf(toMatch, Options.HistoryStringComparison);
+ if (startIndex >= 0)
+ {
+ if (Options.HistoryNoDuplicates)
+ {
+ int index;
+ if (!_hashedHistory.TryGetValue(line, out index))
+ {
+ _hashedHistory.Add(line, searchFromPoint);
+ }
+ else if (index != searchFromPoint)
+ {
+ continue;
+ }
+ }
+ _statusLinePrompt = direction > 0 ? _forwardISearchPrompt : _backwardISearchPrompt;
+ _current = startIndex;
+ _emphasisStart = startIndex;
+ _emphasisLength = toMatch.Length;
+ _currentHistoryIndex = searchFromPoint;
+ UpdateFromHistory(moveCursor: Options.HistorySearchCursorMovesToEnd);
+ return;
+ }
+ }
+
+ // Make sure we're never more than 1 away from being in range so if they
+ // reverse direction, the first time they reverse they are back in range.
+ if (searchFromPoint < 0)
+ searchFromPoint = -1;
+ else if (searchFromPoint >= _history.Count)
+ searchFromPoint = _history.Count;
+
+ _emphasisStart = -1;
+ _emphasisLength = 0;
+ _statusLinePrompt = direction > 0 ? _failedForwardISearchPrompt : _failedBackwardISearchPrompt;
+ Render();
+ }
+
+ private void InteractiveHistorySearchLoop(int direction)
+ {
+ var searchFromPoint = _currentHistoryIndex;
+ var searchPositions = new Stack();
+ searchPositions.Push(_currentHistoryIndex);
+
+ if (Options.HistoryNoDuplicates)
+ {
+ _hashedHistory = new Dictionary();
+ }
+
+ var toMatch = new StringBuilder(64);
+ while (true)
+ {
+ var key = ReadKey();
+ KeyHandler handler;
+ _dispatchTable.TryGetValue(key, out handler);
+ var function = handler != null ? handler.Action : null;
+ if (function == ReverseSearchHistory)
+ {
+ UpdateHistoryDuringInteractiveSearch(toMatch.ToString(), -1, ref searchFromPoint);
+ }
+ else if (function == ForwardSearchHistory)
+ {
+ UpdateHistoryDuringInteractiveSearch(toMatch.ToString(), +1, ref searchFromPoint);
+ }
+ else if (function == BackwardDeleteChar || key == Keys.Backspace || key == Keys.CtrlH)
+ {
+ if (toMatch.Length > 0)
+ {
+ toMatch.Remove(toMatch.Length - 1, 1);
+ _statusBuffer.Remove(_statusBuffer.Length - 2, 1);
+ searchPositions.Pop();
+ searchFromPoint = _currentHistoryIndex = searchPositions.Peek();
+ UpdateFromHistory(moveCursor: Options.HistorySearchCursorMovesToEnd);
+
+ if (_hashedHistory != null)
+ {
+ // Remove any entries with index < searchFromPoint because
+ // we are starting the search from this new index - we always
+ // want to find the latest entry that matches the search string
+ foreach (var pair in _hashedHistory.ToArray())
+ {
+ if (pair.Value < searchFromPoint)
+ {
+ _hashedHistory.Remove(pair.Key);
+ }
+ }
+ }
+
+ // Prompt may need to have 'failed-' removed.
+ var toMatchStr = toMatch.ToString();
+ var startIndex = _buffer.ToString().IndexOf(toMatchStr, Options.HistoryStringComparison);
+ if (startIndex >= 0)
+ {
+ _statusLinePrompt = direction > 0 ? _forwardISearchPrompt : _backwardISearchPrompt;
+ _current = startIndex;
+ _emphasisStart = startIndex;
+ _emphasisLength = toMatch.Length;
+ Render();
+ }
+ }
+ else
+ {
+ Ding();
+ }
+ }
+ else if (key == Keys.Escape)
+ {
+ // End search
+ break;
+ }
+ else if (function == Abort)
+ {
+ // Abort search
+ EndOfHistory();
+ break;
+ }
+ else if (EndInteractiveHistorySearch(key, function))
+ {
+ PrependQueuedKeys(key);
+ break;
+ }
+ else
+ {
+ toMatch.Append(key.KeyChar);
+ _statusBuffer.Insert(_statusBuffer.Length - 1, key.KeyChar);
+
+ var toMatchStr = toMatch.ToString();
+ var startIndex = _buffer.ToString().IndexOf(toMatchStr, Options.HistoryStringComparison);
+ if (startIndex < 0)
+ {
+ UpdateHistoryDuringInteractiveSearch(toMatchStr, direction, ref searchFromPoint);
+ }
+ else
+ {
+ _current = startIndex;
+ _emphasisStart = startIndex;
+ _emphasisLength = toMatch.Length;
+ Render();
+ }
+ searchPositions.Push(_currentHistoryIndex);
+ }
+ }
+ }
+
+ private static bool EndInteractiveHistorySearch(ConsoleKeyInfo key, Action function)
+ {
+ // Keys < ' ' are control characters
+ if (key.KeyChar < ' ')
+ {
+ return true;
+ }
+
+ if ((key.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ private void InteractiveHistorySearch(int direction)
+ {
+ SaveCurrentLine();
+
+ // Add a status line that will contain the search prompt and string
+ _statusLinePrompt = direction > 0 ? _forwardISearchPrompt : _backwardISearchPrompt;
+ _statusBuffer.Append("_");
+
+ Render(); // Render prompt
+ InteractiveHistorySearchLoop(direction);
+
+ _hashedHistory = null;
+ _currentHistoryIndex = _history.Count;
+
+ _emphasisStart = -1;
+ _emphasisLength = 0;
+
+ // Remove our status line, this will render
+ ClearStatusMessage(render: true);
+ }
+
+ ///
+ /// Perform an incremental forward search through history
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void ForwardSearchHistory(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ _singleton.InteractiveHistorySearch(+1);
+ }
+
+ ///
+ /// Perform an incremental backward search through history
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void ReverseSearchHistory(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ _singleton.InteractiveHistorySearch(-1);
+ }
+ }
+}
diff --git a/src/Microsoft.PowerShell.PSReadLine/HistoryQueue.cs b/src/Microsoft.PowerShell.PSReadLine/HistoryQueue.cs
new file mode 100644
index 00000000000..ea6e86d485b
--- /dev/null
+++ b/src/Microsoft.PowerShell.PSReadLine/HistoryQueue.cs
@@ -0,0 +1,132 @@
+/********************************************************************++
+Copyright (c) Microsoft Corporation. All rights reserved.
+--********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+
+namespace Microsoft.PowerShell
+{
+ [ExcludeFromCodeCoverage]
+ internal sealed class QueueDebugView
+ {
+ private readonly HistoryQueue _queue;
+
+ [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
+ public T[] Items
+ {
+ get { return this._queue.ToArray(); }
+ }
+
+ public QueueDebugView(HistoryQueue queue)
+ {
+ if (queue == null)
+ throw new ArgumentNullException("queue");
+ this._queue = queue;
+ }
+ }
+
+ [DebuggerDisplay("Count = {Count}")]
+ [DebuggerTypeProxy(typeof(QueueDebugView<>))]
+ internal class HistoryQueue
+ {
+ private readonly T[] _array;
+ private int _head;
+ private int _tail;
+
+ public HistoryQueue(int capacity)
+ {
+ Debug.Assert(capacity > 0);
+ _array = new T[capacity];
+ _head = _tail = Count = 0;
+ }
+
+ public void Clear()
+ {
+ for (int i = 0; i < Count; i++)
+ {
+ this[i] = default(T);
+ }
+ _head = _tail = Count = 0;
+ }
+
+ public bool Contains(T item)
+ {
+ return IndexOf(item) != -1;
+ }
+
+ public int Count { get; private set; }
+
+ public int IndexOf(T item)
+ {
+ // REVIEW: should we use case insensitive here?
+ var eqComparer = EqualityComparer.Default;
+ for (int i = 0; i < Count; i++)
+ {
+ if (eqComparer.Equals(this[i], item))
+ {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ public void Enqueue(T item)
+ {
+ if (Count == _array.Length)
+ {
+ Dequeue();
+ }
+ _array[_tail] = item;
+ _tail = (_tail + 1) % _array.Length;
+ Count += 1;
+ }
+
+ public T Dequeue()
+ {
+ Debug.Assert(Count > 0);
+
+ T obj = _array[_head];
+ _array[_head] = default(T);
+ _head = (_head + 1) % _array.Length;
+ Count -= 1;
+ return obj;
+ }
+
+ public T[] ToArray()
+ {
+ var result = new T[Count];
+ if (Count > 0)
+ {
+ if (_head < _tail)
+ {
+ Array.Copy(_array, _head, result, 0, Count);
+ }
+ else
+ {
+ Array.Copy(_array, _head, result, 0, _array.Length - _head);
+ Array.Copy(_array, 0, result, _array.Length - _head, _tail);
+ }
+ }
+ return result;
+ }
+
+ [ExcludeFromCodeCoverage]
+ public T this[int index]
+ {
+ get
+ {
+ Debug.Assert(index >= 0 && index < Count);
+ return _array[(_head + index) % _array.Length];
+ }
+ set
+ {
+ Debug.Assert(index >= 0 && index < Count);
+ _array[(_head + index) % _array.Length] = value;
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.PowerShell.PSReadLine/KeyBindings.cs b/src/Microsoft.PowerShell.PSReadLine/KeyBindings.cs
new file mode 100644
index 00000000000..05f55382095
--- /dev/null
+++ b/src/Microsoft.PowerShell.PSReadLine/KeyBindings.cs
@@ -0,0 +1,423 @@
+/********************************************************************++
+Copyright (c) Microsoft Corporation. All rights reserved.
+--********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+using System.Management.Automation;
+using System.Text;
+using Microsoft.PowerShell.Internal;
+
+namespace Microsoft.PowerShell
+{
+ ///
+ /// The class is used as the output type for the cmdlet Get-PSReadlineKeyHandler
+ ///
+ public class KeyHandler
+ {
+ ///
+ /// The key that is bound or unbound.
+ ///
+ public string Key { get; set; }
+
+ ///
+ /// The name of the function that a key is bound to, if any.
+ ///
+ public string Function { get; set; }
+
+ ///
+ /// A short description of the behavior of the function.
+ ///
+ public string Description
+ {
+ get
+ {
+ var result = _description;
+ if (string.IsNullOrWhiteSpace(result))
+ result = PSReadLineResources.ResourceManager.GetString(Function + "Description");
+ if (string.IsNullOrWhiteSpace(result))
+ result = Function;
+ return result;
+ }
+ set { _description = value; }
+ }
+ private string _description;
+ }
+
+ public partial class PSConsoleReadLine
+ {
+ class KeyHandler
+ {
+ // Each key handler will be passed 2 arguments. Most will ignore these arguments,
+ // but having a consistent signature greatly simplifies dispatch. Defaults
+ // should be included on all handlers that ignore their parameters so they
+ // can be called from PowerShell without passing anything.
+ //
+ // The first argument is the key that caused the action to be called
+ // (the second key when it's a 2 key chord). The default is null (it's nullable)
+ // because PowerShell can't handle default(ConsoleKeyInfo) as a default.
+ // Most actions will ignore this argument.
+ //
+ // The second argument is an arbitrary object. It will usually be either a number
+ // (e.g. as a repeat count) or a string. Most actions will ignore this argument.
+ public Action Action;
+ public string BriefDescription;
+ public string LongDescription
+ {
+ get
+ {
+ return _longDescription ??
+ (_longDescription =
+ PSReadLineResources.ResourceManager.GetString(BriefDescription + "Description"));
+ }
+ set { _longDescription = value; }
+ }
+ private string _longDescription;
+ public ScriptBlock ScriptBlock;
+ }
+
+ internal class ConsoleKeyInfoComparer : IEqualityComparer
+ {
+ public bool Equals(ConsoleKeyInfo x, ConsoleKeyInfo y)
+ {
+ // We *must not* compare the KeyChar field as its value is platform-dependent.
+ // We compare exactly the ConsoleKey enum field (which is platform-agnostic)
+ // and the modifiers.
+
+ return x.Key == y.Key && x.Modifiers == y.Modifiers;
+ }
+
+ public int GetHashCode(ConsoleKeyInfo obj)
+ {
+ // Because a comparison of two ConsoleKeyInfo objects is a comparison of the
+ // combination of the ConsoleKey and Modifiers, we must combine their hashes.
+ // Note that if the ConsoleKey is default, we must fall back to the KeyChar,
+ // otherwise every non-special key will compare as the same.
+ int h1 = obj.Key == default(ConsoleKey)
+ ? obj.KeyChar.GetHashCode()
+ : obj.Key.GetHashCode();
+ int h2 = obj.Modifiers.GetHashCode();
+ // This is based on Tuple.GetHashCode
+ return unchecked(((h1 << 5) + h1) ^ h2);
+ }
+ }
+
+ static KeyHandler MakeKeyHandler(Action action, string briefDescription, string longDescription = null, ScriptBlock scriptBlock = null)
+ {
+ return new KeyHandler
+ {
+ Action = action,
+ BriefDescription = briefDescription,
+ LongDescription = longDescription,
+ ScriptBlock = scriptBlock,
+ };
+ }
+
+ private Dictionary _dispatchTable;
+ private Dictionary> _chordDispatchTable;
+
+ ///
+ /// Helper to set bindings based on EditMode
+ ///
+ void SetDefaultBindings(EditMode editMode)
+ {
+ switch (editMode)
+ {
+ case EditMode.Emacs:
+ SetDefaultEmacsBindings();
+ break;
+ case EditMode.Vi:
+ SetDefaultViBindings();
+ break;
+ case EditMode.Windows:
+ SetDefaultWindowsBindings();
+ break;
+ }
+ }
+
+ void SetDefaultWindowsBindings()
+ {
+ _dispatchTable = new Dictionary(new ConsoleKeyInfoComparer())
+ {
+ { Keys.Enter, MakeKeyHandler(AcceptLine, "AcceptLine") },
+ { Keys.ShiftEnter, MakeKeyHandler(AddLine, "AddLine") },
+ { Keys.CtrlEnter, MakeKeyHandler(InsertLineAbove, "InsertLineAbove") },
+ { Keys.CtrlShiftEnter, MakeKeyHandler(InsertLineBelow, "InsertLineBelow") },
+ { Keys.Escape, MakeKeyHandler(RevertLine, "RevertLine") },
+ { Keys.LeftArrow, MakeKeyHandler(BackwardChar, "BackwardChar") },
+ { Keys.RightArrow, MakeKeyHandler(ForwardChar, "ForwardChar") },
+ { Keys.CtrlLeftArrow, MakeKeyHandler(BackwardWord, "BackwardWord") },
+ { Keys.CtrlRightArrow, MakeKeyHandler(NextWord, "NextWord") },
+ { Keys.ShiftLeftArrow, MakeKeyHandler(SelectBackwardChar, "SelectBackwardChar") },
+ { Keys.ShiftRightArrow, MakeKeyHandler(SelectForwardChar, "SelectForwardChar") },
+ { Keys.ShiftCtrlLeftArrow, MakeKeyHandler(SelectBackwardWord, "SelectBackwardWord") },
+ { Keys.ShiftCtrlRightArrow, MakeKeyHandler(SelectNextWord, "SelectNextWord") },
+ { Keys.UpArrow, MakeKeyHandler(PreviousHistory, "PreviousHistory") },
+ { Keys.DownArrow, MakeKeyHandler(NextHistory, "NextHistory") },
+ { Keys.Home, MakeKeyHandler(BeginningOfLine, "BeginningOfLine") },
+ { Keys.End, MakeKeyHandler(EndOfLine, "EndOfLine") },
+ { Keys.ShiftHome, MakeKeyHandler(SelectBackwardsLine, "SelectBackwardsLine") },
+ { Keys.ShiftEnd, MakeKeyHandler(SelectLine, "SelectLine") },
+ { Keys.Delete, MakeKeyHandler(DeleteChar, "DeleteChar") },
+ { Keys.Backspace, MakeKeyHandler(BackwardDeleteChar, "BackwardDeleteChar") },
+ { Keys.CtrlSpace, MakeKeyHandler(MenuComplete, "MenuComplete") },
+ { Keys.Tab, MakeKeyHandler(TabCompleteNext, "TabCompleteNext") },
+ { Keys.ShiftTab, MakeKeyHandler(TabCompletePrevious, "TabCompletePrevious") },
+#if !UNIX
+ { Keys.VolumeDown, MakeKeyHandler(Ignore, "Ignore") },
+ { Keys.VolumeUp, MakeKeyHandler(Ignore, "Ignore") },
+ { Keys.VolumeMute, MakeKeyHandler(Ignore, "Ignore") },
+#endif
+ { Keys.CtrlA, MakeKeyHandler(SelectAll, "SelectAll") },
+ { Keys.CtrlC, MakeKeyHandler(CopyOrCancelLine, "CopyOrCancelLine") },
+ { Keys.CtrlShiftC, MakeKeyHandler(Copy, "Copy") },
+ { Keys.CtrlL, MakeKeyHandler(ClearScreen, "ClearScreen") },
+ { Keys.CtrlR, MakeKeyHandler(ReverseSearchHistory, "ReverseSearchHistory") },
+ { Keys.CtrlS, MakeKeyHandler(ForwardSearchHistory, "ForwardSearchHistory") },
+ { Keys.CtrlV, MakeKeyHandler(Paste, "Paste") },
+ { Keys.CtrlX, MakeKeyHandler(Cut, "Cut") },
+ { Keys.CtrlY, MakeKeyHandler(Redo, "Redo") },
+ { Keys.CtrlZ, MakeKeyHandler(Undo, "Undo") },
+ { Keys.CtrlBackspace, MakeKeyHandler(BackwardKillWord, "BackwardKillWord") },
+ { Keys.CtrlDelete, MakeKeyHandler(KillWord, "KillWord") },
+ { Keys.CtrlEnd, MakeKeyHandler(ForwardDeleteLine, "ForwardDeleteLine") },
+ { Keys.CtrlHome, MakeKeyHandler(BackwardDeleteLine, "BackwardDeleteLine") },
+ { Keys.CtrlRBracket, MakeKeyHandler(GotoBrace, "GotoBrace") },
+ { Keys.CtrlAltQuestion, MakeKeyHandler(ShowKeyBindings, "ShowKeyBindings") },
+ { Keys.AltPeriod, MakeKeyHandler(YankLastArg, "YankLastArg") },
+ { Keys.Alt0, MakeKeyHandler(DigitArgument, "DigitArgument") },
+ { Keys.Alt1, MakeKeyHandler(DigitArgument, "DigitArgument") },
+ { Keys.Alt2, MakeKeyHandler(DigitArgument, "DigitArgument") },
+ { Keys.Alt3, MakeKeyHandler(DigitArgument, "DigitArgument") },
+ { Keys.Alt4, MakeKeyHandler(DigitArgument, "DigitArgument") },
+ { Keys.Alt5, MakeKeyHandler(DigitArgument, "DigitArgument") },
+ { Keys.Alt6, MakeKeyHandler(DigitArgument, "DigitArgument") },
+ { Keys.Alt7, MakeKeyHandler(DigitArgument, "DigitArgument") },
+ { Keys.Alt8, MakeKeyHandler(DigitArgument, "DigitArgument") },
+ { Keys.Alt9, MakeKeyHandler(DigitArgument, "DigitArgument") },
+ { Keys.AltMinus, MakeKeyHandler(DigitArgument, "DigitArgument") },
+ { Keys.AltQuestion, MakeKeyHandler(WhatIsKey, "WhatIsKey") },
+ { Keys.AltF7, MakeKeyHandler(ClearHistory, "ClearHistory") },
+ { Keys.F3, MakeKeyHandler(CharacterSearch, "CharacterSearch") },
+ { Keys.ShiftF3, MakeKeyHandler(CharacterSearchBackward, "CharacterSearchBackward") },
+ { Keys.F8, MakeKeyHandler(HistorySearchBackward, "HistorySearchBackward") },
+ { Keys.ShiftF8, MakeKeyHandler(HistorySearchForward, "HistorySearchForward") },
+#if !UNIX
+ { Keys.PageUp, MakeKeyHandler(ScrollDisplayUp, "ScrollDisplayUp") },
+ { Keys.PageDown, MakeKeyHandler(ScrollDisplayDown, "ScrollDisplayDown") },
+ { Keys.CtrlPageUp, MakeKeyHandler(ScrollDisplayUpLine, "ScrollDisplayUpLine") },
+ { Keys.CtrlPageDown, MakeKeyHandler(ScrollDisplayDownLine, "ScrollDisplayDownLine") },
+#endif
+ };
+
+ _chordDispatchTable = new Dictionary>();
+ }
+
+ void SetDefaultEmacsBindings()
+ {
+ _dispatchTable = new Dictionary(new ConsoleKeyInfoComparer())
+ {
+ { Keys.Backspace, MakeKeyHandler(BackwardDeleteChar, "BackwardDeleteChar") },
+ { Keys.Enter, MakeKeyHandler(AcceptLine, "AcceptLine") },
+ { Keys.ShiftEnter, MakeKeyHandler(AddLine, "AddLine") },
+ { Keys.LeftArrow, MakeKeyHandler(BackwardChar, "BackwardChar") },
+ { Keys.RightArrow, MakeKeyHandler(ForwardChar, "ForwardChar") },
+ { Keys.ShiftLeftArrow, MakeKeyHandler(SelectBackwardChar, "SelectBackwardChar") },
+ { Keys.ShiftRightArrow, MakeKeyHandler(SelectForwardChar, "SelectForwardChar") },
+ { Keys.UpArrow, MakeKeyHandler(PreviousHistory, "PreviousHistory") },
+ { Keys.DownArrow, MakeKeyHandler(NextHistory, "NextHistory") },
+ { Keys.AltLess, MakeKeyHandler(BeginningOfHistory, "BeginningOfHistory") },
+ { Keys.AltGreater, MakeKeyHandler(EndOfHistory, "EndOfHistory") },
+ { Keys.Home, MakeKeyHandler(BeginningOfLine, "BeginningOfLine") },
+ { Keys.End, MakeKeyHandler(EndOfLine, "EndOfLine") },
+ { Keys.ShiftHome, MakeKeyHandler(SelectBackwardsLine, "SelectBackwardsLine") },
+ { Keys.ShiftEnd, MakeKeyHandler(SelectLine, "SelectLine") },
+ { Keys.Escape, MakeKeyHandler(Chord, "ChordFirstKey") },
+ { Keys.Delete, MakeKeyHandler(DeleteChar, "DeleteChar") },
+ { Keys.Tab, MakeKeyHandler(Complete, "Complete") },
+ { Keys.CtrlA, MakeKeyHandler(BeginningOfLine, "BeginningOfLine") },
+ { Keys.CtrlB, MakeKeyHandler(BackwardChar, "BackwardChar") },
+ { Keys.CtrlC, MakeKeyHandler(CopyOrCancelLine, "CopyOrCancelLine") },
+ { Keys.CtrlD, MakeKeyHandler(DeleteCharOrExit, "DeleteCharOrExit") },
+ { Keys.CtrlE, MakeKeyHandler(EndOfLine, "EndOfLine") },
+ { Keys.CtrlF, MakeKeyHandler(ForwardChar, "ForwardChar") },
+ { Keys.CtrlG, MakeKeyHandler(Abort, "Abort") },
+ { Keys.CtrlH, MakeKeyHandler(BackwardDeleteChar, "BackwardDeleteChar") },
+ { Keys.CtrlL, MakeKeyHandler(ClearScreen, "ClearScreen") },
+ { Keys.CtrlK, MakeKeyHandler(KillLine, "KillLine") },
+ { Keys.CtrlM, MakeKeyHandler(ValidateAndAcceptLine,"ValidateAndAcceptLine") },
+ { Keys.CtrlN, MakeKeyHandler(NextHistory, "NextHistory") },
+ { Keys.CtrlO, MakeKeyHandler(AcceptAndGetNext, "AcceptAndGetNext") },
+ { Keys.CtrlP, MakeKeyHandler(PreviousHistory, "PreviousHistory") },
+ { Keys.CtrlR, MakeKeyHandler(ReverseSearchHistory, "ReverseSearchHistory") },
+ { Keys.CtrlS, MakeKeyHandler(ForwardSearchHistory, "ForwardSearchHistory") },
+ { Keys.CtrlU, MakeKeyHandler(BackwardKillLine, "BackwardKillLine") },
+ { Keys.CtrlX, MakeKeyHandler(Chord, "ChordFirstKey") },
+ { Keys.CtrlW, MakeKeyHandler(UnixWordRubout, "UnixWordRubout") },
+ { Keys.CtrlY, MakeKeyHandler(Yank, "Yank") },
+ { Keys.CtrlAt, MakeKeyHandler(SetMark, "SetMark") },
+ { Keys.CtrlUnderbar, MakeKeyHandler(Undo, "Undo") },
+ { Keys.CtrlRBracket, MakeKeyHandler(CharacterSearch, "CharacterSearch") },
+ { Keys.AltCtrlRBracket, MakeKeyHandler(CharacterSearchBackward,"CharacterSearchBackward") },
+ { Keys.Alt0, MakeKeyHandler(DigitArgument, "DigitArgument") },
+ { Keys.Alt1, MakeKeyHandler(DigitArgument, "DigitArgument") },
+ { Keys.Alt2, MakeKeyHandler(DigitArgument, "DigitArgument") },
+ { Keys.Alt3, MakeKeyHandler(DigitArgument, "DigitArgument") },
+ { Keys.Alt4, MakeKeyHandler(DigitArgument, "DigitArgument") },
+ { Keys.Alt5, MakeKeyHandler(DigitArgument, "DigitArgument") },
+ { Keys.Alt6, MakeKeyHandler(DigitArgument, "DigitArgument") },
+ { Keys.Alt7, MakeKeyHandler(DigitArgument, "DigitArgument") },
+ { Keys.Alt8, MakeKeyHandler(DigitArgument, "DigitArgument") },
+ { Keys.Alt9, MakeKeyHandler(DigitArgument, "DigitArgument") },
+ { Keys.AltMinus, MakeKeyHandler(DigitArgument, "DigitArgument") },
+ { Keys.AltB, MakeKeyHandler(BackwardWord, "BackwardWord") },
+ { Keys.AltShiftB, MakeKeyHandler(SelectBackwardWord, "SelectBackwardWord") },
+ { Keys.AltD, MakeKeyHandler(KillWord, "KillWord") },
+ { Keys.AltF, MakeKeyHandler(ForwardWord, "ForwardWord") },
+ { Keys.AltShiftF, MakeKeyHandler(SelectForwardWord, "SelectForwardWord") },
+ { Keys.AltR, MakeKeyHandler(RevertLine, "RevertLine") },
+ { Keys.AltY, MakeKeyHandler(YankPop, "YankPop") },
+ { Keys.AltBackspace, MakeKeyHandler(BackwardKillWord, "BackwardKillWord") },
+ { Keys.AltEquals, MakeKeyHandler(PossibleCompletions, "PossibleCompletions") },
+ { Keys.CtrlSpace, MakeKeyHandler(MenuComplete, "MenuComplete") },
+ { Keys.CtrlAltQuestion, MakeKeyHandler(ShowKeyBindings, "ShowKeyBindings") },
+ { Keys.AltQuestion, MakeKeyHandler(WhatIsKey, "WhatIsKey") },
+ { Keys.AltSpace, MakeKeyHandler(SetMark, "SetMark") }, // useless entry here for completeness - brings up system menu on Windows
+ { Keys.AltPeriod, MakeKeyHandler(YankLastArg, "YankLastArg") },
+ { Keys.AltUnderbar, MakeKeyHandler(YankLastArg, "YankLastArg") },
+ { Keys.AltCtrlY, MakeKeyHandler(YankNthArg, "YankNthArg") },
+#if !UNIX
+ { Keys.VolumeDown, MakeKeyHandler(Ignore, "Ignore") },
+ { Keys.VolumeUp, MakeKeyHandler(Ignore, "Ignore") },
+ { Keys.VolumeMute, MakeKeyHandler(Ignore, "Ignore") },
+ { Keys.PageUp, MakeKeyHandler(ScrollDisplayUp, "ScrollDisplayUp") },
+ { Keys.CtrlPageUp, MakeKeyHandler(ScrollDisplayUpLine, "ScrollDisplayUpLine") },
+ { Keys.PageDown, MakeKeyHandler(ScrollDisplayDown, "ScrollDisplayDown") },
+ { Keys.CtrlPageDown, MakeKeyHandler(ScrollDisplayDownLine,"ScrollDisplayDownLine") },
+ { Keys.CtrlHome, MakeKeyHandler(ScrollDisplayTop, "ScrollDisplayTop") },
+ { Keys.CtrlEnd, MakeKeyHandler(ScrollDisplayToCursor,"ScrollDisplayToCursor") },
+#endif
+ };
+
+ _chordDispatchTable = new Dictionary>();
+
+ // Escape, table (meta key)
+ _chordDispatchTable[Keys.Escape] = new Dictionary(new ConsoleKeyInfoComparer())
+ {
+ { Keys.B, MakeKeyHandler(BackwardWord, "BackwardWord") },
+ { Keys.D, MakeKeyHandler(KillWord, "KillWord") },
+ { Keys.F, MakeKeyHandler(ForwardWord, "ForwardWord") },
+ { Keys.R, MakeKeyHandler(RevertLine, "RevertLine") },
+ { Keys.Y, MakeKeyHandler(YankPop, "YankPop") },
+ { Keys.CtrlY, MakeKeyHandler(YankNthArg, "YankNthArg") },
+ { Keys.Backspace, MakeKeyHandler(BackwardKillWord, "BackwardKillWord") },
+ { Keys.Period, MakeKeyHandler(YankLastArg, "YankLastArg") },
+ { Keys.Underbar, MakeKeyHandler(YankLastArg, "YankLastArg") },
+ };
+
+ // Ctrl+X, table
+ _chordDispatchTable[Keys.CtrlX] = new Dictionary(new ConsoleKeyInfoComparer())
+ {
+ { Keys.Backspace, MakeKeyHandler(BackwardKillLine, "BackwardKillLine") },
+ { Keys.CtrlU, MakeKeyHandler(Undo, "Undo") },
+ { Keys.CtrlX, MakeKeyHandler(ExchangePointAndMark, "ExchangePointAndMark") },
+ };
+ }
+
+ ///
+ /// Show all bound keys
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void ShowKeyBindings(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ var buffer = new StringBuilder();
+ buffer.AppendFormat(CultureInfo.InvariantCulture, "{0,-20} {1,-24} {2}\n", "Key", "Function", "Description");
+ buffer.AppendFormat(CultureInfo.InvariantCulture, "{0,-20} {1,-24} {2}\n", "---", "--------", "-----------");
+ var boundKeys = GetKeyHandlers(includeBound: true, includeUnbound: false);
+ var console = _singleton._console;
+ var maxDescriptionLength = console.WindowWidth - 20 - 24 - 2;
+ foreach (var boundKey in boundKeys)
+ {
+ var description = boundKey.Description;
+ var newline = "\n";
+ if (description.Length >= maxDescriptionLength)
+ {
+ description = description.Substring(0, maxDescriptionLength - 3) + "...";
+ newline = "";
+ }
+ buffer.AppendFormat(CultureInfo.InvariantCulture, "{0,-20} {1,-24} {2}{3}", boundKey.Key, boundKey.Function, description, newline);
+ }
+
+ // Don't overwrite any of the line - so move to first line after the end of our buffer.
+ var coords = _singleton.ConvertOffsetToCoordinates(_singleton._buffer.Length);
+ var y = coords.Y + 1;
+ _singleton.PlaceCursor(0, ref y);
+
+ console.WriteLine(buffer.ToString());
+ _singleton._initialY = console.CursorTop;
+ _singleton.Render();
+ }
+
+ ///
+ /// Read a key and tell me what the key is bound to.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void WhatIsKey(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ _singleton._statusLinePrompt = "what-is-key: ";
+ _singleton.Render();
+ var toLookup = ReadKey();
+ KeyHandler keyHandler;
+ var buffer = new StringBuilder();
+ _singleton._dispatchTable.TryGetValue(toLookup, out keyHandler);
+ buffer.Append(toLookup.ToGestureString());
+ if (keyHandler != null)
+ {
+ if (keyHandler.BriefDescription == "ChordFirstKey")
+ {
+ Dictionary secondKeyDispatchTable;
+ if (_singleton._chordDispatchTable.TryGetValue(toLookup, out secondKeyDispatchTable))
+ {
+ toLookup = ReadKey();
+ secondKeyDispatchTable.TryGetValue(toLookup, out keyHandler);
+ buffer.Append(",");
+ buffer.Append(toLookup.ToGestureString());
+ }
+ }
+ }
+ buffer.Append(": ");
+ if (keyHandler != null)
+ {
+ buffer.Append(keyHandler.BriefDescription);
+ if (!string.IsNullOrWhiteSpace(keyHandler.LongDescription))
+ {
+ buffer.Append(" - ");
+ buffer.Append(keyHandler.LongDescription);
+ }
+ }
+ else if (toLookup.KeyChar != 0)
+ {
+ buffer.Append("SelfInsert");
+ buffer.Append(" - ");
+ buffer.Append(PSReadLineResources.SelfInsertDescription);
+ }
+ else
+ {
+ buffer.Append(PSReadLineResources.KeyIsUnbound);
+ }
+
+ _singleton.ClearStatusMessage(render: false);
+
+ // Don't overwrite any of the line - so move to first line after the end of our buffer.
+ var coords = _singleton.ConvertOffsetToCoordinates(_singleton._buffer.Length);
+ var y = coords.Y + 1;
+ _singleton.PlaceCursor(0, ref y);
+
+ _singleton._console.WriteLine(buffer.ToString());
+ _singleton._initialY = _singleton._console.CursorTop;
+ _singleton.Render();
+ }
+ }
+}
diff --git a/src/Microsoft.PowerShell.PSReadLine/KeyBindings.vi.cs b/src/Microsoft.PowerShell.PSReadLine/KeyBindings.vi.cs
new file mode 100644
index 00000000000..dd9f09b633a
--- /dev/null
+++ b/src/Microsoft.PowerShell.PSReadLine/KeyBindings.vi.cs
@@ -0,0 +1,284 @@
+/********************************************************************++
+Copyright (c) Microsoft Corporation. All rights reserved.
+--********************************************************************/
+
+using System;
+using System.Collections.Generic;
+
+namespace Microsoft.PowerShell
+{
+ public partial class PSConsoleReadLine
+ {
+ internal static ConsoleColor AlternateBackground(ConsoleColor bg)
+ {
+ switch (bg)
+ {
+ case ConsoleColor.Black: return ConsoleColor.DarkGray;
+ case ConsoleColor.Blue: return ConsoleColor.DarkBlue;
+ case ConsoleColor.Cyan: return ConsoleColor.DarkCyan;
+ case ConsoleColor.DarkBlue: return ConsoleColor.Black;
+ case ConsoleColor.DarkCyan: return ConsoleColor.Black;
+ case ConsoleColor.DarkGray: return ConsoleColor.Black;
+ case ConsoleColor.DarkGreen: return ConsoleColor.Black;
+ case ConsoleColor.DarkMagenta: return ConsoleColor.Black;
+ case ConsoleColor.DarkRed: return ConsoleColor.Black;
+ case ConsoleColor.DarkYellow: return ConsoleColor.Black;
+ case ConsoleColor.Gray: return ConsoleColor.White;
+ case ConsoleColor.Green: return ConsoleColor.DarkGreen;
+ case ConsoleColor.Magenta: return ConsoleColor.DarkMagenta;
+ case ConsoleColor.Red: return ConsoleColor.DarkRed;
+ case ConsoleColor.White: return ConsoleColor.Gray;
+ case ConsoleColor.Yellow: return ConsoleColor.DarkYellow;
+ default:
+ return ConsoleColor.Black;
+ }
+ }
+
+ private int _normalCursorSize = 10;
+
+ private static Dictionary _viInsKeyMap;
+ private static Dictionary _viCmdKeyMap;
+ private static Dictionary _viChordDTable;
+ private static Dictionary _viChordCTable;
+ private static Dictionary _viChordYTable;
+
+ private static Dictionary> _viCmdChordTable;
+ private static Dictionary> _viInsChordTable;
+
+ ///
+ /// Sets up the key bindings for vi operations.
+ ///
+ private void SetDefaultViBindings()
+ {
+ _viInsKeyMap = new Dictionary(new ConsoleKeyInfoComparer())
+ {
+ { Keys.Enter, MakeKeyHandler(AcceptLine, "AcceptLine" ) },
+ { Keys.CtrlD, MakeKeyHandler(ViAcceptLineOrExit, "ViAcceptLineOrExit" ) },
+ { Keys.ShiftEnter, MakeKeyHandler(AddLine, "AddLine") },
+ { Keys.Escape, MakeKeyHandler(ViCommandMode, "ViCommandMode") },
+ { Keys.LeftArrow, MakeKeyHandler(BackwardChar, "BackwardChar") },
+ { Keys.RightArrow, MakeKeyHandler(ForwardChar, "ForwardChar") },
+ { Keys.CtrlLeftArrow, MakeKeyHandler(BackwardWord, "BackwardWord") },
+ { Keys.CtrlRightArrow, MakeKeyHandler(NextWord, "NextWord") },
+ { Keys.UpArrow, MakeKeyHandler(PreviousHistory, "PreviousHistory") },
+ { Keys.DownArrow, MakeKeyHandler(NextHistory, "NextHistory") },
+ { Keys.Home, MakeKeyHandler(BeginningOfLine, "BeginningOfLine") },
+ { Keys.End, MakeKeyHandler(EndOfLine, "EndOfLine") },
+ { Keys.Delete, MakeKeyHandler(DeleteChar, "DeleteChar") },
+ { Keys.Backspace, MakeKeyHandler(BackwardDeleteChar, "BackwardDeleteChar") },
+ { Keys.CtrlSpace, MakeKeyHandler(PossibleCompletions, "PossibleCompletions") },
+ { Keys.Tab, MakeKeyHandler(ViTabCompleteNext, "ViTabCompleteNext") },
+ { Keys.ShiftTab, MakeKeyHandler(ViTabCompletePrevious, "ViTabCompletePrevious") },
+ { Keys.CtrlV, MakeKeyHandler(Paste, "Paste") },
+#if !UNIX
+ { Keys.VolumeDown, MakeKeyHandler(Ignore, "Ignore") },
+ { Keys.VolumeUp, MakeKeyHandler(Ignore, "Ignore") },
+ { Keys.VolumeMute, MakeKeyHandler(Ignore, "Ignore") },
+#endif
+ { Keys.CtrlC, MakeKeyHandler(CancelLine, "CancelLine") },
+ { Keys.CtrlL, MakeKeyHandler(ClearScreen, "ClearScreen") },
+ { Keys.CtrlY, MakeKeyHandler(Redo, "Redo") },
+ { Keys.CtrlZ, MakeKeyHandler(Undo, "Undo") },
+ { Keys.CtrlBackspace, MakeKeyHandler(BackwardKillWord, "BackwardKillWord") },
+ { Keys.CtrlDelete, MakeKeyHandler(KillWord, "KillWord") },
+ { Keys.CtrlEnd, MakeKeyHandler(ForwardDeleteLine, "ForwardDeleteLine") },
+ { Keys.CtrlHome, MakeKeyHandler(BackwardDeleteLine, "BackwardDeleteLine") },
+ { Keys.CtrlRBracket, MakeKeyHandler(GotoBrace, "GotoBrace") },
+ { Keys.F3, MakeKeyHandler(CharacterSearch, "CharacterSearch") },
+ { Keys.ShiftF3, MakeKeyHandler(CharacterSearchBackward,"CharacterSearchBackward") },
+ { Keys.CtrlAltQuestion, MakeKeyHandler(ShowKeyBindings, "ShowKeyBindings") }
+ };
+ _viCmdKeyMap = new Dictionary(new ConsoleKeyInfoComparer())
+ {
+ { Keys.Enter, MakeKeyHandler(ViAcceptLine, "ViAcceptLine") },
+ { Keys.CtrlD, MakeKeyHandler(ViAcceptLineOrExit, "ViAcceptLineOrExit") },
+ { Keys.ShiftEnter, MakeKeyHandler(AddLine, "AddLine") },
+ { Keys.Escape, MakeKeyHandler(Ding, "Ignore") },
+ { Keys.LeftArrow, MakeKeyHandler(BackwardChar, "BackwardChar") },
+ { Keys.RightArrow, MakeKeyHandler(ForwardChar, "ForwardChar") },
+ { Keys.Space, MakeKeyHandler(ForwardChar, "ForwardChar") },
+ { Keys.CtrlLeftArrow, MakeKeyHandler(BackwardWord, "BackwardWord") },
+ { Keys.CtrlRightArrow, MakeKeyHandler(NextWord, "NextWord") },
+ { Keys.UpArrow, MakeKeyHandler(PreviousHistory, "PreviousHistory") },
+ { Keys.DownArrow, MakeKeyHandler(NextHistory, "NextHistory") },
+ { Keys.Home, MakeKeyHandler(BeginningOfLine, "BeginningOfLine") },
+ { Keys.End, MakeKeyHandler(MoveToEndOfLine, "MoveToEndOfLine") },
+ { Keys.Delete, MakeKeyHandler(DeleteChar, "DeleteChar") },
+ { Keys.Backspace, MakeKeyHandler(BackwardChar, "BackwardChar") },
+ { Keys.CtrlSpace, MakeKeyHandler(PossibleCompletions, "PossibleCompletions") },
+ { Keys.Tab, MakeKeyHandler(TabCompleteNext, "TabCompleteNext") },
+ { Keys.ShiftTab, MakeKeyHandler(TabCompletePrevious, "TabCompletePrevious") },
+ { Keys.CtrlV, MakeKeyHandler(Paste, "Paste") },
+#if !UNIX
+ { Keys.VolumeDown, MakeKeyHandler(Ignore, "Ignore") },
+ { Keys.VolumeUp, MakeKeyHandler(Ignore, "Ignore") },
+ { Keys.VolumeMute, MakeKeyHandler(Ignore, "Ignore") },
+#endif
+ { Keys.CtrlC, MakeKeyHandler(CancelLine, "CancelLine") },
+ { Keys.CtrlL, MakeKeyHandler(ClearScreen, "ClearScreen") },
+ { Keys.CtrlT, MakeKeyHandler(SwapCharacters, "SwapCharacters") },
+ { Keys.CtrlU, MakeKeyHandler(BackwardDeleteLine, "BackwardDeleteLine") },
+ { Keys.CtrlW, MakeKeyHandler(BackwardDeleteWord, "BackwardDeleteWord") },
+ { Keys.CtrlY, MakeKeyHandler(Redo, "Redo") },
+ { Keys.CtrlZ, MakeKeyHandler(Undo, "Undo") },
+ { Keys.CtrlBackspace, MakeKeyHandler(BackwardKillWord, "BackwardKillWord") },
+ { Keys.CtrlDelete, MakeKeyHandler(KillWord, "KillWord") },
+ { Keys.CtrlEnd, MakeKeyHandler(ForwardDeleteLine, "ForwardDeleteLine") },
+ { Keys.CtrlHome, MakeKeyHandler(BackwardDeleteLine, "BackwardDeleteLine") },
+ { Keys.CtrlRBracket, MakeKeyHandler(GotoBrace, "GotoBrace") },
+ { Keys.F3, MakeKeyHandler(CharacterSearch, "CharacterSearch") },
+ { Keys.ShiftF3, MakeKeyHandler(CharacterSearchBackward, "CharacterSearchBackward") },
+ { Keys.A, MakeKeyHandler(ViInsertWithAppend, "ViInsertWithAppend") },
+ { Keys.B, MakeKeyHandler(ViBackwardWord, "ViBackwardWord") },
+ { Keys.C, MakeKeyHandler(ViChord, "ChordFirstKey") },
+ { Keys.D, MakeKeyHandler(ViChord, "ChordFirstKey") },
+ { Keys.E, MakeKeyHandler(NextWordEnd, "NextWordEnd") },
+ { Keys.F, MakeKeyHandler(SearchChar, "SearchChar") },
+ { Keys.G, MakeKeyHandler(Ding, "Ignore") },
+ { Keys.H, MakeKeyHandler(BackwardChar, "BackwardChar") },
+ { Keys.I, MakeKeyHandler(ViInsertMode, "ViInsertMode") },
+ { Keys.J, MakeKeyHandler(NextHistory, "NextHistory") },
+ { Keys.K, MakeKeyHandler(PreviousHistory, "PreviousHistory") },
+ { Keys.L, MakeKeyHandler(ForwardChar, "ForwardChar") },
+ { Keys.M, MakeKeyHandler(Ding, "Ignore") },
+ { Keys.N, MakeKeyHandler(RepeatSearch, "RepeatSearch") },
+ { Keys.O, MakeKeyHandler(ViAppendLine, "ViAppendLine") },
+ { Keys.P, MakeKeyHandler(PasteAfter, "PasteAfter") },
+ { Keys.Q, MakeKeyHandler(Ding, "Ignore") },
+ { Keys.R, MakeKeyHandler(ReplaceCharInPlace, "ReplaceCharInPlace") },
+ { Keys.S, MakeKeyHandler(ViInsertWithDelete, "ViInsertWithDelete") },
+ { Keys.T, MakeKeyHandler(SearchCharWithBackoff,"SearchCharWithBackoff") },
+ { Keys.U, MakeKeyHandler(Undo, "Undo") },
+ { Keys.V, MakeKeyHandler(ViEditVisually, "ViEditVisually") },
+ { Keys.W, MakeKeyHandler(ViNextWord, "ViNextWord") },
+ { Keys.X, MakeKeyHandler(DeleteChar, "DeleteChar") },
+ { Keys.Y, MakeKeyHandler(ViChord, "ChordFirstKey") },
+ { Keys.Z, MakeKeyHandler(Ding, "Ignore") },
+ { Keys.ucA, MakeKeyHandler(ViInsertAtEnd, "ViInsertAtEnd") },
+ { Keys.ucB, MakeKeyHandler(ViBackwardGlob, "ViBackwardGlob") },
+ { Keys.ucC, MakeKeyHandler(ViReplaceToEnd, "ViReplaceToEnd") },
+ { Keys.ucD, MakeKeyHandler(DeleteToEnd, "DeleteToEnd") },
+ { Keys.ucE, MakeKeyHandler(ViEndOfGlob, "ViEndOfGlob") },
+ { Keys.ucF, MakeKeyHandler(SearchCharBackward, "SearchCharBackward") },
+ { Keys.ucG, MakeKeyHandler(Ding, "Ignore") },
+ { Keys.ucH, MakeKeyHandler(Ding, "Ignore") },
+ { Keys.ucI, MakeKeyHandler(ViInsertAtBegining, "ViInsertAtBegining") },
+ { Keys.ucJ, MakeKeyHandler(ViJoinLines, "ViJoinLines") },
+ { Keys.ucK, MakeKeyHandler(Ding, "Ignore") },
+ { Keys.ucL, MakeKeyHandler(Ding, "Ignore") },
+ { Keys.ucM, MakeKeyHandler(Ding, "Ignore") },
+ { Keys.ucN, MakeKeyHandler(RepeatSearchBackward, "RepeatSearchBackward") },
+ { Keys.ucO, MakeKeyHandler(ViInsertLine, "ViInsertLine") },
+ { Keys.ucP, MakeKeyHandler(PasteBefore, "PasteBefore") },
+ { Keys.ucQ, MakeKeyHandler(Ding, "Ignore") },
+ { Keys.ucR, MakeKeyHandler(ViReplaceUntilEsc, "ViReplaceUntilEsc") },
+ { Keys.ucS, MakeKeyHandler(ViReplaceLine, "ViReplaceLine") },
+ { Keys.ucT, MakeKeyHandler(SearchCharBackwardWithBackoff, "SearchCharBackwardWithBackoff") },
+ { Keys.ucU, MakeKeyHandler(UndoAll, "UndoAll") },
+ { Keys.ucV, MakeKeyHandler(Ding, "Ignore") },
+ { Keys.ucW, MakeKeyHandler(ViNextGlob, "ViNextGlob") },
+ { Keys.ucX, MakeKeyHandler(BackwardDeleteChar, "BackwardDeleteChar") },
+ { Keys.ucY, MakeKeyHandler(Ding, "Ignore") },
+ { Keys.ucZ, MakeKeyHandler(Ding, "Ignore") },
+ { Keys._0, MakeKeyHandler(DigitArgument, "DigitArgument") },
+ { Keys._1, MakeKeyHandler(DigitArgument, "DigitArgument") },
+ { Keys._2, MakeKeyHandler(DigitArgument, "DigitArgument") },
+ { Keys._3, MakeKeyHandler(DigitArgument, "DigitArgument") },
+ { Keys._4, MakeKeyHandler(DigitArgument, "DigitArgument") },
+ { Keys._5, MakeKeyHandler(DigitArgument, "DigitArgument") },
+ { Keys._6, MakeKeyHandler(DigitArgument, "DigitArgument") },
+ { Keys._7, MakeKeyHandler(DigitArgument, "DigitArgument") },
+ { Keys._8, MakeKeyHandler(DigitArgument, "DigitArgument") },
+ { Keys._9, MakeKeyHandler(DigitArgument, "DigitArgument") },
+ { Keys.Dollar, MakeKeyHandler(MoveToEndOfLine, "MoveToEndOfLine") },
+ { Keys.Percent, MakeKeyHandler(ViGotoBrace, "ViGotoBrace") },
+ { Keys.Pound, MakeKeyHandler(PrependAndAccept, "PrependAndAccept") },
+ { Keys.Pipe, MakeKeyHandler(GotoColumn, "GotoColumn") },
+ { Keys.Uphat, MakeKeyHandler(GotoFirstNonBlankOfLine, "GotoFirstNonBlankOfLine") },
+ { Keys.Tilde, MakeKeyHandler(InvertCase, "InvertCase") },
+ { Keys.Slash, MakeKeyHandler(ViSearchHistoryBackward, "SearchBackward") },
+ { Keys.CtrlR, MakeKeyHandler(SearchCharBackward, "SearchCharBackward") },
+ { Keys.Question, MakeKeyHandler(SearchForward, "SearchForward") },
+ { Keys.CtrlS, MakeKeyHandler(SearchForward, "SearchForward") },
+ { Keys.Plus, MakeKeyHandler(NextHistory, "NextHistory") },
+ { Keys.Minus, MakeKeyHandler(PreviousHistory, "PreviousHistory") },
+ { Keys.Period, MakeKeyHandler(RepeatLastCommand, "RepeatLastCommand") },
+ { Keys.Semicolon, MakeKeyHandler(RepeatLastCharSearch, "RepeatLastCharSearch") },
+ { Keys.Comma, MakeKeyHandler(RepeatLastCharSearchBackwards, "RepeatLastCharSearchBackwards") }
+ };
+
+ _viChordDTable = new Dictionary(new ConsoleKeyInfoComparer())
+ {
+ { Keys.D, MakeKeyHandler( DeleteLine, "DeleteLine") },
+ { Keys.Dollar, MakeKeyHandler( DeleteToEnd, "DeleteToEnd") },
+ { Keys.B, MakeKeyHandler( BackwardDeleteWord, "BackwardDeleteWord") },
+ { Keys.ucB, MakeKeyHandler( ViBackwardDeleteGlob, "ViBackwardDeleteGlob") },
+ { Keys.W, MakeKeyHandler( DeleteWord, "DeleteWord") },
+ { Keys.ucW, MakeKeyHandler( ViDeleteGlob, "ViDeleteGlob") },
+ { Keys.E, MakeKeyHandler( DeleteEndOfWord, "DeleteEndOfWord") },
+ { Keys.ucE, MakeKeyHandler( ViDeleteEndOfGlob, "ViDeleteEndOfGlob") },
+ { Keys.H, MakeKeyHandler( BackwardDeleteChar, "BackwardDeleteChar") },
+ { Keys.L, MakeKeyHandler( DeleteChar, "DeleteChar") },
+ { Keys.Space, MakeKeyHandler( DeleteChar, "DeleteChar") },
+ { Keys._0, MakeKeyHandler( BackwardDeleteLine, "BackwardDeleteLine") },
+ { Keys.Uphat, MakeKeyHandler( DeleteLineToFirstChar, "DeleteLineToFirstChar") },
+ { Keys.Percent, MakeKeyHandler( ViDeleteBrace, "DeleteBrace") },
+ { Keys.F, MakeKeyHandler( ViDeleteToChar, "ViDeleteToChar") },
+ { Keys.ucF, MakeKeyHandler( ViDeleteToCharBackward, "ViDeleteToCharBackward") },
+ { Keys.T, MakeKeyHandler( ViDeleteToBeforeChar, "ViDeleteToBeforeChar") },
+ { Keys.ucT, MakeKeyHandler( ViDeleteToBeforeCharBackward, "ViDeleteToBeforeCharBackward") },
+ };
+
+ _viChordCTable = new Dictionary(new ConsoleKeyInfoComparer())
+ {
+ { Keys.C, MakeKeyHandler( ViReplaceLine, "ViReplaceLine") },
+ { Keys.Dollar, MakeKeyHandler( ViReplaceToEnd, "ViReplaceToEnd") },
+ { Keys.B, MakeKeyHandler( ViBackwardReplaceWord, "ViBackwardReplaceWord") },
+ { Keys.ucB, MakeKeyHandler( ViBackwardReplaceGlob, "ViBackwardReplaceGlob") },
+ { Keys.W, MakeKeyHandler( ViReplaceWord, "ViReplaceWord") },
+ { Keys.ucW, MakeKeyHandler( ViReplaceGlob, "ViReplaceGlob") },
+ { Keys.E, MakeKeyHandler( ViReplaceEndOfWord, "ViReplaceEndOfWord") },
+ { Keys.ucE, MakeKeyHandler( ViReplaceEndOfGlob, "ViReplaceEndOfGlob") },
+ { Keys.H, MakeKeyHandler( BackwardReplaceChar, "BackwardReplaceChar") },
+ { Keys.L, MakeKeyHandler( ReplaceChar, "ReplaceChar") },
+ { Keys.Space, MakeKeyHandler( ReplaceChar, "ReplaceChar") },
+ { Keys._0, MakeKeyHandler( ViBackwardReplaceLine, "ViBackwardReplaceLine") },
+ { Keys.Uphat, MakeKeyHandler( ViBackwardReplaceLineToFirstChar, "ViBackwardReplaceLineToFirstChar") },
+ { Keys.Percent, MakeKeyHandler( ViReplaceBrace, "ViReplaceBrace") },
+ { Keys.F, MakeKeyHandler( ViReplaceToChar, "ViReplaceToChar") },
+ { Keys.ucF, MakeKeyHandler( ViReplaceToCharBackward, "ViReplaceToCharBackward") },
+ { Keys.T, MakeKeyHandler( ViReplaceToBeforeChar, "ViReplaceToBeforeChar") },
+ { Keys.ucT, MakeKeyHandler( ViReplaceToBeforeCharBackward, "ViReplaceToBeforeCharBackward") },
+ };
+
+ _viChordYTable = new Dictionary(new ConsoleKeyInfoComparer())
+ {
+ { Keys.Y, MakeKeyHandler( ViYankLine, "ViYankLine") },
+ { Keys.Dollar, MakeKeyHandler( ViYankToEndOfLine, "ViYankToEndOfLine") },
+ { Keys.B, MakeKeyHandler( ViYankPreviousWord, "ViYankPreviousWord") },
+ { Keys.ucB, MakeKeyHandler( ViYankPreviousGlob, "ViYankPreviousGlob") },
+ { Keys.W, MakeKeyHandler( ViYankNextWord, "ViYankNextWord") },
+ { Keys.ucW, MakeKeyHandler( ViYankNextGlob, "ViYankNextGlob") },
+ { Keys.E, MakeKeyHandler( ViYankEndOfWord, "ViYankEndOfWord") },
+ { Keys.ucE, MakeKeyHandler( ViYankEndOfGlob, "ViYankEndOfGlob") },
+ { Keys.H, MakeKeyHandler( ViYankLeft, "ViYankLeft") },
+ { Keys.L, MakeKeyHandler( ViYankRight, "ViYankRight") },
+ { Keys.Space, MakeKeyHandler( ViYankRight, "ViYankRight") },
+ { Keys._0, MakeKeyHandler( ViYankBeginningOfLine, "ViYankBeginningOfLine") },
+ { Keys.Uphat, MakeKeyHandler( ViYankToFirstChar, "ViYankToFirstChar") },
+ { Keys.Percent, MakeKeyHandler( ViYankPercent, "ViYankPercent") },
+ };
+
+ _viCmdChordTable = new Dictionary>();
+ _viInsChordTable = new Dictionary>();
+
+ _dispatchTable = _viInsKeyMap;
+ _chordDispatchTable = _viInsChordTable;
+ _viCmdChordTable[Keys.D] = _viChordDTable;
+ _viCmdChordTable[Keys.C] = _viChordCTable;
+ _viCmdChordTable[Keys.Y] = _viChordYTable;
+
+ _normalCursorSize = _console.CursorSize;
+ }
+ }
+}
diff --git a/src/Microsoft.PowerShell.PSReadLine/Keys.cs b/src/Microsoft.PowerShell.PSReadLine/Keys.cs
new file mode 100644
index 00000000000..df66e6baae5
--- /dev/null
+++ b/src/Microsoft.PowerShell.PSReadLine/Keys.cs
@@ -0,0 +1,299 @@
+/********************************************************************++
+Copyright (c) Microsoft Corporation. All rights reserved.
+--********************************************************************/
+
+using System;
+
+namespace Microsoft.PowerShell
+{
+ internal static class Keys
+ {
+ public static ConsoleKeyInfo A = new ConsoleKeyInfo('a', ConsoleKey.A, false, false, false);
+ public static ConsoleKeyInfo B = new ConsoleKeyInfo('b', ConsoleKey.B, false, false, false);
+ public static ConsoleKeyInfo C = new ConsoleKeyInfo('c', ConsoleKey.C, false, false, false);
+ public static ConsoleKeyInfo D = new ConsoleKeyInfo('d', ConsoleKey.D, false, false, false);
+ public static ConsoleKeyInfo E = new ConsoleKeyInfo('e', ConsoleKey.E, false, false, false);
+ public static ConsoleKeyInfo F = new ConsoleKeyInfo('f', ConsoleKey.F, false, false, false);
+ public static ConsoleKeyInfo G = new ConsoleKeyInfo('g', ConsoleKey.G, false, false, false);
+ public static ConsoleKeyInfo H = new ConsoleKeyInfo('h', ConsoleKey.H, false, false, false);
+ public static ConsoleKeyInfo I = new ConsoleKeyInfo('i', ConsoleKey.I, false, false, false);
+ public static ConsoleKeyInfo J = new ConsoleKeyInfo('j', ConsoleKey.J, false, false, false);
+ public static ConsoleKeyInfo K = new ConsoleKeyInfo('k', ConsoleKey.K, false, false, false);
+ public static ConsoleKeyInfo L = new ConsoleKeyInfo('l', ConsoleKey.L, false, false, false);
+ public static ConsoleKeyInfo M = new ConsoleKeyInfo('m', ConsoleKey.M, false, false, false);
+ public static ConsoleKeyInfo N = new ConsoleKeyInfo('n', ConsoleKey.N, false, false, false);
+ public static ConsoleKeyInfo O = new ConsoleKeyInfo('o', ConsoleKey.O, false, false, false);
+ public static ConsoleKeyInfo P = new ConsoleKeyInfo('p', ConsoleKey.P, false, false, false);
+ public static ConsoleKeyInfo Q = new ConsoleKeyInfo('q', ConsoleKey.Q, false, false, false);
+ public static ConsoleKeyInfo R = new ConsoleKeyInfo('r', ConsoleKey.R, false, false, false);
+ public static ConsoleKeyInfo S = new ConsoleKeyInfo('s', ConsoleKey.S, false, false, false);
+ public static ConsoleKeyInfo T = new ConsoleKeyInfo('t', ConsoleKey.T, false, false, false);
+ public static ConsoleKeyInfo U = new ConsoleKeyInfo('u', ConsoleKey.U, false, false, false);
+ public static ConsoleKeyInfo V = new ConsoleKeyInfo('v', ConsoleKey.V, false, false, false);
+ public static ConsoleKeyInfo W = new ConsoleKeyInfo('w', ConsoleKey.W, false, false, false);
+ public static ConsoleKeyInfo X = new ConsoleKeyInfo('x', ConsoleKey.X, false, false, false);
+ public static ConsoleKeyInfo Y = new ConsoleKeyInfo('y', ConsoleKey.Y, false, false, false);
+ public static ConsoleKeyInfo Z = new ConsoleKeyInfo('z', ConsoleKey.Z, false, false, false);
+ public static ConsoleKeyInfo ucA = new ConsoleKeyInfo('A', ConsoleKey.A, true, false, false);
+ public static ConsoleKeyInfo ucB = new ConsoleKeyInfo('B', ConsoleKey.B, true, false, false);
+ public static ConsoleKeyInfo ucC = new ConsoleKeyInfo('C', ConsoleKey.C, true, false, false);
+ public static ConsoleKeyInfo ucD = new ConsoleKeyInfo('D', ConsoleKey.D, true, false, false);
+ public static ConsoleKeyInfo ucE = new ConsoleKeyInfo('E', ConsoleKey.E, true, false, false);
+ public static ConsoleKeyInfo ucF = new ConsoleKeyInfo('F', ConsoleKey.F, true, false, false);
+ public static ConsoleKeyInfo ucG = new ConsoleKeyInfo('G', ConsoleKey.G, true, false, false);
+ public static ConsoleKeyInfo ucH = new ConsoleKeyInfo('H', ConsoleKey.H, true, false, false);
+ public static ConsoleKeyInfo ucI = new ConsoleKeyInfo('I', ConsoleKey.I, true, false, false);
+ public static ConsoleKeyInfo ucJ = new ConsoleKeyInfo('J', ConsoleKey.J, true, false, false);
+ public static ConsoleKeyInfo ucK = new ConsoleKeyInfo('K', ConsoleKey.K, true, false, false);
+ public static ConsoleKeyInfo ucL = new ConsoleKeyInfo('L', ConsoleKey.L, true, false, false);
+ public static ConsoleKeyInfo ucM = new ConsoleKeyInfo('M', ConsoleKey.M, true, false, false);
+ public static ConsoleKeyInfo ucN = new ConsoleKeyInfo('N', ConsoleKey.N, true, false, false);
+ public static ConsoleKeyInfo ucO = new ConsoleKeyInfo('O', ConsoleKey.O, true, false, false);
+ public static ConsoleKeyInfo ucP = new ConsoleKeyInfo('P', ConsoleKey.P, true, false, false);
+ public static ConsoleKeyInfo ucQ = new ConsoleKeyInfo('Q', ConsoleKey.Q, true, false, false);
+ public static ConsoleKeyInfo ucR = new ConsoleKeyInfo('R', ConsoleKey.R, true, false, false);
+ public static ConsoleKeyInfo ucS = new ConsoleKeyInfo('S', ConsoleKey.S, true, false, false);
+ public static ConsoleKeyInfo ucT = new ConsoleKeyInfo('T', ConsoleKey.T, true, false, false);
+ public static ConsoleKeyInfo ucU = new ConsoleKeyInfo('U', ConsoleKey.U, true, false, false);
+ public static ConsoleKeyInfo ucV = new ConsoleKeyInfo('V', ConsoleKey.V, true, false, false);
+ public static ConsoleKeyInfo ucW = new ConsoleKeyInfo('W', ConsoleKey.W, true, false, false);
+ public static ConsoleKeyInfo ucX = new ConsoleKeyInfo('X', ConsoleKey.X, true, false, false);
+ public static ConsoleKeyInfo ucY = new ConsoleKeyInfo('Y', ConsoleKey.Y, true, false, false);
+ public static ConsoleKeyInfo ucZ = new ConsoleKeyInfo('Z', ConsoleKey.Z, true, false, false);
+
+ public static ConsoleKeyInfo _0 = new ConsoleKeyInfo('0', ConsoleKey.D0, false, false, false);
+ public static ConsoleKeyInfo _1 = new ConsoleKeyInfo('1', ConsoleKey.D1, false, false, false);
+ public static ConsoleKeyInfo _2 = new ConsoleKeyInfo('2', ConsoleKey.D2, false, false, false);
+ public static ConsoleKeyInfo _3 = new ConsoleKeyInfo('3', ConsoleKey.D3, false, false, false);
+ public static ConsoleKeyInfo _4 = new ConsoleKeyInfo('4', ConsoleKey.D4, false, false, false);
+ public static ConsoleKeyInfo _5 = new ConsoleKeyInfo('5', ConsoleKey.D5, false, false, false);
+ public static ConsoleKeyInfo _6 = new ConsoleKeyInfo('6', ConsoleKey.D6, false, false, false);
+ public static ConsoleKeyInfo _7 = new ConsoleKeyInfo('7', ConsoleKey.D7, false, false, false);
+ public static ConsoleKeyInfo _8 = new ConsoleKeyInfo('8', ConsoleKey.D8, false, false, false);
+ public static ConsoleKeyInfo _9 = new ConsoleKeyInfo('9', ConsoleKey.D9, false, false, false);
+
+ public static ConsoleKeyInfo RParen = new ConsoleKeyInfo(')', ConsoleKey.D0, true, false, false);
+ public static ConsoleKeyInfo Bang = new ConsoleKeyInfo('!', ConsoleKey.D1, true, false, false);
+ public static ConsoleKeyInfo At = new ConsoleKeyInfo('@', ConsoleKey.D2, true, false, false);
+ public static ConsoleKeyInfo Pound = new ConsoleKeyInfo('#', ConsoleKey.D3, true, false, false);
+ public static ConsoleKeyInfo Dollar = new ConsoleKeyInfo('$', ConsoleKey.D4, true, false, false);
+ public static ConsoleKeyInfo Percent = new ConsoleKeyInfo('%', ConsoleKey.D5, true, false, false);
+ public static ConsoleKeyInfo Uphat = new ConsoleKeyInfo('^', ConsoleKey.D6, true, false, false);
+ public static ConsoleKeyInfo Ampersand = new ConsoleKeyInfo('&', ConsoleKey.D7, true, false, false);
+ public static ConsoleKeyInfo Star = new ConsoleKeyInfo('*', ConsoleKey.D8, true, false, false);
+ public static ConsoleKeyInfo LParen = new ConsoleKeyInfo('(', ConsoleKey.D9, true, false, false);
+
+ public static ConsoleKeyInfo Colon = new ConsoleKeyInfo(':', ConsoleKey.Oem1, true, false, false);
+ public static ConsoleKeyInfo Semicolon = new ConsoleKeyInfo(';', ConsoleKey.Oem1, false, false, false);
+ public static ConsoleKeyInfo Question = new ConsoleKeyInfo('?', ConsoleKey.Oem2, true, false, false);
+ public static ConsoleKeyInfo Slash = new ConsoleKeyInfo('/', ConsoleKey.Oem2, false, false, false);
+ public static ConsoleKeyInfo Tilde = new ConsoleKeyInfo('~', ConsoleKey.Oem3, true, false, false);
+ public static ConsoleKeyInfo Backtick = new ConsoleKeyInfo('`', ConsoleKey.Oem3, false, false, false);
+ public static ConsoleKeyInfo LCurly = new ConsoleKeyInfo('{', ConsoleKey.Oem4, true, false, false);
+ public static ConsoleKeyInfo LBracket = new ConsoleKeyInfo('[', ConsoleKey.Oem4, false, false, false);
+ public static ConsoleKeyInfo Pipe = new ConsoleKeyInfo('|', ConsoleKey.Oem5, true, false, false);
+ public static ConsoleKeyInfo Backslash = new ConsoleKeyInfo('\\', ConsoleKey.Oem5, false, false, false);
+ public static ConsoleKeyInfo RCurly = new ConsoleKeyInfo('}', ConsoleKey.Oem6, true, false, false);
+ public static ConsoleKeyInfo RBracket = new ConsoleKeyInfo(']', ConsoleKey.Oem6, false, false, false);
+ public static ConsoleKeyInfo SQuote = new ConsoleKeyInfo('\'', ConsoleKey.Oem7, false, false, false);
+ public static ConsoleKeyInfo DQuote = new ConsoleKeyInfo('"', ConsoleKey.Oem7, true, false, false);
+ public static ConsoleKeyInfo LessThan = new ConsoleKeyInfo('<', ConsoleKey.OemComma, true, false, false);
+ public static ConsoleKeyInfo Comma = new ConsoleKeyInfo(',', ConsoleKey.OemComma, false, false, false);
+ public static ConsoleKeyInfo GreaterThan = new ConsoleKeyInfo('>', ConsoleKey.OemPeriod, true, false, false);
+ public static ConsoleKeyInfo Period = new ConsoleKeyInfo('.', ConsoleKey.OemPeriod, false, false, false);
+ public static ConsoleKeyInfo Underbar = new ConsoleKeyInfo('_', ConsoleKey.OemMinus, true, false, false);
+ public static ConsoleKeyInfo Minus = new ConsoleKeyInfo('-', ConsoleKey.OemMinus, false, false, false);
+ public static ConsoleKeyInfo AltMinus = new ConsoleKeyInfo('-', ConsoleKey.OemMinus, false, true, false);
+ public static ConsoleKeyInfo Plus = new ConsoleKeyInfo('+', ConsoleKey.OemPlus, true, false, false);
+ new
+ public static ConsoleKeyInfo Equals = new ConsoleKeyInfo('=', ConsoleKey.OemPlus, false, false, false);
+
+ public static ConsoleKeyInfo CtrlAt = new ConsoleKeyInfo((char)0, ConsoleKey.D2, true, false, true);
+ public static ConsoleKeyInfo AltUnderbar = new ConsoleKeyInfo('_', ConsoleKey.OemMinus, true, true, false);
+ public static ConsoleKeyInfo CtrlUnderbar = new ConsoleKeyInfo((char)31, ConsoleKey.OemMinus, true, false, true);
+ public static ConsoleKeyInfo AltEquals = new ConsoleKeyInfo('=', ConsoleKey.OemPlus, false, true, false);
+ public static ConsoleKeyInfo Space = new ConsoleKeyInfo(' ', ConsoleKey.Spacebar, false, false, false);
+ // Useless because it's caught by the console to bring up the system menu.
+ public static ConsoleKeyInfo AltSpace = new ConsoleKeyInfo(' ', ConsoleKey.Spacebar, false, true, false);
+ public static ConsoleKeyInfo CtrlSpace = new ConsoleKeyInfo(' ', ConsoleKey.Spacebar, false, false, true);
+ public static ConsoleKeyInfo AltLess = new ConsoleKeyInfo('<', ConsoleKey.OemComma, true, true, false);
+ public static ConsoleKeyInfo AltGreater = new ConsoleKeyInfo('>', ConsoleKey.OemPeriod, true, true, false);
+ public static ConsoleKeyInfo CtrlRBracket = new ConsoleKeyInfo((char)29, ConsoleKey.Oem6, false, false, true);
+ public static ConsoleKeyInfo AltCtrlRBracket = new ConsoleKeyInfo((char)0, ConsoleKey.Oem6, false, true, true);
+ public static ConsoleKeyInfo AltPeriod = new ConsoleKeyInfo('.', ConsoleKey.OemPeriod, false, true, false);
+ public static ConsoleKeyInfo CtrlAltQuestion = new ConsoleKeyInfo((char)0, ConsoleKey.Oem2, true, true, true);
+ public static ConsoleKeyInfo AltQuestion = new ConsoleKeyInfo('?', ConsoleKey.Oem2, true, true, false);
+
+ public static ConsoleKeyInfo Alt0 = new ConsoleKeyInfo('0', ConsoleKey.D0, false, true, false);
+ public static ConsoleKeyInfo Alt1 = new ConsoleKeyInfo('1', ConsoleKey.D1, false, true, false);
+ public static ConsoleKeyInfo Alt2 = new ConsoleKeyInfo('2', ConsoleKey.D2, false, true, false);
+ public static ConsoleKeyInfo Alt3 = new ConsoleKeyInfo('3', ConsoleKey.D3, false, true, false);
+ public static ConsoleKeyInfo Alt4 = new ConsoleKeyInfo('4', ConsoleKey.D4, false, true, false);
+ public static ConsoleKeyInfo Alt5 = new ConsoleKeyInfo('5', ConsoleKey.D5, false, true, false);
+ public static ConsoleKeyInfo Alt6 = new ConsoleKeyInfo('6', ConsoleKey.D6, false, true, false);
+ public static ConsoleKeyInfo Alt7 = new ConsoleKeyInfo('7', ConsoleKey.D7, false, true, false);
+ public static ConsoleKeyInfo Alt8 = new ConsoleKeyInfo('8', ConsoleKey.D8, false, true, false);
+ public static ConsoleKeyInfo Alt9 = new ConsoleKeyInfo('9', ConsoleKey.D9, false, true, false);
+
+ public static ConsoleKeyInfo AltA = new ConsoleKeyInfo((char)97, ConsoleKey.A, false, true, false);
+ public static ConsoleKeyInfo AltB = new ConsoleKeyInfo((char)98, ConsoleKey.B, false, true, false);
+ public static ConsoleKeyInfo AltC = new ConsoleKeyInfo((char)99, ConsoleKey.C, false, true, false);
+ public static ConsoleKeyInfo AltD = new ConsoleKeyInfo((char)100, ConsoleKey.D, false, true, false);
+ public static ConsoleKeyInfo AltE = new ConsoleKeyInfo((char)101, ConsoleKey.E, false, true, false);
+ public static ConsoleKeyInfo AltF = new ConsoleKeyInfo((char)102, ConsoleKey.F, false, true, false);
+ public static ConsoleKeyInfo AltG = new ConsoleKeyInfo((char)103, ConsoleKey.G, false, true, false);
+ public static ConsoleKeyInfo AltH = new ConsoleKeyInfo((char)104, ConsoleKey.H, false, true, false);
+ public static ConsoleKeyInfo AltI = new ConsoleKeyInfo((char)105, ConsoleKey.I, false, true, false);
+ public static ConsoleKeyInfo AltJ = new ConsoleKeyInfo((char)106, ConsoleKey.J, false, true, false);
+ public static ConsoleKeyInfo AltK = new ConsoleKeyInfo((char)107, ConsoleKey.K, false, true, false);
+ public static ConsoleKeyInfo AltL = new ConsoleKeyInfo((char)108, ConsoleKey.L, false, true, false);
+ public static ConsoleKeyInfo AltM = new ConsoleKeyInfo((char)109, ConsoleKey.M, false, true, false);
+ public static ConsoleKeyInfo AltN = new ConsoleKeyInfo((char)110, ConsoleKey.N, false, true, false);
+ public static ConsoleKeyInfo AltO = new ConsoleKeyInfo((char)111, ConsoleKey.O, false, true, false);
+ public static ConsoleKeyInfo AltP = new ConsoleKeyInfo((char)112, ConsoleKey.P, false, true, false);
+ public static ConsoleKeyInfo AltQ = new ConsoleKeyInfo((char)113, ConsoleKey.Q, false, true, false);
+ public static ConsoleKeyInfo AltR = new ConsoleKeyInfo((char)114, ConsoleKey.R, false, true, false);
+ public static ConsoleKeyInfo AltS = new ConsoleKeyInfo((char)115, ConsoleKey.S, false, true, false);
+ public static ConsoleKeyInfo AltT = new ConsoleKeyInfo((char)116, ConsoleKey.T, false, true, false);
+ public static ConsoleKeyInfo AltU = new ConsoleKeyInfo((char)117, ConsoleKey.U, false, true, false);
+ public static ConsoleKeyInfo AltV = new ConsoleKeyInfo((char)118, ConsoleKey.V, false, true, false);
+ public static ConsoleKeyInfo AltW = new ConsoleKeyInfo((char)119, ConsoleKey.W, false, true, false);
+ public static ConsoleKeyInfo AltX = new ConsoleKeyInfo((char)120, ConsoleKey.X, false, true, false);
+ public static ConsoleKeyInfo AltY = new ConsoleKeyInfo((char)121, ConsoleKey.Y, false, true, false);
+ public static ConsoleKeyInfo AltZ = new ConsoleKeyInfo((char)122, ConsoleKey.Z, false, true, false);
+
+ public static ConsoleKeyInfo CtrlA = new ConsoleKeyInfo((char)1, ConsoleKey.A, false, false, true);
+ public static ConsoleKeyInfo CtrlB = new ConsoleKeyInfo((char)2, ConsoleKey.B, false, false, true);
+ public static ConsoleKeyInfo CtrlC = new ConsoleKeyInfo((char)3, ConsoleKey.C, false, false, true);
+ public static ConsoleKeyInfo CtrlD = new ConsoleKeyInfo((char)4, ConsoleKey.D, false, false, true);
+ public static ConsoleKeyInfo CtrlE = new ConsoleKeyInfo((char)5, ConsoleKey.E, false, false, true);
+ public static ConsoleKeyInfo CtrlF = new ConsoleKeyInfo((char)6, ConsoleKey.F, false, false, true);
+ public static ConsoleKeyInfo CtrlG = new ConsoleKeyInfo((char)7, ConsoleKey.G, false, false, true);
+ public static ConsoleKeyInfo CtrlH = new ConsoleKeyInfo((char)8, ConsoleKey.H, false, false, true);
+ public static ConsoleKeyInfo CtrlI = new ConsoleKeyInfo((char)9, ConsoleKey.I, false, false, true);
+ public static ConsoleKeyInfo CtrlJ = new ConsoleKeyInfo((char)10, ConsoleKey.J, false, false, true);
+ public static ConsoleKeyInfo CtrlK = new ConsoleKeyInfo((char)11, ConsoleKey.K, false, false, true);
+ public static ConsoleKeyInfo CtrlL = new ConsoleKeyInfo((char)12, ConsoleKey.L, false, false, true);
+ public static ConsoleKeyInfo CtrlM = new ConsoleKeyInfo((char)13, ConsoleKey.M, false, false, true);
+ public static ConsoleKeyInfo CtrlN = new ConsoleKeyInfo((char)14, ConsoleKey.N, false, false, true);
+ public static ConsoleKeyInfo CtrlO = new ConsoleKeyInfo((char)15, ConsoleKey.O, false, false, true);
+ public static ConsoleKeyInfo CtrlP = new ConsoleKeyInfo((char)16, ConsoleKey.P, false, false, true);
+ public static ConsoleKeyInfo CtrlQ = new ConsoleKeyInfo((char)17, ConsoleKey.Q, false, false, true);
+ public static ConsoleKeyInfo CtrlR = new ConsoleKeyInfo((char)18, ConsoleKey.R, false, false, true);
+ public static ConsoleKeyInfo CtrlS = new ConsoleKeyInfo((char)19, ConsoleKey.S, false, false, true);
+ public static ConsoleKeyInfo CtrlT = new ConsoleKeyInfo((char)20, ConsoleKey.T, false, false, true);
+ public static ConsoleKeyInfo CtrlU = new ConsoleKeyInfo((char)21, ConsoleKey.U, false, false, true);
+ public static ConsoleKeyInfo CtrlV = new ConsoleKeyInfo((char)22, ConsoleKey.V, false, false, true);
+ public static ConsoleKeyInfo CtrlW = new ConsoleKeyInfo((char)23, ConsoleKey.W, false, false, true);
+ public static ConsoleKeyInfo CtrlX = new ConsoleKeyInfo((char)24, ConsoleKey.X, false, false, true);
+ public static ConsoleKeyInfo CtrlY = new ConsoleKeyInfo((char)25, ConsoleKey.Y, false, false, true);
+ public static ConsoleKeyInfo CtrlZ = new ConsoleKeyInfo((char)26, ConsoleKey.Z, false, false, true);
+
+ public static ConsoleKeyInfo CtrlShiftC = new ConsoleKeyInfo((char)3, ConsoleKey.C, true, false, true);
+
+ public static ConsoleKeyInfo AltShiftB = new ConsoleKeyInfo('B', ConsoleKey.B, true, true, false);
+ public static ConsoleKeyInfo AltShiftF = new ConsoleKeyInfo('F', ConsoleKey.F, true, true, false);
+
+ public static ConsoleKeyInfo AltCtrlY = new ConsoleKeyInfo((char)0, ConsoleKey.Y, false, true, true);
+
+ public static ConsoleKeyInfo Backspace = new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, false, false);
+ public static ConsoleKeyInfo CtrlBackspace = new ConsoleKeyInfo((char)0x7f, ConsoleKey.Backspace, false, false, true);
+ public static ConsoleKeyInfo AltBackspace = new ConsoleKeyInfo((char)8, ConsoleKey.Backspace, false, true, false);
+ public static ConsoleKeyInfo Delete = new ConsoleKeyInfo((char)0, ConsoleKey.Delete, false, false, false);
+ public static ConsoleKeyInfo CtrlDelete = new ConsoleKeyInfo((char)0, ConsoleKey.Delete, false, false, true);
+ public static ConsoleKeyInfo DownArrow = new ConsoleKeyInfo((char)0, ConsoleKey.DownArrow, false, false, false);
+ public static ConsoleKeyInfo End = new ConsoleKeyInfo((char)0, ConsoleKey.End, false, false, false);
+ public static ConsoleKeyInfo CtrlEnd = new ConsoleKeyInfo((char)0, ConsoleKey.End, false, false, true);
+ public static ConsoleKeyInfo ShiftEnd = new ConsoleKeyInfo((char)0, ConsoleKey.End, true, false, false);
+ public static ConsoleKeyInfo Enter = new ConsoleKeyInfo((char)13, ConsoleKey.Enter, false, false, false);
+ public static ConsoleKeyInfo Escape = new ConsoleKeyInfo((char)27, ConsoleKey.Escape, false, false, false);
+ public static ConsoleKeyInfo Home = new ConsoleKeyInfo((char)0, ConsoleKey.Home, false, false, false);
+ public static ConsoleKeyInfo CtrlHome = new ConsoleKeyInfo((char)0, ConsoleKey.Home, false, false, true);
+ public static ConsoleKeyInfo ShiftHome = new ConsoleKeyInfo((char)0, ConsoleKey.Home, true, false, false);
+ public static ConsoleKeyInfo LeftArrow = new ConsoleKeyInfo((char)0, ConsoleKey.LeftArrow, false, false, false);
+ public static ConsoleKeyInfo RightArrow = new ConsoleKeyInfo((char)0, ConsoleKey.RightArrow, false, false, false);
+ public static ConsoleKeyInfo Tab = new ConsoleKeyInfo((char)9, ConsoleKey.Tab, false, false, false);
+ public static ConsoleKeyInfo UpArrow = new ConsoleKeyInfo((char)0, ConsoleKey.UpArrow, false, false, false);
+ public static ConsoleKeyInfo PageUp = new ConsoleKeyInfo((char)0, ConsoleKey.PageUp, false, false, false);
+ public static ConsoleKeyInfo PageDown = new ConsoleKeyInfo((char)0, ConsoleKey.PageDown, false, false, false);
+ public static ConsoleKeyInfo ShiftPageUp = new ConsoleKeyInfo((char)0, ConsoleKey.PageUp, true, false, false);
+ public static ConsoleKeyInfo ShiftPageDown = new ConsoleKeyInfo((char)0, ConsoleKey.PageDown, true, false, false);
+ public static ConsoleKeyInfo CtrlPageUp = new ConsoleKeyInfo((char)0, ConsoleKey.PageUp, false, false, true);
+ public static ConsoleKeyInfo CtrlPageDown = new ConsoleKeyInfo((char)0, ConsoleKey.PageDown, false, false, true);
+ public static ConsoleKeyInfo AltPageUp = new ConsoleKeyInfo((char)0, ConsoleKey.PageUp, false, true, false);
+ public static ConsoleKeyInfo AltPageDown = new ConsoleKeyInfo((char)0, ConsoleKey.PageDown, false, true, false);
+
+ public static ConsoleKeyInfo ShiftLeftArrow = new ConsoleKeyInfo((char)0, ConsoleKey.LeftArrow, true, false, false);
+ public static ConsoleKeyInfo ShiftRightArrow = new ConsoleKeyInfo((char)0, ConsoleKey.RightArrow, true, false, false);
+ public static ConsoleKeyInfo CtrlLeftArrow = new ConsoleKeyInfo((char)0, ConsoleKey.LeftArrow, false, false, true);
+ public static ConsoleKeyInfo CtrlRightArrow = new ConsoleKeyInfo((char)0, ConsoleKey.RightArrow, false, false, true);
+ public static ConsoleKeyInfo ShiftCtrlLeftArrow = new ConsoleKeyInfo((char)0, ConsoleKey.LeftArrow, true, false, true);
+ public static ConsoleKeyInfo ShiftCtrlRightArrow = new ConsoleKeyInfo((char)0, ConsoleKey.RightArrow, true, false, true);
+
+ public static ConsoleKeyInfo ShiftTab = new ConsoleKeyInfo((char)9, ConsoleKey.Tab, true, false, false);
+
+ public static ConsoleKeyInfo CtrlEnter = new ConsoleKeyInfo((char)10, ConsoleKey.Enter, false, false, true);
+ public static ConsoleKeyInfo CtrlShiftEnter = new ConsoleKeyInfo((char)0, ConsoleKey.Enter, true, false, true);
+ public static ConsoleKeyInfo ShiftEnter = new ConsoleKeyInfo((char)13, ConsoleKey.Enter, true, false, false);
+
+ public static ConsoleKeyInfo F1 = new ConsoleKeyInfo((char)0, ConsoleKey.F1, false, false, false);
+ public static ConsoleKeyInfo F2 = new ConsoleKeyInfo((char)0, ConsoleKey.F2, false, false, false);
+ public static ConsoleKeyInfo F3 = new ConsoleKeyInfo((char)0, ConsoleKey.F3, false, false, false);
+ public static ConsoleKeyInfo F4 = new ConsoleKeyInfo((char)0, ConsoleKey.F4, false, false, false);
+ public static ConsoleKeyInfo F5 = new ConsoleKeyInfo((char)0, ConsoleKey.F5, false, false, false);
+ public static ConsoleKeyInfo F6 = new ConsoleKeyInfo((char)0, ConsoleKey.F6, false, false, false);
+ public static ConsoleKeyInfo F7 = new ConsoleKeyInfo((char)0, ConsoleKey.F7, false, false, false);
+ public static ConsoleKeyInfo F8 = new ConsoleKeyInfo((char)0, ConsoleKey.F8, false, false, false);
+ public static ConsoleKeyInfo F9 = new ConsoleKeyInfo((char)0, ConsoleKey.F9, false, false, false);
+ public static ConsoleKeyInfo Fl0 = new ConsoleKeyInfo((char)0, ConsoleKey.F10, false, false, false);
+ public static ConsoleKeyInfo F11 = new ConsoleKeyInfo((char)0, ConsoleKey.F11, false, false, false);
+ public static ConsoleKeyInfo F12 = new ConsoleKeyInfo((char)0, ConsoleKey.F12, false, false, false);
+ public static ConsoleKeyInfo F13 = new ConsoleKeyInfo((char)0, ConsoleKey.F13, false, false, false);
+ public static ConsoleKeyInfo F14 = new ConsoleKeyInfo((char)0, ConsoleKey.F14, false, false, false);
+ public static ConsoleKeyInfo F15 = new ConsoleKeyInfo((char)0, ConsoleKey.F15, false, false, false);
+ public static ConsoleKeyInfo F16 = new ConsoleKeyInfo((char)0, ConsoleKey.F16, false, false, false);
+ public static ConsoleKeyInfo F17 = new ConsoleKeyInfo((char)0, ConsoleKey.F17, false, false, false);
+ public static ConsoleKeyInfo F18 = new ConsoleKeyInfo((char)0, ConsoleKey.F18, false, false, false);
+ public static ConsoleKeyInfo F19 = new ConsoleKeyInfo((char)0, ConsoleKey.F19, false, false, false);
+ public static ConsoleKeyInfo F20 = new ConsoleKeyInfo((char)0, ConsoleKey.F20, false, false, false);
+ public static ConsoleKeyInfo F21 = new ConsoleKeyInfo((char)0, ConsoleKey.F21, false, false, false);
+ public static ConsoleKeyInfo F22 = new ConsoleKeyInfo((char)0, ConsoleKey.F22, false, false, false);
+ public static ConsoleKeyInfo F23 = new ConsoleKeyInfo((char)0, ConsoleKey.F23, false, false, false);
+ public static ConsoleKeyInfo F24 = new ConsoleKeyInfo((char)0, ConsoleKey.F24, false, false, false);
+
+ public static ConsoleKeyInfo AltF1 = new ConsoleKeyInfo((char)0, ConsoleKey.F1, false, true, false);
+ public static ConsoleKeyInfo AltF2 = new ConsoleKeyInfo((char)0, ConsoleKey.F2, false, true, false);
+ public static ConsoleKeyInfo AltF3 = new ConsoleKeyInfo((char)0, ConsoleKey.F3, false, true, false);
+ public static ConsoleKeyInfo AltF4 = new ConsoleKeyInfo((char)0, ConsoleKey.F4, false, true, false);
+ public static ConsoleKeyInfo AltF5 = new ConsoleKeyInfo((char)0, ConsoleKey.F5, false, true, false);
+ public static ConsoleKeyInfo AltF6 = new ConsoleKeyInfo((char)0, ConsoleKey.F6, false, true, false);
+ public static ConsoleKeyInfo AltF7 = new ConsoleKeyInfo((char)0, ConsoleKey.F7, false, true, false);
+ public static ConsoleKeyInfo AltF8 = new ConsoleKeyInfo((char)0, ConsoleKey.F8, false, true, false);
+ public static ConsoleKeyInfo AltF9 = new ConsoleKeyInfo((char)0, ConsoleKey.F9, false, true, false);
+ public static ConsoleKeyInfo AltFl0 = new ConsoleKeyInfo((char)0, ConsoleKey.F10, false, true, false);
+ public static ConsoleKeyInfo AltF11 = new ConsoleKeyInfo((char)0, ConsoleKey.F11, false, true, false);
+ public static ConsoleKeyInfo AltF12 = new ConsoleKeyInfo((char)0, ConsoleKey.F12, false, true, false);
+ public static ConsoleKeyInfo AltF13 = new ConsoleKeyInfo((char)0, ConsoleKey.F13, false, true, false);
+ public static ConsoleKeyInfo AltF14 = new ConsoleKeyInfo((char)0, ConsoleKey.F14, false, true, false);
+ public static ConsoleKeyInfo AltF15 = new ConsoleKeyInfo((char)0, ConsoleKey.F15, false, true, false);
+ public static ConsoleKeyInfo AltF16 = new ConsoleKeyInfo((char)0, ConsoleKey.F16, false, true, false);
+ public static ConsoleKeyInfo AltF17 = new ConsoleKeyInfo((char)0, ConsoleKey.F17, false, true, false);
+ public static ConsoleKeyInfo AltF18 = new ConsoleKeyInfo((char)0, ConsoleKey.F18, false, true, false);
+ public static ConsoleKeyInfo AltF19 = new ConsoleKeyInfo((char)0, ConsoleKey.F19, false, true, false);
+ public static ConsoleKeyInfo AltF20 = new ConsoleKeyInfo((char)0, ConsoleKey.F20, false, true, false);
+ public static ConsoleKeyInfo AltF21 = new ConsoleKeyInfo((char)0, ConsoleKey.F21, false, true, false);
+ public static ConsoleKeyInfo AltF22 = new ConsoleKeyInfo((char)0, ConsoleKey.F22, false, true, false);
+ public static ConsoleKeyInfo AltF23 = new ConsoleKeyInfo((char)0, ConsoleKey.F23, false, true, false);
+ public static ConsoleKeyInfo AltF24 = new ConsoleKeyInfo((char)0, ConsoleKey.F24, false, true, false);
+
+ public static ConsoleKeyInfo ShiftF3 = new ConsoleKeyInfo((char)0, ConsoleKey.F3, true, false, false);
+ public static ConsoleKeyInfo ShiftF8 = new ConsoleKeyInfo((char)0, ConsoleKey.F8, true, false, false);
+
+ // Keys to ignore
+#if !UNIX
+ public static ConsoleKeyInfo VolumeUp = new ConsoleKeyInfo((char)0, /*ConsoleKey.VolumeUp*/(ConsoleKey)175, false, false, false);
+ public static ConsoleKeyInfo VolumeDown = new ConsoleKeyInfo((char)0, /*ConsoleKey.VolumeDown*/(ConsoleKey)174, false, false, false);
+ public static ConsoleKeyInfo VolumeMute = new ConsoleKeyInfo((char)0, /*ConsoleKey.VolumeMute*/(ConsoleKey)173, false, false, false);
+#endif
+ }
+}
diff --git a/src/Microsoft.PowerShell.PSReadLine/KillYank.cs b/src/Microsoft.PowerShell.PSReadLine/KillYank.cs
new file mode 100644
index 00000000000..b06be4cc928
--- /dev/null
+++ b/src/Microsoft.PowerShell.PSReadLine/KillYank.cs
@@ -0,0 +1,565 @@
+/********************************************************************++
+Copyright (c) Microsoft Corporation. All rights reserved.
+--********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Management.Automation.Language;
+#if !CORECLR // TODO: clipboard
+using System.Windows.Forms;
+#endif
+
+namespace Microsoft.PowerShell
+{
+ public partial class PSConsoleReadLine
+ {
+ // Yank/Kill state
+ private List _killRing;
+ private int _killIndex;
+ private int _killCommandCount;
+ private int _yankCommandCount;
+ private int _yankStartPoint;
+ private int _yankLastArgCommandCount;
+ class YankLastArgState
+ {
+ internal int argument;
+ internal int historyIndex;
+ internal int historyIncrement;
+ internal int startPoint = -1;
+ }
+ private YankLastArgState _yankLastArgState;
+ private int _visualSelectionCommandCount;
+
+ ///
+ /// Mark the current location of the cursor for use in a subsequent editing command.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void SetMark(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ _singleton._mark = _singleton._current;
+ }
+
+ ///
+ /// The cursor is placed at the location of the mark and the mark is moved
+ /// to the location of the cursor.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void ExchangePointAndMark(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ var tmp = _singleton._mark;
+ _singleton._mark = _singleton._current;
+ _singleton._current = tmp;
+ _singleton.PlaceCursor();
+ }
+
+ ///
+ /// The contents of the kill ring are cleared.
+ ///
+ public static void ClearKillRing()
+ {
+ if (_singleton._killRing != null)
+ {
+ _singleton._killRing.Clear();
+ }
+ _singleton._killIndex = -1; // So first add indexes 0.
+ }
+
+ private void Kill(int start, int length, bool prepend)
+ {
+ if (length > 0)
+ {
+ var killText = _buffer.ToString(start, length);
+ SaveEditItem(EditItemDelete.Create(killText, start));
+ _buffer.Remove(start, length);
+ _current = start;
+ Render();
+ if (_killCommandCount > 0)
+ {
+ if (prepend)
+ {
+ _killRing[_killIndex] = killText + _killRing[_killIndex];
+ }
+ else
+ {
+ _killRing[_killIndex] += killText;
+ }
+ }
+ else
+ {
+ if (_killRing.Count < Options.MaximumKillRingCount)
+ {
+ _killRing.Add(killText);
+ _killIndex = _killRing.Count - 1;
+ }
+ else
+ {
+ _killIndex += 1;
+ if (_killIndex == _killRing.Count)
+ {
+ _killIndex = 0;
+ }
+ _killRing[_killIndex] = killText;
+ }
+ }
+ }
+ _killCommandCount += 1;
+ }
+
+ ///
+ /// Clear the input from the cursor to the end of the input. The cleared text is placed
+ /// in the kill ring.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void KillLine(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ _singleton.Kill(_singleton._current, _singleton._buffer.Length - _singleton._current, false);
+ }
+
+ ///
+ /// Clear the input from the start of the input to the cursor. The cleared text is placed
+ /// in the kill ring.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void BackwardKillLine(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ _singleton.Kill(0, _singleton._current, true);
+ }
+
+ ///
+ /// Clear the input from the cursor to the end of the current word. If the cursor
+ /// is between words, the input is cleared from the cursor to the end of the next word.
+ /// The cleared text is placed in the kill ring.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void KillWord(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ int i = _singleton.FindForwardWordPoint(_singleton.Options.WordDelimiters);
+ _singleton.Kill(_singleton._current, i - _singleton._current, false);
+ }
+
+ ///
+ /// Clear the input from the cursor to the end of the current word. If the cursor
+ /// is between words, the input is cleared from the cursor to the end of the next word.
+ /// The cleared text is placed in the kill ring.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void ShellKillWord(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ var token = _singleton.FindToken(_singleton._current, FindTokenMode.CurrentOrNext);
+ var end = (token.Kind == TokenKind.EndOfInput)
+ ? _singleton._buffer.Length
+ : token.Extent.EndOffset;
+ _singleton.Kill(_singleton._current, end - _singleton._current, false);
+ }
+
+ ///
+ /// Clear the input from the start of the current word to the cursor. If the cursor
+ /// is between words, the input is cleared from the start of the previous word to the
+ /// cursor. The cleared text is placed in the kill ring.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void BackwardKillWord(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ int i = _singleton.FindBackwardWordPoint(_singleton.Options.WordDelimiters);
+ _singleton.Kill(i, _singleton._current - i, true);
+ }
+
+ ///
+ /// Clear the input from the start of the current word to the cursor. If the cursor
+ /// is between words, the input is cleared from the start of the previous word to the
+ /// cursor. The cleared text is placed in the kill ring.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void UnixWordRubout(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ int i = _singleton.FindBackwardWordPoint("");
+ _singleton.Kill(i, _singleton._current - i, true);
+ }
+
+ ///
+ /// Clear the input from the start of the current word to the cursor. If the cursor
+ /// is between words, the input is cleared from the start of the previous word to the
+ /// cursor. The cleared text is placed in the kill ring.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void ShellBackwardKillWord(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ var token = _singleton.FindToken(_singleton._current, FindTokenMode.Previous);
+ var start = token == null
+ ? 0
+ : token.Extent.StartOffset;
+ _singleton.Kill(start, _singleton._current - start, true);
+ }
+
+ ///
+ /// Kill the text between the cursor and the mark.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void KillRegion(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ int start, length;
+ _singleton.GetRegion(out start, out length);
+ _singleton.Kill(start, length, true);
+ }
+
+ private void YankImpl()
+ {
+ if (_killRing.Count == 0)
+ return;
+
+ // Starting a yank session, yank the last thing killed and
+ // remember where we started.
+ _mark = _yankStartPoint = _current;
+ Insert(_killRing[_killIndex]);
+
+ _yankCommandCount += 1;
+ }
+
+ ///
+ /// Add the most recently killed text to the input.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void Yank(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ _singleton.YankImpl();
+ }
+
+ private void YankPopImpl()
+ {
+ if (_yankCommandCount == 0)
+ return;
+
+ _killIndex -= 1;
+ if (_killIndex < 0)
+ {
+ _killIndex = _killRing.Count - 1;
+ }
+ var yankText = _killRing[_killIndex];
+ Replace(_yankStartPoint, _current - _yankStartPoint, yankText);
+ _yankCommandCount += 1;
+ }
+
+ ///
+ /// If the previous operation was Yank or YankPop, replace the previously yanked
+ /// text with the next killed text from the kill ring.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void YankPop(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ _singleton.YankPopImpl();
+ }
+
+ void YankArgImpl(YankLastArgState yankLastArgState)
+ {
+ if (yankLastArgState.historyIndex < 0 || yankLastArgState.historyIndex >= _history.Count)
+ {
+ Ding();
+ return;
+ }
+
+ Token[] tokens;
+ ParseError[] errors;
+ var buffer = _history[yankLastArgState.historyIndex];
+ Parser.ParseInput(buffer._line, out tokens, out errors);
+
+ int arg = (yankLastArgState.argument < 0)
+ ? tokens.Length + yankLastArgState.argument - 1
+ : yankLastArgState.argument;
+ if (arg < 0 || arg >= tokens.Length)
+ {
+ Ding();
+ return;
+ }
+
+ var argText = tokens[arg].Text;
+ if (yankLastArgState.startPoint < 0)
+ {
+ yankLastArgState.startPoint = _current;
+ Insert(argText);
+ }
+ else
+ {
+ Replace(yankLastArgState.startPoint, _current - yankLastArgState.startPoint, argText);
+ }
+ }
+
+ ///
+ /// Yank the first argument (after the command) from the previous history line.
+ /// With an argument, yank the nth argument (starting from 0), if the argument
+ /// is negative, start from the last argument.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void YankNthArg(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ var yankLastArgState = new YankLastArgState
+ {
+ argument = (arg is int) ? (int)arg : 1,
+ historyIndex = _singleton._currentHistoryIndex - 1,
+ };
+ _singleton.YankArgImpl(yankLastArgState);
+ }
+
+ ///
+ /// Yank the last argument from the previous history line. With an argument,
+ /// the first time it is invoked, behaves just like YankNthArg. If invoked
+ /// multiple times, instead it iterates through history and arg sets the direction
+ /// (negative reverses the direction.)
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void YankLastArg(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ if (arg != null && !(arg is int))
+ {
+ Ding();
+ return;
+ }
+
+ _singleton._yankLastArgCommandCount += 1;
+
+ if (_singleton._yankLastArgCommandCount == 1)
+ {
+ _singleton._yankLastArgState = new YankLastArgState
+ {
+ argument = (arg != null) ? (int)arg : -1,
+ historyIncrement = -1,
+ historyIndex = _singleton._currentHistoryIndex - 1
+ };
+
+ _singleton.YankArgImpl(_singleton._yankLastArgState);
+ return;
+ }
+
+ var yankLastArgState = _singleton._yankLastArgState;
+
+ if (arg != null)
+ {
+ if ((int)arg < 0)
+ {
+ yankLastArgState.historyIncrement = -yankLastArgState.historyIncrement;
+ }
+ }
+
+ yankLastArgState.historyIndex += yankLastArgState.historyIncrement;
+
+ // Don't increment more than 1 out of range so it's quick to get back to being in range.
+ if (yankLastArgState.historyIndex < 0)
+ {
+ Ding();
+ yankLastArgState.historyIndex = 0;
+ }
+ else if (yankLastArgState.historyIndex >= _singleton._history.Count)
+ {
+ Ding();
+ yankLastArgState.historyIndex = _singleton._history.Count - 1;
+ }
+ else
+ {
+ _singleton.YankArgImpl(yankLastArgState);
+ }
+ }
+
+ private void VisualSelectionCommon(Action action)
+ {
+ if (_singleton._visualSelectionCommandCount == 0)
+ {
+ SetMark();
+ }
+ _singleton._visualSelectionCommandCount += 1;
+ action();
+ _singleton.Render();
+ }
+
+ ///
+ /// Adjust the current selection to include the previous character
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void SelectBackwardChar(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ _singleton.VisualSelectionCommon(() => BackwardChar(key, arg));
+ }
+
+ ///
+ /// Adjust the current selection to include the next character
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void SelectForwardChar(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ _singleton.VisualSelectionCommon(() => ForwardChar(key, arg));
+ }
+
+ ///
+ /// Adjust the current selection to include the previous word
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void SelectBackwardWord(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ _singleton.VisualSelectionCommon(() => BackwardWord(key, arg));
+ }
+
+ ///
+ /// Adjust the current selection to include the next word
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void SelectNextWord(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ _singleton.VisualSelectionCommon(() => NextWord(key, arg));
+ }
+
+ ///
+ /// Adjust the current selection to include the next word using ForwardWord
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void SelectForwardWord(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ _singleton.VisualSelectionCommon(() => ForwardWord(key, arg));
+ }
+
+ ///
+ /// Adjust the current selection to include the next word using ShellForwardWord
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void SelectShellForwardWord(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ _singleton.VisualSelectionCommon(() => ShellForwardWord(key, arg));
+ }
+
+ ///
+ /// Adjust the current selection to include the next word using ShellNextWord
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void SelectShellNextWord(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ _singleton.VisualSelectionCommon(() => ShellNextWord(key, arg));
+ }
+
+ ///
+ /// Adjust the current selection to include the previous word using ShellBackwardWord
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void SelectShellBackwardWord(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ _singleton.VisualSelectionCommon(() => ShellBackwardWord(key, arg));
+ }
+
+ ///
+ /// Select the entire line
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void SelectAll(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ _singleton._visualSelectionCommandCount += 1;
+ _singleton._mark = 0;
+ _singleton._current = _singleton._buffer.Length;
+ _singleton.Render();
+ }
+
+ ///
+ /// Adjust the current selection to include from the cursor to the end of the line
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void SelectLine(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ _singleton.VisualSelectionCommon(() => EndOfLine(key, arg));
+ }
+
+ ///
+ /// Adjust the current selection to include from the cursor to the start of the line
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void SelectBackwardsLine(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ _singleton.VisualSelectionCommon(() => BeginningOfLine(key, arg));
+ }
+
+ ///
+ /// Paste text from the system clipboard.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void Paste(ConsoleKeyInfo? key = null, object arg = null)
+ {
+#if !CORECLR // TODO: clipboard
+ string textToPaste = null;
+ ExecuteOnSTAThread(() => {
+ if (Clipboard.ContainsText())
+ {
+ textToPaste = Clipboard.GetText();
+ }
+ });
+
+ if (textToPaste != null)
+ {
+ textToPaste = textToPaste.Replace("\r", "");
+ if (_singleton._visualSelectionCommandCount > 0)
+ {
+ int start, length;
+ _singleton.GetRegion(out start, out length);
+ Replace(start, length, textToPaste);
+ }
+ else
+ {
+ Insert(textToPaste);
+ }
+ }
+#endif
+ }
+
+ ///
+ /// Copy selected region to the system clipboard. If no region is selected, copy the whole line.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void Copy(ConsoleKeyInfo? key = null, object arg = null)
+ {
+#if !CORECLR // TODO: clipboard
+ string textToSet;
+ if (_singleton._visualSelectionCommandCount > 0)
+ {
+ int start, length;
+ _singleton.GetRegion(out start, out length);
+ textToSet = _singleton._buffer.ToString(start, length);
+ }
+ else
+ {
+ textToSet = _singleton._buffer.ToString();
+ }
+ if (!string.IsNullOrEmpty(textToSet))
+ {
+ ExecuteOnSTAThread(() => Clipboard.SetText(textToSet));
+ }
+#endif
+ }
+
+ ///
+ /// If text is selected, copy to the clipboard, otherwise cancel the line.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void CopyOrCancelLine(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ if (_singleton._visualSelectionCommandCount > 0)
+ {
+ Copy(key, arg);
+ }
+ else
+ {
+ CancelLine(key, arg);
+ }
+ }
+
+ ///
+ /// Delete selected region placing deleted text in the system clipboard.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void Cut(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ if (_singleton._visualSelectionCommandCount > 0)
+ {
+ int start, length;
+ _singleton.GetRegion(out start, out length);
+#if !CORECLR // TODO: clipboard
+ ExecuteOnSTAThread(() => Clipboard.SetText(_singleton._buffer.ToString(start, length)));
+#endif
+ Delete(start, length);
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.PowerShell.PSReadLine/License.txt b/src/Microsoft.PowerShell.PSReadLine/License.txt
new file mode 100644
index 00000000000..6e21fbbafe2
--- /dev/null
+++ b/src/Microsoft.PowerShell.PSReadLine/License.txt
@@ -0,0 +1,22 @@
+Copyright (c) 2013, Jason Shirk
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/src/Microsoft.PowerShell.PSReadLine/Microsoft.PowerShell.PSReadLine.csproj b/src/Microsoft.PowerShell.PSReadLine/Microsoft.PowerShell.PSReadLine.csproj
new file mode 100644
index 00000000000..cd14d6c2faa
--- /dev/null
+++ b/src/Microsoft.PowerShell.PSReadLine/Microsoft.PowerShell.PSReadLine.csproj
@@ -0,0 +1,28 @@
+
+
+
+ PowerShell Core's Microsoft.PowerShell.PSReadLine project
+ Microsoft.PowerShell.PSReadLine
+
+
+
+
+
+
+
+ $(DefineConstants);CORECLR
+
+
+
+ portable
+
+
+
+ $(DefineConstants);UNIX
+
+
+
+ full
+
+
+
diff --git a/src/Microsoft.PowerShell.PSReadLine/Movement.cs b/src/Microsoft.PowerShell.PSReadLine/Movement.cs
new file mode 100644
index 00000000000..b309b3245b5
--- /dev/null
+++ b/src/Microsoft.PowerShell.PSReadLine/Movement.cs
@@ -0,0 +1,550 @@
+/********************************************************************++
+Copyright (c) Microsoft Corporation. All rights reserved.
+--********************************************************************/
+
+using System;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Management.Automation.Language;
+using Microsoft.PowerShell.Internal;
+
+namespace Microsoft.PowerShell
+{
+ public partial class PSConsoleReadLine
+ {
+ private int _moveToLineCommandCount;
+ private int _moveToLineDesiredColumn;
+
+ ///
+ /// If the input has multiple lines, move to the end of the current line,
+ /// or if already at the end of the line, move to the end of the input.
+ /// If the input has a single line, move to the end of the input.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void EndOfLine(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ if (_singleton.LineIsMultiLine())
+ {
+ int i = _singleton._current;
+ for (; i < _singleton._buffer.Length; i++)
+ {
+ if (_singleton._buffer[i] == '\n')
+ {
+ break;
+ }
+ }
+
+ _singleton._current = (i == _singleton._current) ? _singleton._buffer.Length : i;
+ }
+ else
+ {
+ _singleton._current = _singleton._buffer.Length;
+ }
+ _singleton.PlaceCursor();
+ }
+
+ ///
+ /// If the input has multiple lines, move to the start of the current line,
+ /// or if already at the start of the line, move to the start of the input.
+ /// If the input has a single line, move to the start of the input.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void BeginningOfLine(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ if (_singleton.LineIsMultiLine())
+ {
+ int i = Math.Max(0, _singleton._current - 1);
+ for (; i > 0; i--)
+ {
+ if (_singleton._buffer[i] == '\n')
+ {
+ i += 1;
+ break;
+ }
+ }
+
+ _singleton._current = (i == _singleton._current) ? 0 : i;
+ }
+ else
+ {
+ _singleton._current = 0;
+ }
+ _singleton.PlaceCursor();
+ }
+
+ ///
+ /// Move the cursor one character to the right. This may move the cursor to the next
+ /// line of multi-line input.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void ForwardChar(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ int numericArg;
+ if (TryGetArgAsInt(arg, out numericArg, 1))
+ {
+ SetCursorPosition(_singleton._current + numericArg);
+ }
+ }
+
+ ///
+ /// Move the cursor one character to the left. This may move the cursor to the previous
+ /// line of multi-line input.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void BackwardChar(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ int numericArg;
+ if (TryGetArgAsInt(arg, out numericArg, 1))
+ {
+ SetCursorPosition(_singleton._current - numericArg);
+ }
+ }
+
+ private void MoveToLine(int numericArg)
+ {
+ const int endOfLine = int.MaxValue;
+
+ _moveToLineCommandCount += 1;
+ var coords = ConvertOffsetToCoordinates(_current);
+ if (_moveToLineCommandCount == 1)
+ {
+ _moveToLineDesiredColumn =
+ (_current == _buffer.Length || _buffer[_current] == '\n')
+ ? endOfLine
+ : coords.X;
+ }
+
+ var topLine = _initialY + Options.ExtraPromptLineCount;
+
+ var newY = coords.Y + numericArg;
+ coords.Y = (short)Math.Max(newY, topLine);
+ if (_moveToLineDesiredColumn != endOfLine)
+ {
+ coords.X = (short)_moveToLineDesiredColumn;
+ }
+
+ var newCurrent = ConvertLineAndColumnToOffset(coords);
+ if (newCurrent != -1)
+ {
+ _current = newCurrent;
+ if (_moveToLineDesiredColumn == endOfLine)
+ {
+ while (_current < _buffer.Length && _buffer[_current] != '\n')
+ {
+ _current += 1;
+ }
+ }
+ PlaceCursor();
+ }
+ }
+
+ ///
+ /// Move the cursor to the previous line.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void PreviousLine(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ int numericArg;
+ if (TryGetArgAsInt(arg, out numericArg, 1))
+ {
+ _singleton.MoveToLine(-numericArg);
+ }
+ }
+
+ ///
+ /// Move the cursor to the next line.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void NextLine(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ int numericArg;
+ if (TryGetArgAsInt(arg, out numericArg, 1))
+ {
+ _singleton.MoveToLine(numericArg);
+ }
+ }
+
+ ///
+ /// Move the cursor forward to the start of the next word.
+ /// Word boundaries are defined by a configurable set of characters.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void NextWord(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ int numericArg;
+ if (!TryGetArgAsInt(arg, out numericArg, 1))
+ {
+ return;
+ }
+
+ if (numericArg < 0)
+ {
+ BackwardWord(key, -numericArg);
+ return;
+ }
+
+ while (numericArg-- > 0)
+ {
+ int i = _singleton.FindNextWordPoint(_singleton.Options.WordDelimiters);
+ _singleton._current = i;
+ _singleton.PlaceCursor();
+ }
+ }
+
+ ///
+ /// Move the cursor forward to the end of the current word, or if between words,
+ /// to the end of the next word. Word boundaries are defined by PowerShell tokens.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void ShellNextWord(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ int numericArg;
+ if (!TryGetArgAsInt(arg, out numericArg, 1))
+ {
+ return;
+ }
+
+ if (numericArg < 0)
+ {
+ ShellBackwardWord(key, -numericArg);
+ return;
+ }
+
+ while (numericArg-- > 0)
+ {
+ var token = _singleton.FindToken(_singleton._current, FindTokenMode.Next);
+
+ Debug.Assert(token != null, "We'll always find EOF");
+
+ _singleton._current = token.Kind == TokenKind.EndOfInput
+ ? _singleton._buffer.Length
+ : token.Extent.StartOffset;
+ _singleton.PlaceCursor();
+ }
+ }
+
+ ///
+ /// Move the cursor forward to the end of the current word, or if between words,
+ /// to the end of the next word. Word boundaries are defined by a configurable
+ /// set of characters.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void ForwardWord(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ int numericArg;
+ if (!TryGetArgAsInt(arg, out numericArg, 1))
+ {
+ return;
+ }
+
+ if (numericArg < 0)
+ {
+ BackwardWord(key, -numericArg);
+ return;
+ }
+
+ while (numericArg-- > 0)
+ {
+ int i = _singleton.FindForwardWordPoint(_singleton.Options.WordDelimiters);
+ _singleton._current = i;
+ _singleton.PlaceCursor();
+ }
+ }
+
+ ///
+ /// Move the cursor forward to the start of the next word.
+ /// Word boundaries are defined by PowerShell tokens.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void ShellForwardWord(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ int numericArg;
+ if (!TryGetArgAsInt(arg, out numericArg, 1))
+ {
+ return;
+ }
+
+ if (numericArg < 0)
+ {
+ ShellBackwardWord(key, -numericArg);
+ return;
+ }
+
+ while (numericArg-- > 0)
+ {
+ var token = _singleton.FindToken(_singleton._current, FindTokenMode.CurrentOrNext);
+
+ Debug.Assert(token != null, "We'll always find EOF");
+
+ _singleton._current = token.Kind == TokenKind.EndOfInput
+ ? _singleton._buffer.Length
+ : token.Extent.EndOffset;
+ _singleton.PlaceCursor();
+ }
+ }
+
+ private static bool CheckIsBound(Action action)
+ {
+ foreach (var entry in _singleton._dispatchTable)
+ {
+ if (entry.Value.Action == action)
+ return true;
+ }
+ return false;
+ }
+
+ ///
+ /// Move the cursor back to the start of the current word, or if between words,
+ /// the start of the previous word. Word boundaries are defined by a configurable
+ /// set of characters.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void BackwardWord(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ int numericArg;
+ if (!TryGetArgAsInt(arg, out numericArg, 1))
+ {
+ return;
+ }
+
+ if (numericArg < 0)
+ {
+ if (CheckIsBound(ForwardWord))
+ {
+ ForwardWord(key, -numericArg);
+ }
+ else
+ {
+ NextWord(key, -numericArg);
+ }
+ return;
+ }
+
+ while (numericArg-- > 0)
+ {
+ int i = _singleton.FindBackwardWordPoint(_singleton.Options.WordDelimiters);
+ _singleton._current = i;
+ _singleton.PlaceCursor();
+ }
+ }
+
+ ///
+ /// Move the cursor back to the start of the current word, or if between words,
+ /// the start of the previous word. Word boundaries are defined by PowerShell tokens.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void ShellBackwardWord(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ int numericArg;
+ if (!TryGetArgAsInt(arg, out numericArg, 1))
+ {
+ return;
+ }
+
+ if (numericArg < 0)
+ {
+ if (CheckIsBound(ShellForwardWord))
+ {
+ ShellForwardWord(key, -numericArg);
+ }
+ else
+ {
+ ShellNextWord(key, -numericArg);
+ }
+ return;
+ }
+
+ while (numericArg-- > 0)
+ {
+ var token = _singleton.FindToken(_singleton._current, FindTokenMode.Previous);
+
+ _singleton._current = (token != null) ? token.Extent.StartOffset : 0;
+ _singleton.PlaceCursor();
+ }
+ }
+
+ ///
+ /// Go to the matching brace, paren, or square bracket
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void GotoBrace(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ if (_singleton._current >= _singleton._buffer.Length)
+ {
+ Ding();
+ return;
+ }
+
+ _singleton.MaybeParseInput();
+
+ Token token = null;
+ var index = 0;
+ for (; index < _singleton._tokens.Length; index++)
+ {
+ token = _singleton._tokens[index];
+ if (token.Extent.StartOffset == _singleton._current)
+ break;
+ }
+
+ TokenKind toMatch;
+ int direction;
+ switch (token.Kind)
+ {
+ case TokenKind.LParen: toMatch = TokenKind.RParen; direction = 1; break;
+ case TokenKind.LCurly: toMatch = TokenKind.RCurly; direction = 1; break;
+ case TokenKind.LBracket: toMatch = TokenKind.RBracket; direction = 1; break;
+
+ case TokenKind.RParen: toMatch = TokenKind.LParen; direction = -1; break;
+ case TokenKind.RCurly: toMatch = TokenKind.LCurly; direction = -1; break;
+ case TokenKind.RBracket: toMatch = TokenKind.LBracket; direction = -1; break;
+
+ default:
+ // Nothing to match (don't match inside strings/comments)
+ Ding();
+ return;
+ }
+
+ var matchCount = 0;
+ var limit = (direction > 0) ? _singleton._tokens.Length - 1 : -1;
+ for (; index != limit; index += direction)
+ {
+ var t = _singleton._tokens[index];
+ if (t.Kind == token.Kind)
+ {
+ matchCount++;
+ }
+ else if (t.Kind == toMatch)
+ {
+ matchCount--;
+ if (matchCount == 0)
+ {
+ _singleton._current = t.Extent.StartOffset;
+ _singleton.PlaceCursor();
+ return;
+ }
+ }
+ }
+ Ding();
+ }
+
+ ///
+ /// Clear the screen and draw the current line at the top of the screen.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void ClearScreen(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ var console = _singleton._console;
+#if UNIX // TODO: this is not correct, it should only scroll
+ console.Clear();
+ _singleton._initialY = 0;
+ _singleton.Render();
+#else
+ if (_singleton._initialY + console.WindowHeight > console.BufferHeight)
+ {
+ var scrollCount = _singleton._initialY - console.WindowTop;
+ console.ScrollBuffer(scrollCount);
+ _singleton._initialY -= scrollCount;
+ console.SetCursorPosition(console.CursorLeft, console.CursorTop - scrollCount);
+ }
+ else
+ {
+ console.SetWindowPosition(0, _singleton._initialY);
+ }
+#endif
+ }
+
+ // Try to convert the arg to a char, return 0 for failure
+ private static char TryGetArgAsChar(object arg)
+ {
+ if (arg is char)
+ {
+ return (char)arg;
+ }
+
+ var s = arg as string;
+ if (s != null && s.Length == 1)
+ {
+ return s[0];
+ }
+
+ return (char)0;
+ }
+
+ ///
+ /// Read a character and search forward for the next occurence of that character.
+ /// If an argument is specified, search forward (or backward if negative) for the
+ /// nth occurence.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void CharacterSearch(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ int occurence = (arg is int) ? (int)arg : 1;
+ if (occurence < 0)
+ {
+ CharacterSearchBackward(key, -occurence);
+ return;
+ }
+
+ char toFind = TryGetArgAsChar(arg);
+ if (toFind == (char)0)
+ {
+ // Should we prompt?
+ toFind = ReadKey().KeyChar;
+ }
+ for (int i = _singleton._current + 1; i < _singleton._buffer.Length; i++)
+ {
+ if (_singleton._buffer[i] == toFind)
+ {
+ occurence -= 1;
+ if (occurence == 0)
+ {
+ _singleton._current = i;
+ _singleton.PlaceCursor();
+ break;
+ }
+ }
+ }
+ if (occurence > 0)
+ {
+ Ding();
+ }
+ }
+
+ ///
+ /// Read a character and search backward for the next occurence of that character.
+ /// If an argument is specified, search backward (or forward if negative) for the
+ /// nth occurence.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static void CharacterSearchBackward(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ int occurence = (arg is int) ? (int)arg : 1;
+ if (occurence < 0)
+ {
+ CharacterSearch(key, -occurence);
+ return;
+ }
+
+ char toFind = TryGetArgAsChar(arg);
+ if (toFind == (char)0)
+ {
+ // Should we prompt?
+ toFind = ReadKey().KeyChar;
+ }
+ for (int i = _singleton._current - 1; i >= 0; i--)
+ {
+ if (_singleton._buffer[i] == toFind)
+ {
+ occurence -= 1;
+ if (occurence == 0)
+ {
+ _singleton._current = i;
+ _singleton.PlaceCursor();
+ return;
+ }
+ }
+ }
+ Ding();
+ }
+ }
+}
diff --git a/src/Microsoft.PowerShell.PSReadLine/Movement.vi.cs b/src/Microsoft.PowerShell.PSReadLine/Movement.vi.cs
new file mode 100644
index 00000000000..7c1c8a7bf21
--- /dev/null
+++ b/src/Microsoft.PowerShell.PSReadLine/Movement.vi.cs
@@ -0,0 +1,326 @@
+/********************************************************************++
+Copyright (c) Microsoft Corporation. All rights reserved.
+--********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Microsoft.PowerShell
+{
+ public partial class PSConsoleReadLine
+ {
+ ///
+ /// Move the cursor forward to the start of the next word.
+ /// Word boundaries are defined by a configurable set of characters.
+ ///
+ public static void ViNextWord(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ int numericArg;
+ if (!TryGetArgAsInt(arg, out numericArg, 1))
+ {
+ return;
+ }
+
+ if (numericArg < 0)
+ {
+ ViBackwardWord(key, -numericArg);
+ return;
+ }
+
+ while (numericArg-- > 0)
+ {
+ int i = _singleton.ViFindNextWordPoint(_singleton.Options.WordDelimiters);
+ if (i >= _singleton._buffer.Length)
+ {
+ i += ViEndOfLineFactor;
+ }
+ _singleton._current = i;
+ _singleton.PlaceCursor();
+ }
+ }
+
+ ///
+ /// Move the cursor back to the start of the current word, or if between words,
+ /// the start of the previous word. Word boundaries are defined by a configurable
+ /// set of characters.
+ ///
+ public static void ViBackwardWord(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ int numericArg;
+ if (!TryGetArgAsInt(arg, out numericArg, 1))
+ {
+ return;
+ }
+
+ if (numericArg < 0)
+ {
+ ViNextWord(key, -numericArg);
+ return;
+ }
+
+ while (numericArg-- > 0)
+ {
+ int i = _singleton.ViFindPreviousWordPoint(_singleton.Options.WordDelimiters);
+ _singleton._current = i;
+ _singleton.PlaceCursor();
+ }
+ }
+
+ ///
+ /// Moves the cursor back to the beginning of the previous word, using only white space as delimiters.
+ ///
+ public static void ViBackwardGlob(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ int numericArg;
+ if (!TryGetArgAsInt(arg, out numericArg, 1))
+ {
+ return;
+ }
+
+ int i = _singleton._current;
+ while (numericArg-- > 0)
+ {
+ i = _singleton.ViFindPreviousGlob(i - 1);
+ }
+ _singleton._current = i;
+ _singleton.PlaceCursor();
+ }
+
+ ///
+ /// Moves to the next word, using only white space as a word delimiter.
+ ///
+ private static void ViNextGlob(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ int numericArg;
+ if (!TryGetArgAsInt(arg, out numericArg, 1))
+ {
+ return;
+ }
+
+ int i = _singleton._current;
+ while (numericArg-- > 0)
+ {
+ i = _singleton.ViFindNextGlob(i);
+ }
+
+ _singleton._current = Math.Min(i, _singleton._buffer.Length - 1);
+ _singleton.PlaceCursor();
+ }
+
+ private static void ViEndOfGlob(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ int numericArg;
+ if (!TryGetArgAsInt(arg, out numericArg, 1))
+ {
+ return;
+ }
+
+ if (numericArg < 0)
+ {
+ ViEndOfPreviousGlob(key, -numericArg);
+ return;
+ }
+
+ while (numericArg-- > 0)
+ {
+ int i = _singleton.ViFindEndOfGlob();
+ _singleton._current = i;
+ _singleton.PlaceCursor();
+ }
+ }
+
+ private static void ViEndOfPreviousGlob(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ int numericArg;
+ if (!TryGetArgAsInt(arg, out numericArg, 1))
+ {
+ return;
+ }
+
+ if (numericArg < 0)
+ {
+ ViEndOfGlob(key, -numericArg);
+ return;
+ }
+
+ while (numericArg-- > 0)
+ {
+ int i = _singleton.ViFindEndOfPreviousGlob();
+ _singleton._current = i;
+ _singleton.PlaceCursor();
+ }
+ }
+
+ ///
+ /// Returns 0 if the cursor is allowed to go past the last character in the line, -1 otherwise.
+ ///
+ ///
+ private static int ViEndOfLineFactor
+ {
+ get
+ {
+ if (_singleton._dispatchTable == _viCmdKeyMap)
+ {
+ return -1;
+ }
+ return 0;
+ }
+ }
+
+ ///
+ /// Move the cursor to the end of the input.
+ ///
+ public static void MoveToEndOfLine(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ _singleton._current = Math.Max(0, _singleton._buffer.Length + ViEndOfLineFactor);
+ _singleton.PlaceCursor();
+ }
+
+ ///
+ /// Move the cursor forward to the end of the current word, or if between words,
+ /// to the end of the next word. Word boundaries are defined by a configurable
+ /// set of characters.
+ ///
+ public static void NextWordEnd(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ int qty = (arg is int) ? (int)arg : 1;
+ for (; qty > 0 && _singleton._current < _singleton._buffer.Length - 1; qty--)
+ {
+ int i = _singleton.ViFindNextWordEnd(_singleton.Options.WordDelimiters);
+ _singleton._current = i;
+ _singleton.PlaceCursor();
+ }
+ }
+
+ ///
+ /// Move to the column indicated by arg.
+ ///
+ public static void GotoColumn(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ int col = (arg is int) ? (int) arg : -1;
+ if (col < 0 ) {
+ Ding();
+ return;
+ }
+
+ if (col < _singleton._buffer.Length + ViEndOfLineFactor)
+ {
+ _singleton._current = Math.Min(col, _singleton._buffer.Length) - 1;
+ }
+ else
+ {
+ _singleton._current = _singleton._buffer.Length + ViEndOfLineFactor;
+ Ding();
+ }
+ _singleton.PlaceCursor();
+ }
+
+ ///
+ /// Move the cursor to the first non-blank character in the line.
+ ///
+ public static void GotoFirstNonBlankOfLine(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ for (int i = 0; i < _singleton._buffer.Length; i++)
+ {
+ if (!Char.IsWhiteSpace(_singleton._buffer[i]))
+ {
+ _singleton._current = i;
+ _singleton.PlaceCursor();
+ return;
+ }
+ }
+ }
+
+ ///
+ /// Similar to , but is character based instead of token based.
+ ///
+ public static void ViGotoBrace(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ int i = _singleton.ViFindBrace(_singleton._current);
+ if (i == _singleton._current)
+ {
+ Ding();
+ return;
+ }
+ _singleton._current = i;
+ _singleton.PlaceCursor();
+ }
+
+ private int ViFindBrace(int i)
+ {
+ switch (_buffer[i])
+ {
+ case '{':
+ return ViFindForward(i, '}', withoutPassing: '{');
+ case '[':
+ return ViFindForward(i, ']', withoutPassing: '[');
+ case '(':
+ return ViFindForward(i, ')', withoutPassing: '(');
+ case '}':
+ return ViFindBackward(i, '{', withoutPassing: '}');
+ case ']':
+ return ViFindBackward(i, '[', withoutPassing: ']');
+ case ')':
+ return ViFindBackward(i, '(', withoutPassing: ')');
+ default:
+ return i;
+ }
+ }
+
+ private int ViFindBackward(int start, char target, char withoutPassing)
+ {
+ if (start == 0)
+ {
+ return start;
+ }
+ int i = start - 1;
+ int withoutPassingCount = 0;
+ while (i != 0 && !(_buffer[i] == target && withoutPassingCount == 0))
+ {
+ if (_buffer[i] == withoutPassing)
+ {
+ withoutPassingCount++;
+ }
+ if (_buffer[i] == target)
+ {
+ withoutPassingCount--;
+ }
+ i--;
+ }
+ if (_buffer[i] == target && withoutPassingCount == 0)
+ {
+ return i;
+ }
+ return start;
+ }
+
+ private int ViFindForward(int start, char target, char withoutPassing)
+ {
+ if (IsAtEndOfLine(start))
+ {
+ return start;
+ }
+ int i = start + 1;
+ int withoutPassingCount = 0;
+ while (!IsAtEndOfLine(i) && !(_buffer[i] == target && withoutPassingCount == 0))
+ {
+ if (_buffer[i] == withoutPassing)
+ {
+ withoutPassingCount++;
+ }
+ if (_buffer[i] == target)
+ {
+ withoutPassingCount--;
+ }
+ i++;
+ }
+ if (_buffer[i] == target && withoutPassingCount == 0)
+ {
+ return i;
+ }
+ return start;
+ }
+ }
+}
diff --git a/src/Microsoft.PowerShell.PSReadLine/Options.cs b/src/Microsoft.PowerShell.PSReadLine/Options.cs
new file mode 100644
index 00000000000..63b440f145a
--- /dev/null
+++ b/src/Microsoft.PowerShell.PSReadLine/Options.cs
@@ -0,0 +1,403 @@
+/********************************************************************++
+Copyright (c) Microsoft Corporation. All rights reserved.
+--********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Management.Automation;
+using System.Reflection;
+using System.Threading;
+using Microsoft.PowerShell.Internal;
+
+namespace Microsoft.PowerShell
+{
+ public partial class PSConsoleReadLine
+ {
+ private readonly PSConsoleReadlineOptions _options;
+ private PSConsoleReadlineOptions Options
+ {
+ get { return _options; }
+ }
+
+ private void SetOptionsInternal(SetPSReadlineOption options)
+ {
+ if (options.ContinuationPrompt != null)
+ {
+ Options.ContinuationPrompt = options.ContinuationPrompt;
+ }
+ if (options._continuationPromptForegroundColor.HasValue)
+ {
+ Options.ContinuationPromptForegroundColor = options.ContinuationPromptForegroundColor;
+ }
+ if (options._continuationPromptBackgroundColor.HasValue)
+ {
+ Options.ContinuationPromptBackgroundColor = options.ContinuationPromptBackgroundColor;
+ }
+ if (options._emphasisBackgroundColor.HasValue)
+ {
+ Options.EmphasisBackgroundColor = options.EmphasisBackgroundColor;
+ }
+ if (options._emphasisForegroundColor.HasValue)
+ {
+ Options.EmphasisForegroundColor = options.EmphasisForegroundColor;
+ }
+ if (options._errorBackgroundColor.HasValue)
+ {
+ Options.ErrorBackgroundColor = options.ErrorBackgroundColor;
+ }
+ if (options._errorForegroundColor.HasValue)
+ {
+ Options.ErrorForegroundColor = options.ErrorForegroundColor;
+ }
+ if (options._historyNoDuplicates.HasValue)
+ {
+ Options.HistoryNoDuplicates = options.HistoryNoDuplicates;
+ }
+ if (options._historySearchCursorMovesToEnd.HasValue)
+ {
+ Options.HistorySearchCursorMovesToEnd = options.HistorySearchCursorMovesToEnd;
+ }
+ if (options._addToHistoryHandlerSpecified)
+ {
+ Options.AddToHistoryHandler = options.AddToHistoryHandler;
+ }
+ if (options._commandValidationHandlerSpecified)
+ {
+ Options.CommandValidationHandler = options.CommandValidationHandler;
+ }
+ if (options._maximumHistoryCount.HasValue)
+ {
+ Options.MaximumHistoryCount = options.MaximumHistoryCount;
+ if (_history != null)
+ {
+ var newHistory = new HistoryQueue(Options.MaximumHistoryCount);
+ while (_history.Count > Options.MaximumHistoryCount)
+ {
+ _history.Dequeue();
+ }
+ while (_history.Count > 0)
+ {
+ newHistory.Enqueue(_history.Dequeue());
+ }
+ _history = newHistory;
+ _currentHistoryIndex = _history.Count;
+ }
+ }
+ if (options._maximumKillRingCount.HasValue)
+ {
+ Options.MaximumKillRingCount = options.MaximumKillRingCount;
+ // TODO - make _killRing smaller
+ }
+ if (options._editMode.HasValue)
+ {
+ Options.EditMode = options.EditMode;
+
+ // Switching/resetting modes - clear out chord dispatch table
+ _chordDispatchTable.Clear();
+
+ SetDefaultBindings(Options.EditMode);
+ }
+ if (options._showToolTips.HasValue)
+ {
+ Options.ShowToolTips = options.ShowToolTips;
+ }
+ if (options._extraPromptLineCount.HasValue)
+ {
+ Options.ExtraPromptLineCount = options.ExtraPromptLineCount;
+ }
+ if (options._dingTone.HasValue)
+ {
+ Options.DingTone = options.DingTone;
+ }
+ if (options._dingDuration.HasValue)
+ {
+ Options.DingDuration = options.DingDuration;
+ }
+ if (options._bellStyle.HasValue)
+ {
+ Options.BellStyle = options.BellStyle;
+ }
+ if (options._completionQueryItems.HasValue)
+ {
+ Options.CompletionQueryItems = options.CompletionQueryItems;
+ }
+ if (options.WordDelimiters != null)
+ {
+ Options.WordDelimiters = options.WordDelimiters;
+ }
+ if (options._historySearchCaseSensitive.HasValue)
+ {
+ Options.HistorySearchCaseSensitive = options.HistorySearchCaseSensitive;
+ }
+ if (options._historySaveStyle.HasValue)
+ {
+ Options.HistorySaveStyle = options.HistorySaveStyle;
+ }
+ #region vi
+ if (options._viModeIndicator.HasValue)
+ {
+ Options.ViModeIndicator = options.ViModeIndicator;
+ }
+ #endregion
+ if (options.HistorySavePath != null)
+ {
+ Options.HistorySavePath = options.HistorySavePath;
+ if (_historyFileMutex != null)
+ {
+ _historyFileMutex.Dispose();
+ }
+ _historyFileMutex = new Mutex(false, GetHistorySaveFileMutexName());
+ _historyFileLastSavedSize = 0;
+ }
+ if (options.ResetTokenColors)
+ {
+ Options.ResetColors();
+ }
+ if (options._tokenKind.HasValue)
+ {
+ if (options._foregroundColor.HasValue)
+ {
+ Options.SetForegroundColor(options.TokenKind, options.ForegroundColor);
+ }
+ if (options._backgroundColor.HasValue)
+ {
+ Options.SetBackgroundColor(options.TokenKind, options.BackgroundColor);
+ }
+ }
+ }
+
+ private void SetKeyHandlerInternal(string[] keys, Action handler, string briefDescription, string longDescription, ScriptBlock scriptBlock)
+ {
+ foreach (var key in keys)
+ {
+ var chord = ConsoleKeyChordConverter.Convert(key);
+ if (chord.Length == 1)
+ {
+ _dispatchTable[chord[0]] = MakeKeyHandler(handler, briefDescription, longDescription, scriptBlock);
+ }
+ else
+ {
+ _dispatchTable[chord[0]] = MakeKeyHandler(Chord, "ChordFirstKey");
+ Dictionary secondDispatchTable;
+ if (!_chordDispatchTable.TryGetValue(chord[0], out secondDispatchTable))
+ {
+ secondDispatchTable = new Dictionary();
+ _chordDispatchTable[chord[0]] = secondDispatchTable;
+ }
+ secondDispatchTable[chord[1]] = MakeKeyHandler(handler, briefDescription, longDescription, scriptBlock);
+ }
+ }
+ }
+
+ private void RemoveKeyHandlerInternal(string[] keys)
+ {
+ foreach (var key in keys)
+ {
+ var chord = ConsoleKeyChordConverter.Convert(key);
+ if (chord.Length == 1)
+ {
+ _dispatchTable.Remove(chord[0]);
+ }
+ else
+ {
+ Dictionary secondDispatchTable;
+ if (_chordDispatchTable.TryGetValue(chord[0], out secondDispatchTable))
+ {
+ secondDispatchTable.Remove(chord[1]);
+ if (secondDispatchTable.Count == 0)
+ {
+ _dispatchTable.Remove(chord[0]);
+ }
+ }
+ }
+ }
+ }
+
+ ///
+ /// Helper function for the Set-PSReadlineOption cmdlet.
+ ///
+ public static void SetOptions(SetPSReadlineOption options)
+ {
+ _singleton.SetOptionsInternal(options);
+ }
+
+ ///
+ /// Helper function for the Get-PSReadlineOption cmdlet.
+ ///
+ public static PSConsoleReadlineOptions GetOptions()
+ {
+ // Should we copy? It doesn't matter much, everything can be tweaked from
+ // the cmdlet anyway.
+ return _singleton._options;
+ }
+
+ class CustomHandlerException : Exception
+ {
+ internal CustomHandlerException(Exception innerException)
+ : base("", innerException)
+ {
+ }
+ }
+
+ ///
+ /// Helper function for the Set-PSReadlineKeyHandler cmdlet.
+ ///
+ public static void SetKeyHandler(string[] key, ScriptBlock scriptBlock, string briefDescription, string longDescription)
+ {
+ Action handler = (k, arg) =>
+ {
+ try
+ {
+ scriptBlock.Invoke(k, arg);
+ }
+ catch (Exception e)
+ {
+ throw new CustomHandlerException(e);
+ }
+ };
+
+ _singleton.SetKeyHandlerInternal(key, handler, briefDescription, longDescription, scriptBlock);
+ }
+
+ ///
+ /// Helper function for the Set-PSReadlineKeyHandler cmdlet.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
+ public static void SetKeyHandler(string[] key, Action handler, string briefDescription, string longDescription)
+ {
+ _singleton.SetKeyHandlerInternal(key, handler, briefDescription, longDescription, null);
+ }
+
+ ///
+ /// Helper function for the Remove-PSReadlineKeyHandler cmdlet.
+ ///
+ ///
+ public static void RemoveKeyHandler(string[] key)
+ {
+ _singleton.RemoveKeyHandlerInternal(key);
+ }
+
+ ///
+ /// Helper function for the Get-PSReadlineKeyHandler cmdlet.
+ ///
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
+ public static IEnumerable GetKeyHandlers(bool includeBound = true, bool includeUnbound = false)
+ {
+ var boundFunctions = new HashSet(StringComparer.OrdinalIgnoreCase);
+
+ foreach (var entry in _singleton._dispatchTable)
+ {
+ if (entry.Value.BriefDescription == "Ignore"
+ || entry.Value.BriefDescription == "ChordFirstKey")
+ {
+ continue;
+ }
+ boundFunctions.Add(entry.Value.BriefDescription);
+ if (includeBound)
+ {
+ yield return new Microsoft.PowerShell.KeyHandler
+ {
+ Key = entry.Key.ToGestureString(),
+ Function = entry.Value.BriefDescription,
+ Description = entry.Value.LongDescription,
+ };
+ }
+ }
+
+ // Added to support vi command mode mappings
+ if (_singleton._options.EditMode == EditMode.Vi)
+ {
+ foreach (var entry in _viCmdKeyMap)
+ {
+ if (entry.Value.BriefDescription == "Ignore"
+ || entry.Value.BriefDescription == "ChordFirstKey")
+ {
+ continue;
+ }
+ boundFunctions.Add(entry.Value.BriefDescription);
+ if (includeBound)
+ {
+ yield return new Microsoft.PowerShell.KeyHandler
+ {
+ Key = "<" + entry.Key.ToGestureString() + ">",
+ Function = entry.Value.BriefDescription,
+ Description = entry.Value.LongDescription,
+ };
+ }
+ }
+ }
+
+ foreach( var entry in _singleton._chordDispatchTable )
+ {
+ foreach( var secondEntry in entry.Value )
+ {
+ boundFunctions.Add( secondEntry.Value.BriefDescription );
+ if (includeBound)
+ {
+ yield return new Microsoft.PowerShell.KeyHandler
+ {
+ Key = entry.Key.ToGestureString() + "," + secondEntry.Key.ToGestureString(),
+ Function = secondEntry.Value.BriefDescription,
+ Description = secondEntry.Value.LongDescription,
+ };
+ }
+ }
+ }
+
+ // Added to support vi command mode chorded mappings
+ if (_singleton._options.EditMode == EditMode.Vi)
+ {
+ foreach (var entry in _viCmdChordTable)
+ {
+ foreach (var secondEntry in entry.Value)
+ {
+ if (secondEntry.Value.BriefDescription == "Ignore")
+ {
+ continue;
+ }
+ boundFunctions.Add(secondEntry.Value.BriefDescription);
+ if (includeBound)
+ {
+ yield return new Microsoft.PowerShell.KeyHandler
+ {
+ Key = "<" + entry.Key.ToGestureString() + "," + secondEntry.Key.ToGestureString() + ">",
+ Function = secondEntry.Value.BriefDescription,
+ Description = secondEntry.Value.LongDescription
+ };
+ }
+ }
+ }
+ }
+
+ if (includeUnbound)
+ {
+ // SelfInsert isn't really unbound, but we don't want UI to show it that way
+ boundFunctions.Add("SelfInsert");
+
+ var methods = typeof (PSConsoleReadLine).GetMethods(BindingFlags.Public | BindingFlags.Static);
+ foreach (var method in methods)
+ {
+ var parameters = method.GetParameters();
+ if (parameters.Length != 2 ||
+ parameters[0].ParameterType != typeof (ConsoleKeyInfo?) ||
+ parameters[1].ParameterType != typeof (object))
+ {
+ continue;
+ }
+
+ if (!boundFunctions.Contains(method.Name))
+ {
+ yield return new Microsoft.PowerShell.KeyHandler
+ {
+ Key = "Unbound",
+ Function = method.Name,
+ Description = null,
+ };
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.PowerShell.PSReadLine/PSReadLine.psd1 b/src/Microsoft.PowerShell.PSReadLine/PSReadLine.psd1
new file mode 100644
index 00000000000..a634495d19d
--- /dev/null
+++ b/src/Microsoft.PowerShell.PSReadLine/PSReadLine.psd1
@@ -0,0 +1,17 @@
+@{
+RootModule = 'PSReadLine.psm1'
+NestedModules = @("Microsoft.PowerShell.PSReadLine.dll")
+ModuleVersion = '1.2'
+GUID = '5714753b-2afd-4492-a5fd-01d9e2cff8b5'
+Author = 'Microsoft Corporation'
+CompanyName = 'Microsoft Corporation'
+Copyright = 'Copyright (c) Microsoft Corporation. All rights reserved.'
+Description = 'Great command line editing in the PowerShell console host'
+PowerShellVersion = '3.0'
+DotNetFrameworkVersion = '4.0'
+CLRVersion = '4.0'
+FunctionsToExport = 'PSConsoleHostReadline'
+CmdletsToExport = 'Get-PSReadlineKeyHandler','Set-PSReadlineKeyHandler','Remove-PSReadlineKeyHandler',
+ 'Get-PSReadlineOption','Set-PSReadlineOption'
+HelpInfoURI = 'https://go.microsoft.com/fwlink/?linkid=855966'
+}
diff --git a/src/Microsoft.PowerShell.PSReadLine/PSReadLine.psm1 b/src/Microsoft.PowerShell.PSReadLine/PSReadLine.psm1
new file mode 100644
index 00000000000..4f2bf37fe89
--- /dev/null
+++ b/src/Microsoft.PowerShell.PSReadLine/PSReadLine.psm1
@@ -0,0 +1,5 @@
+function PSConsoleHostReadline
+{
+ Microsoft.PowerShell.Core\Set-StrictMode -Off
+ [Microsoft.PowerShell.PSConsoleReadLine]::ReadLine($host.Runspace, $ExecutionContext)
+}
diff --git a/src/Microsoft.PowerShell.PSReadLine/PSReadLineResources.Designer.cs b/src/Microsoft.PowerShell.PSReadLine/PSReadLineResources.Designer.cs
new file mode 100644
index 00000000000..c96e5f059da
--- /dev/null
+++ b/src/Microsoft.PowerShell.PSReadLine/PSReadLineResources.Designer.cs
@@ -0,0 +1,1839 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace Microsoft.PowerShell {
+ using System;
+ using System.Reflection;
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class PSReadLineResources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal PSReadLineResources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.PowerShell.PSReadLine.PSReadLineResources", typeof(PSReadLineResources).GetTypeInfo().Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Abort the current operation, e.g. incremental history search.
+ ///
+ internal static string AbortDescription {
+ get {
+ return ResourceManager.GetString("AbortDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Accept the current line and recall the next line from history after the current line finishes executing.
+ ///
+ internal static string AcceptAndGetNextDescription {
+ get {
+ return ResourceManager.GetString("AcceptAndGetNextDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Accept the input or move to the next line if input is missing a closing token..
+ ///
+ internal static string AcceptLineDescription {
+ get {
+ return ResourceManager.GetString("AcceptLineDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Move the cursor to the next line without attempting to execute the input.
+ ///
+ internal static string AddLineDescription {
+ get {
+ return ResourceManager.GetString("AddLineDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Move the cursor back one character.
+ ///
+ internal static string BackwardCharDescription {
+ get {
+ return ResourceManager.GetString("BackwardCharDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Delete the character before the cursor.
+ ///
+ internal static string BackwardDeleteCharDescription {
+ get {
+ return ResourceManager.GetString("BackwardDeleteCharDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Delete text from the cursor to the start of the line.
+ ///
+ internal static string BackwardDeleteLineDescription {
+ get {
+ return ResourceManager.GetString("BackwardDeleteLineDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Delete the previous word in the line..
+ ///
+ internal static string BackwardDeleteWordDescription {
+ get {
+ return ResourceManager.GetString("BackwardDeleteWordDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Move the text from the cursor to the beginning of the line to the kill ring.
+ ///
+ internal static string BackwardKillLineDescription {
+ get {
+ return ResourceManager.GetString("BackwardKillLineDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Move the text from the start of the current or previous word to the cursor to the kill ring.
+ ///
+ internal static string BackwardKillWordDescription {
+ get {
+ return ResourceManager.GetString("BackwardKillWordDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Replaces the character in front of the cursor..
+ ///
+ internal static string BackwardReplaceCharDescription {
+ get {
+ return ResourceManager.GetString("BackwardReplaceCharDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Move the cursor to the beginning of the current or previous word.
+ ///
+ internal static string BackwardWordDescription {
+ get {
+ return ResourceManager.GetString("BackwardWordDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Move to the first item in the history.
+ ///
+ internal static string BeginningOfHistoryDescription {
+ get {
+ return ResourceManager.GetString("BeginningOfHistoryDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Move the cursor to the beginning of the line.
+ ///
+ internal static string BeginningOfLineDescription {
+ get {
+ return ResourceManager.GetString("BeginningOfLineDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Abort editing the current line and re-evaluate the prompt.
+ ///
+ internal static string CancelLineDescription {
+ get {
+ return ResourceManager.GetString("CancelLineDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Unable to translate '{0}' to virtual key code: {1}..
+ ///
+ internal static string CantTranslateKey {
+ get {
+ return ResourceManager.GetString("CantTranslateKey", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Allows you to select multiple lines from the console using Shift+UpArrow/DownArrow and copy the selected lines to clipboard by pressing Enter..
+ ///
+ internal static string CaptureScreenDescription {
+ get {
+ return ResourceManager.GetString("CaptureScreenDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Read a character and move the cursor to the previous occurence of that character.
+ ///
+ internal static string CharacterSearchBackwardDescription {
+ get {
+ return ResourceManager.GetString("CharacterSearchBackwardDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Read a character and move the cursor to the next occurence of that character.
+ ///
+ internal static string CharacterSearchDescription {
+ get {
+ return ResourceManager.GetString("CharacterSearchDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Chord can have at most two keys..
+ ///
+ internal static string ChordWithTooManyKeys {
+ get {
+ return ResourceManager.GetString("ChordWithTooManyKeys", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Remove all items from the command line history (not PowerShell history).
+ ///
+ internal static string ClearHistoryDescription {
+ get {
+ return ResourceManager.GetString("ClearHistoryDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Remove all items from the kill ring.
+ ///
+ internal static string ClearKillRingDescription {
+ get {
+ return ResourceManager.GetString("ClearKillRingDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Clear the screen and redraw the current line at the top of the screen.
+ ///
+ internal static string ClearScreenDescription {
+ get {
+ return ResourceManager.GetString("ClearScreenDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Command '{0}' cannot be found..
+ ///
+ internal static string CommandNotFoundError {
+ get {
+ return ResourceManager.GetString("CommandNotFoundError", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Complete the input if there is a single completion, otherwise complete the input with common prefix for all completions. Show possible completions if pressed a second time..
+ ///
+ internal static string CompleteDescription {
+ get {
+ return ResourceManager.GetString("CompleteDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Copy selected region to the system clipboard. If no region is selected, copy the whole line.
+ ///
+ internal static string CopyDescription {
+ get {
+ return ResourceManager.GetString("CopyDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Either copy selected text to the clipboard, or if no text is selected, cancel editing the line with CancelLine..
+ ///
+ internal static string CopyOrCancelLineDescription {
+ get {
+ return ResourceManager.GetString("CopyOrCancelLineDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Delete selected region placing deleted text in the system clipboard.
+ ///
+ internal static string CutDescription {
+ get {
+ return ResourceManager.GetString("CutDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Deletes all characters between the cursor and the matching brace..
+ ///
+ internal static string DeleteBraceDescription {
+ get {
+ return ResourceManager.GetString("DeleteBraceDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Delete the character under the cursor.
+ ///
+ internal static string DeleteCharDescription {
+ get {
+ return ResourceManager.GetString("DeleteCharDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Delete the character under the cursor, or if the line is empty, exit the process..
+ ///
+ internal static string DeleteCharOrExitDescription {
+ get {
+ return ResourceManager.GetString("DeleteCharOrExitDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Delete to the end of the current word, as delimited by white space and common delimiters..
+ ///
+ internal static string DeleteEndOfWordDescription {
+ get {
+ return ResourceManager.GetString("DeleteEndOfWordDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Deletes the current line..
+ ///
+ internal static string DeleteLineDescription {
+ get {
+ return ResourceManager.GetString("DeleteLineDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Deletes all of the line except for leading whitespace..
+ ///
+ internal static string DeleteLineToFirstCharDescription {
+ get {
+ return ResourceManager.GetString("DeleteLineToFirstCharDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Deletes from the cursor to the end of the line..
+ ///
+ internal static string DeleteToEndDescription {
+ get {
+ return ResourceManager.GetString("DeleteToEndDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Deletes from the cursor to the end of the current word..
+ ///
+ internal static string DeleteToEndOfWordDescription {
+ get {
+ return ResourceManager.GetString("DeleteToEndOfWordDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Deletes the current word..
+ ///
+ internal static string DeleteWordDescription {
+ get {
+ return ResourceManager.GetString("DeleteWordDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Start or accumulate a numeric argument to other functions.
+ ///
+ internal static string DigitArgumentDescription {
+ get {
+ return ResourceManager.GetString("DigitArgumentDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Display all {0} possibilities? (y or n) _.
+ ///
+ internal static string DisplayAllPossibilities {
+ get {
+ return ResourceManager.GetString("DisplayAllPossibilities", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to String is not used in the UI.
+ ///
+ internal static string EmacsCtrlXDescription {
+ get {
+ return ResourceManager.GetString("EmacsCtrlXDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to String is not used in the UI.
+ ///
+ internal static string EmacsMetaDescription {
+ get {
+ return ResourceManager.GetString("EmacsMetaDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Move to the last item (the current input) in the history.
+ ///
+ internal static string EndOfHistoryDescription {
+ get {
+ return ResourceManager.GetString("EndOfHistoryDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Move the cursor to the end of the line.
+ ///
+ internal static string EndOfLineDescription {
+ get {
+ return ResourceManager.GetString("EndOfLineDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Mark the location of the cursor and move the cursor to the position of the previous mark.
+ ///
+ internal static string ExchangePointAndMarkDescription {
+ get {
+ return ResourceManager.GetString("ExchangePointAndMarkDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Move the cursor forward one character.
+ ///
+ internal static string ForwardCharDescription {
+ get {
+ return ResourceManager.GetString("ForwardCharDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Delete text from the cursor to the end of the line.
+ ///
+ internal static string ForwardDeleteLineDescription {
+ get {
+ return ResourceManager.GetString("ForwardDeleteLineDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Search history forward interactively.
+ ///
+ internal static string ForwardSearchHistoryDescription {
+ get {
+ return ResourceManager.GetString("ForwardSearchHistoryDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Move the cursor forward to the end of the current word, or if between words, to the end of the next word. .
+ ///
+ internal static string ForwardWordDescription {
+ get {
+ return ResourceManager.GetString("ForwardWordDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Go to matching brace.
+ ///
+ internal static string GotoBraceDescription {
+ get {
+ return ResourceManager.GetString("GotoBraceDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Moves the cursor to the prescribed column..
+ ///
+ internal static string GotoColumnDescription {
+ get {
+ return ResourceManager.GetString("GotoColumnDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Positions the cursor at the first non-blank character..
+ ///
+ internal static string GotoFirstNonBlankOfLineDescription {
+ get {
+ return ResourceManager.GetString("GotoFirstNonBlankOfLineDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to This error will not be reported again in this session. Consider using a different path with:
+ /// Set-PSReadlineOption -HistorySavePath <Path>
+ ///Or not saving history with:
+ /// Set-PSReadlineOption -HistorySaveStyle SaveNothing.
+ ///
+ internal static string HistoryFileErrorFinalMessage {
+ get {
+ return ResourceManager.GetString("HistoryFileErrorFinalMessage", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Error reading or writing history file '{0}': {1}.
+ ///
+ internal static string HistoryFileErrorMessage {
+ get {
+ return ResourceManager.GetString("HistoryFileErrorMessage", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Search for the previous item in the history that starts with the current input - like PreviousHistory if the input is empty.
+ ///
+ internal static string HistorySearchBackwardDescription {
+ get {
+ return ResourceManager.GetString("HistorySearchBackwardDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Search for the next item in the history that starts with the current input - like NextHistory if the input is empty.
+ ///
+ internal static string HistorySearchForwardDescription {
+ get {
+ return ResourceManager.GetString("HistorySearchForwardDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to String is not used in the UI.
+ ///
+ internal static string IgnoreDescription {
+ get {
+ return ResourceManager.GetString("IgnoreDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Delete the current character and insert the next character..
+ ///
+ internal static string InsertCharacterDescription {
+ get {
+ return ResourceManager.GetString("InsertCharacterDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Inserts a new empty line above the current line without attempting to execute the input.
+ ///
+ internal static string InsertLineAboveDescription {
+ get {
+ return ResourceManager.GetString("InsertLineAboveDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Inserts a new empty line below the current line without attempting to execute the input.
+ ///
+ internal static string InsertLineBelowDescription {
+ get {
+ return ResourceManager.GetString("InsertLineBelowDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Duplicate or invalid modifier token '{0}' for key '{1}'..
+ ///
+ internal static string InvalidModifier {
+ get {
+ return ResourceManager.GetString("InvalidModifier", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Invalid sequence '{0}'..
+ ///
+ internal static string InvalidSequence {
+ get {
+ return ResourceManager.GetString("InvalidSequence", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Inverts the case of the current character and advances the cursor..
+ ///
+ internal static string InvertCaseDescription {
+ get {
+ return ResourceManager.GetString("InvertCaseDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Erases the current prompt and calls the prompt function to redisplay the prompt.
+ ///
+ internal static string InvokePromptDescription {
+ get {
+ return ResourceManager.GetString("InvokePromptDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Key is unbound.
+ ///
+ internal static string KeyIsUnbound {
+ get {
+ return ResourceManager.GetString("KeyIsUnbound", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Move the text from the cursor to the end of the input to the kill ring.
+ ///
+ internal static string KillLineDescription {
+ get {
+ return ResourceManager.GetString("KillLineDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Kill the text between the cursor and the mark.
+ ///
+ internal static string KillRegionDescription {
+ get {
+ return ResourceManager.GetString("KillRegionDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Move the text from the cursor to the end of the current or next word to the kill ring.
+ ///
+ internal static string KillWordDescription {
+ get {
+ return ResourceManager.GetString("KillWordDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Complete the input if there is a single completion, otherwise complete the input by selecting from a menu of possible completions..
+ ///
+ internal static string MenuCompleteDescription {
+ get {
+ return ResourceManager.GetString("MenuCompleteDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Move to the end of the line..
+ ///
+ internal static string MoveToEndOfLineDescription {
+ get {
+ return ResourceManager.GetString("MoveToEndOfLineDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Replace the input with the next item in the history.
+ ///
+ internal static string NextHistoryDescription {
+ get {
+ return ResourceManager.GetString("NextHistoryDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Move the cursor to the next line if the input has multiple lines..
+ ///
+ internal static string NextLineDescription {
+ get {
+ return ResourceManager.GetString("NextLineDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Move the cursor forward to the start of the next word.
+ ///
+ internal static string NextWordDescription {
+ get {
+ return ResourceManager.GetString("NextWordDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Moves the cursor forward to the end of the next word..
+ ///
+ internal static string NextWordEndDescription {
+ get {
+ return ResourceManager.GetString("NextWordEndDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to The -ViMode parameter was used, but the current EditMode is not Vi..
+ ///
+ internal static string NotInViMode {
+ get {
+ return ResourceManager.GetString("NotInViMode", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to
+ ///Oops, something went wrong. Please report this bug with the details below.
+ ///Report on GitHub: https://github.com/lzybkr/PSReadLine/issues/new.
+ ///
+ internal static string OopsAnErrorMessage1 {
+ get {
+ return ResourceManager.GetString("OopsAnErrorMessage1", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to -----------------------------------------------------------------------
+ ///Last {0} Keys:
+ ///{1}
+ ///
+ ///Exception:
+ ///{2}
+ ///-----------------------------------------------------------------------.
+ ///
+ internal static string OopsAnErrorMessage2 {
+ get {
+ return ResourceManager.GetString("OopsAnErrorMessage2", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to An exception occurred in custom key handler, see $error for more information: {0}.
+ ///
+ internal static string OopsCustomHandlerException {
+ get {
+ return ResourceManager.GetString("OopsCustomHandlerException", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Write the contents of the local clipboard after the cursor..
+ ///
+ internal static string PasteAfterDescription {
+ get {
+ return ResourceManager.GetString("PasteAfterDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Write the contents of the local clipboard before the cursor..
+ ///
+ internal static string PasteBeforeDescription {
+ get {
+ return ResourceManager.GetString("PasteBeforeDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Paste text from the system clipboard.
+ ///
+ internal static string PasteDescription {
+ get {
+ return ResourceManager.GetString("PasteDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Display the possible completions without changing the input.
+ ///
+ internal static string PossibleCompletionsDescription {
+ get {
+ return ResourceManager.GetString("PossibleCompletionsDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Inserts the entered character at the beginning and accepts the line..
+ ///
+ internal static string PrependAndAcceptDescription {
+ get {
+ return ResourceManager.GetString("PrependAndAcceptDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Replace the input with the previous item in the history.
+ ///
+ internal static string PreviousHistoryDescription {
+ get {
+ return ResourceManager.GetString("PreviousHistoryDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Move the cursor to the previous line if the input has multiple lines..
+ ///
+ internal static string PreviousLineDescription {
+ get {
+ return ResourceManager.GetString("PreviousLineDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Redo an undo.
+ ///
+ internal static string RedoDescription {
+ get {
+ return ResourceManager.GetString("RedoDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Repeat the last recorded character search in the opposite direction..
+ ///
+ internal static string RepeatLastCharSearchBackwardsDescription {
+ get {
+ return ResourceManager.GetString("RepeatLastCharSearchBackwardsDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Repeat the last recorded character search..
+ ///
+ internal static string RepeatLastCharSearchDescription {
+ get {
+ return ResourceManager.GetString("RepeatLastCharSearchDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Repeats the last modification command..
+ ///
+ internal static string RepeatLastCommandDescription {
+ get {
+ return ResourceManager.GetString("RepeatLastCommandDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Repeats the last search..
+ ///
+ internal static string RepeatLastSearchDescription {
+ get {
+ return ResourceManager.GetString("RepeatLastSearchDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Repeat the last search, but in the opposite direction..
+ ///
+ internal static string RepeatSearchBackwardDescription {
+ get {
+ return ResourceManager.GetString("RepeatSearchBackwardDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Repeat the last search..
+ ///
+ internal static string RepeatSearchDescription {
+ get {
+ return ResourceManager.GetString("RepeatSearchDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Replace the current character with the next set of characters typed..
+ ///
+ internal static string ReplaceCharDescription {
+ get {
+ return ResourceManager.GetString("ReplaceCharDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Replace the current character with only one character..
+ ///
+ internal static string ReplaceCharInPlaceDescription {
+ get {
+ return ResourceManager.GetString("ReplaceCharInPlaceDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to length is too big.
+ ///
+ internal static string ReplacementLengthTooBig {
+ get {
+ return ResourceManager.GetString("ReplacementLengthTooBig", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Search history backwards interactively.
+ ///
+ internal static string ReverseSearchHistoryDescription {
+ get {
+ return ResourceManager.GetString("ReverseSearchHistoryDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Equivalent to undo all edits (clears the line except lines imported from history).
+ ///
+ internal static string RevertLineDescription {
+ get {
+ return ResourceManager.GetString("RevertLineDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Scroll the display down one screen.
+ ///
+ internal static string ScrollDisplayDownDescription {
+ get {
+ return ResourceManager.GetString("ScrollDisplayDownDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Scroll the display down one line.
+ ///
+ internal static string ScrollDisplayDownLineDescription {
+ get {
+ return ResourceManager.GetString("ScrollDisplayDownLineDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Scroll the display to the cursor.
+ ///
+ internal static string ScrollDisplayToCursorDescription {
+ get {
+ return ResourceManager.GetString("ScrollDisplayToCursorDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Scroll the display to the top.
+ ///
+ internal static string ScrollDisplayTopDescription {
+ get {
+ return ResourceManager.GetString("ScrollDisplayTopDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Scroll the display up one screen.
+ ///
+ internal static string ScrollDisplayUpDescription {
+ get {
+ return ResourceManager.GetString("ScrollDisplayUpDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Scroll the display up one line.
+ ///
+ internal static string ScrollDisplayUpLineDescription {
+ get {
+ return ResourceManager.GetString("ScrollDisplayUpLineDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Searches backward for the prescribed character..
+ ///
+ internal static string SearchBackwardCharDescription {
+ get {
+ return ResourceManager.GetString("SearchBackwardCharDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Move to the previous occurrence of the specified character..
+ ///
+ internal static string SearchCharBackwardDescription {
+ get {
+ return ResourceManager.GetString("SearchCharBackwardDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Move to the previous occurrence of the specified character and then forward one character..
+ ///
+ internal static string SearchCharBackwardWithBackoffDescription {
+ get {
+ return ResourceManager.GetString("SearchCharBackwardWithBackoffDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Move to the next occurrence of the specified character..
+ ///
+ internal static string SearchCharDescription {
+ get {
+ return ResourceManager.GetString("SearchCharDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Move to he next occurrence of the specified character and then back one character..
+ ///
+ internal static string SearchCharWithBackoffDescription {
+ get {
+ return ResourceManager.GetString("SearchCharWithBackoffDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Searches for the prescribed character in the prescribed direction..
+ ///
+ internal static string SearchDescription {
+ get {
+ return ResourceManager.GetString("SearchDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Prompts for a search string and initiates a search upon AcceptLine..
+ ///
+ internal static string SearchForwardDescription {
+ get {
+ return ResourceManager.GetString("SearchForwardDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Select the entire line. Moves the cursor to the end of the line.
+ ///
+ internal static string SelectAllDescription {
+ get {
+ return ResourceManager.GetString("SelectAllDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Adjust the current selection to include the previous character.
+ ///
+ internal static string SelectBackwardCharDescription {
+ get {
+ return ResourceManager.GetString("SelectBackwardCharDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Adjust the current selection to include from the cursor to the end of the line.
+ ///
+ internal static string SelectBackwardsLineDescription {
+ get {
+ return ResourceManager.GetString("SelectBackwardsLineDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Adjust the current selection to include the previous word.
+ ///
+ internal static string SelectBackwardWordDescription {
+ get {
+ return ResourceManager.GetString("SelectBackwardWordDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Adjust the current selection to include the next character.
+ ///
+ internal static string SelectForwardCharDescription {
+ get {
+ return ResourceManager.GetString("SelectForwardCharDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Adjust the current selection to include the next word using ForwardWord.
+ ///
+ internal static string SelectForwardWordDescription {
+ get {
+ return ResourceManager.GetString("SelectForwardWordDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Adjust the current selection to include from the cursor to the start of the line.
+ ///
+ internal static string SelectLineDescription {
+ get {
+ return ResourceManager.GetString("SelectLineDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Adjust the current selection to include the next word.
+ ///
+ internal static string SelectNextWordDescription {
+ get {
+ return ResourceManager.GetString("SelectNextWordDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Adjust the current selection to include the previous word using ShellBackwardWord.
+ ///
+ internal static string SelectShellBackwardWordDescription {
+ get {
+ return ResourceManager.GetString("SelectShellBackwardWordDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Adjust the current selection to include the next word using ShellForwardWord.
+ ///
+ internal static string SelectShellForwardWordDescription {
+ get {
+ return ResourceManager.GetString("SelectShellForwardWordDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Adjust the current selection to include the next word using ShellNextWord.
+ ///
+ internal static string SelectShellNextWordDescription {
+ get {
+ return ResourceManager.GetString("SelectShellNextWordDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Insert the key typed.
+ ///
+ internal static string SelfInsertDescription {
+ get {
+ return ResourceManager.GetString("SelfInsertDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Mark the location of the cursor.
+ ///
+ internal static string SetMarkDescription {
+ get {
+ return ResourceManager.GetString("SetMarkDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Move the text from the cursor to the start of the current or previous token to the kill ring.
+ ///
+ internal static string ShellBackwardKillWordDescription {
+ get {
+ return ResourceManager.GetString("ShellBackwardKillWordDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Move the cursor to the beginning of the current or previous token or start of the line.
+ ///
+ internal static string ShellBackwardWordDescription {
+ get {
+ return ResourceManager.GetString("ShellBackwardWordDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Move the cursor to the beginning of the next token or end of line.
+ ///
+ internal static string ShellForwardWordDescription {
+ get {
+ return ResourceManager.GetString("ShellForwardWordDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Move the text from the cursor to the end of the current or next token to the kill ring.
+ ///
+ internal static string ShellKillWordDescription {
+ get {
+ return ResourceManager.GetString("ShellKillWordDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Move the cursor to the end of the current token.
+ ///
+ internal static string ShellNextWordDescription {
+ get {
+ return ResourceManager.GetString("ShellNextWordDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Show all key bindings.
+ ///
+ internal static string ShowKeyBindingsDescription {
+ get {
+ return ResourceManager.GetString("ShowKeyBindingsDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to 'start' cannot be less than zero or greater than the length of the buffer.
+ ///
+ internal static string StartOutOfRange {
+ get {
+ return ResourceManager.GetString("StartOutOfRange", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Swap the current character with the character before it..
+ ///
+ internal static string SwapCharactersDescription {
+ get {
+ return ResourceManager.GetString("SwapCharactersDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Complete the input using the next completion.
+ ///
+ internal static string TabCompleteNextDescription {
+ get {
+ return ResourceManager.GetString("TabCompleteNextDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Complete the input using the previous completion.
+ ///
+ internal static string TabCompletePreviousDescription {
+ get {
+ return ResourceManager.GetString("TabCompletePreviousDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Undoes all commands for this line..
+ ///
+ internal static string UndoAllDescription {
+ get {
+ return ResourceManager.GetString("UndoAllDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Undo a previous edit.
+ ///
+ internal static string UndoDescription {
+ get {
+ return ResourceManager.GetString("UndoDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Move the text from the cursor to the start of the current or previous whitespace delimited word to the kill ring.
+ ///
+ internal static string UnixWordRuboutDescription {
+ get {
+ return ResourceManager.GetString("UnixWordRuboutDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Unrecognized key '{0}'. Please use a character literal or a well-known key name from the System.ConsoleKey enumeration..
+ ///
+ internal static string UnrecognizedKey {
+ get {
+ return ResourceManager.GetString("UnrecognizedKey", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Accept the input or move to the next line if input is missing a closing token.
+ ///If there are other parse errors, unresolved commands, or incorrect parameters, show the error and continue editing..
+ ///
+ internal static string ValidateAndAcceptLineDescription {
+ get {
+ return ResourceManager.GetString("ValidateAndAcceptLineDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Accept the line and switch to Vi's insert mode..
+ ///
+ internal static string ViAcceptLineDescription {
+ get {
+ return ResourceManager.GetString("ViAcceptLineDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to If the line is empty, exit, otherwise accept the line as input..
+ ///
+ internal static string ViAcceptLineOrExitDescription {
+ get {
+ return ResourceManager.GetString("ViAcceptLineOrExitDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Switches to insert mode after positioning the cursor past the end of the line..
+ ///
+ internal static string ViAppendAtEndDescription {
+ get {
+ return ResourceManager.GetString("ViAppendAtEndDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Appends a new multi-line edit mode line to the current line..
+ ///
+ internal static string ViAppendLineDescription {
+ get {
+ return ResourceManager.GetString("ViAppendLineDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Delete backward to the beginning of the previous word, as delimited by white space..
+ ///
+ internal static string ViBackwardDeleteGlobDescription {
+ get {
+ return ResourceManager.GetString("ViBackwardDeleteGlobDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Move the cursor to the beginning of the previous word, as delimited by white space..
+ ///
+ internal static string ViBackwardGlobDescription {
+ get {
+ return ResourceManager.GetString("ViBackwardGlobDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Delete backward to the beginning of the previous word, as delimited by white space, and enter insert mode..
+ ///
+ internal static string ViBackwardReplaceGlobDescription {
+ get {
+ return ResourceManager.GetString("ViBackwardReplaceGlobDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Replaces the line left of the cursor and all of the way to the beginning..
+ ///
+ internal static string ViBackwardReplaceLineDescription {
+ get {
+ return ResourceManager.GetString("ViBackwardReplaceLineDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Replaces the line left of the cursor and all but one character to the beginning of the line..
+ ///
+ internal static string ViBackwardReplaceLineToFirstCharDescription {
+ get {
+ return ResourceManager.GetString("ViBackwardReplaceLineToFirstCharDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Replace the previous word..
+ ///
+ internal static string ViBackwardReplaceWordDescription {
+ get {
+ return ResourceManager.GetString("ViBackwardReplaceWordDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Delete backward to the beginning of the previous word, as delimited by white space and common delimiters, and enter insert mode..
+ ///
+ internal static string ViBackwardWordDescription {
+ get {
+ return ResourceManager.GetString("ViBackwardWordDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Switch to VI's command mode..
+ ///
+ internal static string ViCommandModeDescription {
+ get {
+ return ResourceManager.GetString("ViCommandModeDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Deletes all characters between the cursor position and the matching brace..
+ ///
+ internal static string ViDeleteBraceDescription {
+ get {
+ return ResourceManager.GetString("ViDeleteBraceDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Delete to the end of this word, as delimited by white space..
+ ///
+ internal static string ViDeleteEndOfGlobDescription {
+ get {
+ return ResourceManager.GetString("ViDeleteEndOfGlobDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Delete the current word, as delimited by white space..
+ ///
+ internal static string ViDeleteGlobDescription {
+ get {
+ return ResourceManager.GetString("ViDeleteGlobDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Handles the processing of a number argument after the first key of a chord..
+ ///
+ internal static string ViDigitArgumentInChordDescription {
+ get {
+ return ResourceManager.GetString("ViDigitArgumentInChordDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Invokes the console compatible editor specified by $env:VISUAL or $env:$EDITOR on the current command line..
+ ///
+ internal static string ViEditVisuallyDescription {
+ get {
+ return ResourceManager.GetString("ViEditVisuallyDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Move the cursor to the end this word, as delimited by white space..
+ ///
+ internal static string ViEndOfGlobDescription {
+ get {
+ return ResourceManager.GetString("ViEndOfGlobDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Exit the shell..
+ ///
+ internal static string ViExitDescription {
+ get {
+ return ResourceManager.GetString("ViExitDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Move the cursor to the matching brace..
+ ///
+ internal static string ViGotoBraceDescription {
+ get {
+ return ResourceManager.GetString("ViGotoBraceDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Moves the cursor to the beginning of the line and switches to insert mode..
+ ///
+ internal static string ViInsertAtBeginningDescription {
+ get {
+ return ResourceManager.GetString("ViInsertAtBeginingDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Moves the cursor to the end of the line and switches to insert mode..
+ ///
+ internal static string ViInsertAtEndDescription {
+ get {
+ return ResourceManager.GetString("ViInsertAtEndDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Inserts a new multi-line edit mode line in front of the current line..
+ ///
+ internal static string ViInsertLineDescription {
+ get {
+ return ResourceManager.GetString("ViInsertLineDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Switches to insert mode..
+ ///
+ internal static string ViInsertModeDescription {
+ get {
+ return ResourceManager.GetString("ViInsertModeDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Switch to insert mode, appending at the current line position..
+ ///
+ internal static string ViInsertWithAppendDescription {
+ get {
+ return ResourceManager.GetString("ViInsertWithAppendDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Deletes the current character and switches to insert mode..
+ ///
+ internal static string ViInsertWithDeleteDescription {
+ get {
+ return ResourceManager.GetString("ViInsertWithDeleteDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Joins the current multi-line edit mode line with the next..
+ ///
+ internal static string ViJoinLinesDescription {
+ get {
+ return ResourceManager.GetString("ViJoinLinesDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Move the cursor to the beginning of the next word, as delimited by white space..
+ ///
+ internal static string ViNextGlobDescription {
+ get {
+ return ResourceManager.GetString("ViNextGlobDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Move the cursor to the beginning of the next word, as delimited by white space and common delimiters..
+ ///
+ internal static string ViNextWordDescription {
+ get {
+ return ResourceManager.GetString("ViNextWordDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Replace all characters between the current brace character and it's matching partner..
+ ///
+ internal static string ViReplaceBraceDescription {
+ get {
+ return ResourceManager.GetString("ViReplaceBraceDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Delete to the end of the word, as delimited by white space, and enter insert mode..
+ ///
+ internal static string ViReplaceEndOfGlobDescription {
+ get {
+ return ResourceManager.GetString("ViReplaceEndOfGlobDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Delete to the end of the word, as delimited by white space and common delimiters, and enter insert mode..
+ ///
+ internal static string ViReplaceEndOfWordDescription {
+ get {
+ return ResourceManager.GetString("ViReplaceEndOfWordDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Delete to the beginning of the next word, as delimited by white space, and enter insert mode..
+ ///
+ internal static string ViReplaceGlobDescription {
+ get {
+ return ResourceManager.GetString("ViReplaceGlobDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Repace the current line with the next set of characters typed..
+ ///
+ internal static string ViReplaceLineDescription {
+ get {
+ return ResourceManager.GetString("ViReplaceLineDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Replace the characters from the cursor position to the end of the line..
+ ///
+ internal static string ViReplaceToEndDescription {
+ get {
+ return ResourceManager.GetString("ViReplaceToEndDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Replace the current character until an escape is entered or the line is accepted..
+ ///
+ internal static string ViReplaceUntilEscDescription {
+ get {
+ return ResourceManager.GetString("ViReplaceUntilEscDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Replace the current word..
+ ///
+ internal static string ViReplaceWordDescription {
+ get {
+ return ResourceManager.GetString("ViReplaceWordDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Starts a new search backward in the history..
+ ///
+ internal static string ViSearchHistoryBackwardDescription {
+ get {
+ return ResourceManager.GetString("ViSearchHistoryBackwardDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Invokes TabCompleteNext after doing some vi-specific clean up..
+ ///
+ internal static string ViTabCompleteNextDescription {
+ get {
+ return ResourceManager.GetString("ViTabCompleteNextDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Invokes TabCompletePrevious after doing some vi-specific clean up..
+ ///
+ internal static string ViTabCompletePreviousDescription {
+ get {
+ return ResourceManager.GetString("ViTabCompletePreviousDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Transposes the current character with the next character in the line..
+ ///
+ internal static string ViTransposeCharsDescription {
+ get {
+ return ResourceManager.GetString("ViTransposeCharsDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Place the characters before the cursor into the local clipboard..
+ ///
+ internal static string ViYankBeginningOfLineDescription {
+ get {
+ return ResourceManager.GetString("ViYankBeginningOfLineDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Place the characters from the cursor to the end of the next white space delimited word into the local clipboard..
+ ///
+ internal static string ViYankEndOfGlobDescription {
+ get {
+ return ResourceManager.GetString("ViYankEndOfGlobDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Place the characters from the cursor to the end of the next word, as delimited by white space and common delimiters, into the local clipboard..
+ ///
+ internal static string ViYankEndOfWordDescription {
+ get {
+ return ResourceManager.GetString("ViYankEndOfWordDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Place the character to the left of the cursor into the local clipboard..
+ ///
+ internal static string ViYankLeftDescription {
+ get {
+ return ResourceManager.GetString("ViYankLeftDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Place all characters in the current line into the local clipboard..
+ ///
+ internal static string ViYankLineDescription {
+ get {
+ return ResourceManager.GetString("ViYankLineDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Place all characters from the cursor to the end of the word, as delimited by white space, into the local clipboard..
+ ///
+ internal static string ViYankNextGlobDescription {
+ get {
+ return ResourceManager.GetString("ViYankNextGlobDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Place all characters from the cursor to the end of the word, as delimited by white space and common delimiters, into the local clipboard..
+ ///
+ internal static string ViYankNextWordDescription {
+ get {
+ return ResourceManager.GetString("ViYankNextWordDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Place all characters between the matching brace and the cursor into the local clipboard..
+ ///
+ internal static string ViYankPercentDescription {
+ get {
+ return ResourceManager.GetString("ViYankPercentDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Place all characters from before the cursor to the beginning of the previous word, as delimited by white space, into the local clipboard..
+ ///
+ internal static string ViYankPreviousGlobDescription {
+ get {
+ return ResourceManager.GetString("ViYankPreviousGlobDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Place all characters from before the cursor to the beginning of the previous word, as delimited by white space and common delimiters, into the local clipboard..
+ ///
+ internal static string ViYankPreviousWordDescription {
+ get {
+ return ResourceManager.GetString("ViYankPreviousWordDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Place the character at the cursor into the local clipboard..
+ ///
+ internal static string ViYankRightDescription {
+ get {
+ return ResourceManager.GetString("ViYankRightDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Place all characters at and after the cursor into the local clipboard..
+ ///
+ internal static string ViYankToEndOfLineDescription {
+ get {
+ return ResourceManager.GetString("ViYankToEndOfLineDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Place all characters before the cursor and to the 1st non-white space character into the local clipboard..
+ ///
+ internal static string ViYankToFirstCharDescription {
+ get {
+ return ResourceManager.GetString("ViYankToFirstCharDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Show the key binding for the next chord entered.
+ ///
+ internal static string WhatIsKeyDescription {
+ get {
+ return ResourceManager.GetString("WhatIsKeyDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Copy the text from the current kill ring position to the input.
+ ///
+ internal static string YankDescription {
+ get {
+ return ResourceManager.GetString("YankDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Copy the text of the last argument to the input.
+ ///
+ internal static string YankLastArgDescription {
+ get {
+ return ResourceManager.GetString("YankLastArgDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Copy the text of the first argument to the input.
+ ///
+ internal static string YankNthArgDescription {
+ get {
+ return ResourceManager.GetString("YankNthArgDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Replace the previously yanked text with the text from the next kill ring position.
+ ///
+ internal static string YankPopDescription {
+ get {
+ return ResourceManager.GetString("YankPopDescription", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.PowerShell.PSReadLine/PSReadLineResources.resx b/src/Microsoft.PowerShell.PSReadLine/PSReadLineResources.resx
new file mode 100644
index 00000000000..b8d15651123
--- /dev/null
+++ b/src/Microsoft.PowerShell.PSReadLine/PSReadLineResources.resx
@@ -0,0 +1,720 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Accept the input or move to the next line if input is missing a closing token.
+
+
+ Move the cursor to the next line without attempting to execute the input
+
+
+ Move the cursor back one character
+
+
+ Delete the character before the cursor
+
+
+ Delete text from the cursor to the start of the line
+
+
+ Move the text from the cursor to the beginning of the line to the kill ring
+
+
+ Move to the first item in the history
+
+
+ Move the cursor to the beginning of the line
+
+
+ Abort editing the current line and re-evaluate the prompt
+
+
+ Remove all items from the kill ring
+
+
+ Remove all items from the command line history (not PowerShell history)
+
+
+ Complete the input if there is a single completion, otherwise complete the input with common prefix for all completions. Show possible completions if pressed a second time.
+
+
+ Delete the character under the cursor
+
+
+ Move the cursor to the beginning of the current or previous token or start of the line
+
+
+ String is not used in the UI
+
+
+ String is not used in the UI
+
+
+ Move to the last item (the current input) in the history
+
+
+ Move the cursor to the end of the line
+
+
+ Mark the location of the cursor and move the cursor to the position of the previous mark
+
+
+ Move the cursor forward one character
+
+
+ Delete text from the cursor to the end of the line
+
+
+ Move the cursor to the beginning of the next token or end of line
+
+
+ Search for the previous item in the history that starts with the current input - like PreviousHistory if the input is empty
+
+
+ Search for the next item in the history that starts with the current input - like NextHistory if the input is empty
+
+
+ String is not used in the UI
+
+
+ Move the text from the cursor to the start of the current or previous token to the kill ring
+
+
+ Move the text from the cursor to the end of the input to the kill ring
+
+
+ Move the text from the cursor to the end of the current or next token to the kill ring
+
+
+ Replace the input with the next item in the history
+
+
+ Paste text from the system clipboard
+
+
+ Display the possible completions without changing the input
+
+
+ Replace the input with the previous item in the history
+
+
+ Redo an undo
+
+
+ Equivalent to undo all edits (clears the line except lines imported from history)
+
+
+ Mark the location of the cursor
+
+
+ Complete the input using the next completion
+
+
+ Complete the input using the previous completion
+
+
+ Undo a previous edit
+
+
+ Copy the text from the current kill ring position to the input
+
+
+ Replace the previously yanked text with the text from the next kill ring position
+
+
+ 'start' cannot be less than zero or greater than the length of the buffer
+
+
+ length is too big
+
+
+ Display all {0} possibilities? (y or n) _
+
+
+ Clear the screen and redraw the current line at the top of the screen
+
+
+ Go to matching brace
+
+
+ Abort the current operation, e.g. incremental history search
+
+
+ Search history forward interactively
+
+
+ Search history backwards interactively
+
+
+ Move the text from the start of the current or previous word to the cursor to the kill ring
+
+
+ Move the cursor to the beginning of the current or previous word
+
+
+ Move the cursor forward to the end of the current word, or if between words, to the end of the next word.
+
+
+ Move the text from the cursor to the end of the current or next word to the kill ring
+
+
+ Move the cursor forward to the start of the next word
+
+
+ Move the text from the cursor to the start of the current or previous whitespace delimited word to the kill ring
+
+
+ Read a character and move the cursor to the previous occurence of that character
+
+
+ Read a character and move the cursor to the next occurence of that character
+
+
+ Start or accumulate a numeric argument to other functions
+
+
+ Copy the text of the last argument to the input
+
+
+ Copy the text of the first argument to the input
+
+
+ Accept the current line and recall the next line from history after the current line finishes executing
+
+
+ Key is unbound
+
+
+ Insert the key typed
+
+
+ Show all key bindings
+
+
+ Show the key binding for the next chord entered
+
+
+ Copy selected region to the system clipboard. If no region is selected, copy the whole line
+
+
+ Delete selected region placing deleted text in the system clipboard
+
+
+ Kill the text between the cursor and the mark
+
+
+ Adjust the current selection to include the previous character
+
+
+ Adjust the current selection to include the previous word
+
+
+ Adjust the current selection to include the next character
+
+
+ Adjust the current selection to include the next word using ForwardWord
+
+
+ Adjust the current selection to include the next word
+
+
+ Adjust the current selection to include the previous word using ShellBackwardWord
+
+
+ Adjust the current selection to include the next word using ShellForwardWord
+
+
+ Allows you to select multiple lines from the console using Shift+UpArrow/DownArrow and copy the selected lines to clipboard by pressing Enter.
+
+
+ Erases the current prompt and calls the prompt function to redisplay the prompt
+
+
+ Scroll the display down one screen
+
+
+ Scroll the display down one line
+
+
+ Scroll the display to the cursor
+
+
+ Scroll the display to the top
+
+
+ Scroll the display up one screen
+
+
+ Scroll the display up one line
+
+
+ Adjust the current selection to include the next word using ShellNextWord
+
+
+ Move the cursor to the end of the current token
+
+
+ Adjust the current selection to include from the cursor to the end of the line
+
+
+ Adjust the current selection to include from the cursor to the start of the line
+
+
+ Select the entire line. Moves the cursor to the end of the line
+
+
+ Either copy selected text to the clipboard, or if no text is selected, cancel editing the line with CancelLine.
+
+
+ Complete the input if there is a single completion, otherwise complete the input by selecting from a menu of possible completions.
+
+
+
+Oops, something went wrong. Please report this bug with the details below.
+Report on GitHub: https://github.com/lzybkr/PSReadLine/issues/new
+
+
+ -----------------------------------------------------------------------
+Last {0} Keys:
+{1}
+
+Exception:
+{2}
+-----------------------------------------------------------------------
+
+
+ Move the cursor to the next line if the input has multiple lines.
+
+
+ Move the cursor to the previous line if the input has multiple lines.
+
+
+ Command '{0}' cannot be found.
+
+
+ Accept the input or move to the next line if input is missing a closing token.
+If there are other parse errors, unresolved commands, or incorrect parameters, show the error and continue editing.
+
+
+ Delete the character under the cursor, or if the line is empty, exit the process.
+
+
+ Unable to translate '{0}' to virtual key code: {1}.
+
+
+ Chord can have at most two keys.
+
+
+ Duplicate or invalid modifier token '{0}' for key '{1}'.
+
+
+ Invalid sequence '{0}'.
+
+
+ Unrecognized key '{0}'. Please use a character literal or a well-known key name from the System.ConsoleKey enumeration.
+
+
+ Accept the line and switch to Vi's insert mode.
+
+
+ Delete the previous word in the line.
+
+
+ Switch to VI's command mode.
+
+
+ Move to the end of the line.
+
+
+ Switch to insert mode, appending at the current line position.
+
+
+ Swap the current character with the character before it.
+
+
+ Repeats the last search.
+
+
+ Searches backward for the prescribed character.
+
+
+ Searches for the prescribed character in the prescribed direction.
+
+
+ Switches to insert mode after positioning the cursor past the end of the line.
+
+
+ Deletes all of the line except for leading whitespace.
+
+
+ Replaces the character in front of the cursor.
+
+
+ Replaces the line left of the cursor and all of the way to the beginning.
+
+
+ Replaces the line left of the cursor and all but one character to the beginning of the line.
+
+
+ Inserts the entered character at the beginning and accepts the line.
+
+
+ Deletes all characters between the cursor and the matching brace.
+
+
+ Deletes the current line.
+
+
+ Deletes from the cursor to the end of the line.
+
+
+ Deletes from the cursor to the end of the current word.
+
+
+ Deletes the current word.
+
+
+ Positions the cursor at the first non-blank character.
+
+
+ Moves the cursor forward to the end of the next word.
+
+
+ Moves the cursor to the prescribed column.
+
+
+ Switches to insert mode.
+
+
+ Moves the cursor to the beginning of the line and switches to insert mode.
+
+
+ Moves the cursor to the end of the line and switches to insert mode.
+
+
+ Deletes the current character and switches to insert mode.
+
+
+ Inverts the case of the current character and advances the cursor.
+
+
+ Repeats the last modification command.
+
+
+ Repeat the last search, but in the opposite direction.
+
+
+ Repeat the last search.
+
+
+ Replace all characters between the current brace character and it's matching partner.
+
+
+ Replace the current character with the next set of characters typed.
+
+
+ Replace the current character with only one character.
+
+
+ Repace the current line with the next set of characters typed.
+
+
+ Replace the characters from the cursor position to the end of the line.
+
+
+ Replace the current character until an escape is entered or the line is accepted.
+
+
+ Replace the current word.
+
+
+ Delete the current character and insert the next character.
+
+
+ Starts a new search backward in the history.
+
+
+ Transposes the current character with the next character in the line.
+
+
+ Undoes all commands for this line.
+
+
+ Prompts for a search string and initiates a search upon AcceptLine.
+
+
+ Repeat the last recorded character search in the opposite direction.
+
+
+ Repeat the last recorded character search.
+
+
+ Move to the previous occurrence of the specified character.
+
+
+ Move to the previous occurrence of the specified character and then forward one character.
+
+
+ Move to the next occurrence of the specified character.
+
+
+ Move to he next occurrence of the specified character and then back one character.
+
+
+ Replace the previous word.
+
+
+ Delete backward to the beginning of the previous word, as delimited by white space and common delimiters, and enter insert mode.
+
+
+ Write the contents of the local clipboard after the cursor.
+
+
+ Move the cursor to the beginning of the next word, as delimited by white space and common delimiters.
+
+
+ Move the cursor to the beginning of the previous word, as delimited by white space.
+
+
+ Move the cursor to the end this word, as delimited by white space.
+
+
+ Write the contents of the local clipboard before the cursor.
+
+
+ Move the cursor to the beginning of the next word, as delimited by white space.
+
+
+ Move the cursor to the matching brace.
+
+
+ Delete backward to the beginning of the previous word, as delimited by white space.
+
+
+ Delete the current word, as delimited by white space.
+
+
+ Delete to the end of the current word, as delimited by white space and common delimiters.
+
+
+ Delete to the end of this word, as delimited by white space.
+
+
+ Delete backward to the beginning of the previous word, as delimited by white space, and enter insert mode.
+
+
+ Delete to the beginning of the next word, as delimited by white space, and enter insert mode.
+
+
+ Delete to the end of the word, as delimited by white space and common delimiters, and enter insert mode.
+
+
+ Delete to the end of the word, as delimited by white space, and enter insert mode.
+
+
+ Place all characters in the current line into the local clipboard.
+
+
+ Place all characters at and after the cursor into the local clipboard.
+
+
+ Place all characters from before the cursor to the beginning of the previous word, as delimited by white space and common delimiters, into the local clipboard.
+
+
+ Place all characters from before the cursor to the beginning of the previous word, as delimited by white space, into the local clipboard.
+
+
+ Place all characters from the cursor to the end of the word, as delimited by white space and common delimiters, into the local clipboard.
+
+
+ Place all characters from the cursor to the end of the word, as delimited by white space, into the local clipboard.
+
+
+ Place the characters from the cursor to the end of the next word, as delimited by white space and common delimiters, into the local clipboard.
+
+
+ Place the characters from the cursor to the end of the next white space delimited word into the local clipboard.
+
+
+ Place the character to the left of the cursor into the local clipboard.
+
+
+ Place the character at the cursor into the local clipboard.
+
+
+ Place the characters before the cursor into the local clipboard.
+
+
+ Place all characters before the cursor and to the 1st non-white space character into the local clipboard.
+
+
+ Place all characters between the matching brace and the cursor into the local clipboard.
+
+
+ Deletes all characters between the cursor position and the matching brace.
+
+
+ If the line is empty, exit, otherwise accept the line as input.
+
+
+ Handles the processing of a number argument after the first key of a chord.
+
+
+ Exit the shell.
+
+
+ Invokes TabCompleteNext after doing some vi-specific clean up.
+
+
+ Invokes TabCompletePrevious after doing some vi-specific clean up.
+
+
+ Invokes the console compatible editor specified by $env:VISUAL or $env:$EDITOR on the current command line.
+
+
+ Appends a new multi-line edit mode line to the current line.
+
+
+ Inserts a new multi-line edit mode line in front of the current line.
+
+
+ Joins the current multi-line edit mode line with the next.
+
+
+ The -ViMode parameter was used, but the current EditMode is not Vi.
+
+
+ Inserts a new empty line above the current line without attempting to execute the input
+
+
+ Inserts a new empty line below the current line without attempting to execute the input
+
+
+ Error reading or writing history file '{0}': {1}
+
+
+ This error will not be reported again in this session. Consider using a different path with:
+ Set-PSReadlineOption -HistorySavePath <Path>
+Or not saving history with:
+ Set-PSReadlineOption -HistorySaveStyle SaveNothing
+
+
+ An exception occurred in custom key handler, see $error for more information: {0}
+
+
diff --git a/src/Microsoft.PowerShell.PSReadLine/PublicAPI.cs b/src/Microsoft.PowerShell.PSReadLine/PublicAPI.cs
new file mode 100644
index 00000000000..2db274ec43b
--- /dev/null
+++ b/src/Microsoft.PowerShell.PSReadLine/PublicAPI.cs
@@ -0,0 +1,250 @@
+/********************************************************************++
+Copyright (c) Microsoft Corporation. All rights reserved.
+--********************************************************************/
+
+using System;
+using System.Collections;
+using System.Diagnostics.CodeAnalysis;
+using System.Management.Automation;
+using System.Management.Automation.Language;
+using System.Management.Automation.Runspaces;
+
+
+namespace Microsoft.PowerShell
+{
+ namespace Internal
+ {
+#pragma warning disable 1591
+
+ [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")]
+ public interface IPSConsoleReadLineMockableMethods
+ {
+ void Ding();
+ CommandCompletion CompleteInput(string input, int cursorIndex, Hashtable options, System.Management.Automation.PowerShell powershell);
+ bool RunspaceIsRemote(Runspace runspace);
+
+ }
+
+ [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")]
+ public interface IConsole
+ {
+ object GetConsoleInputMode();
+ void SetConsoleInputMode(object mode);
+ void RestoreConsoleInputMode(object mode);
+ ConsoleKeyInfo ReadKey();
+ bool KeyAvailable { get; }
+ int CursorLeft { get; set; }
+ int CursorTop { get; set;}
+ int CursorSize { get; set; }
+ int BufferWidth { get; set; }
+ int BufferHeight { get; set;}
+ int WindowWidth { get; set; }
+ int WindowHeight { get; set; }
+ int WindowTop { get; set; }
+ ConsoleColor BackgroundColor { get; set; }
+ ConsoleColor ForegroundColor { get; set; }
+ void SetWindowPosition(int left, int top);
+ void SetCursorPosition(int left, int top);
+ void WriteLine(string s);
+ void Write(string s);
+ void WriteBufferLines(BufferChar[] buffer, ref int top);
+ void WriteBufferLines(BufferChar[] buffer, ref int top, bool ensureBottomLineVisible);
+ void ScrollBuffer(int lines);
+ BufferChar[] ReadBufferLines(int top, int count);
+
+ void StartRender();
+ int LengthInBufferCells(char c);
+ void EndRender();
+#if UNIX
+ void Clear();
+#endif
+ }
+
+#pragma warning restore 1591
+ }
+
+ ///
+ public partial class PSConsoleReadLine
+ {
+ ///
+ /// Insert a character at the current position. Supports undo.
+ ///
+ /// Character to insert
+ public static void Insert(char c)
+ {
+ _singleton.SaveEditItem(EditItemInsertChar.Create(c, _singleton._current));
+
+ // Use Append if possible because Insert at end makes StringBuilder quite slow.
+ if (_singleton._current == _singleton._buffer.Length)
+ {
+ _singleton._buffer.Append(c);
+ }
+ else
+ {
+ _singleton._buffer.Insert(_singleton._current, c);
+ }
+ _singleton._current += 1;
+ _singleton.Render();
+ }
+
+ ///
+ /// Insert a string at the current position. Supports undo.
+ ///
+ /// String to insert
+ public static void Insert(string s)
+ {
+ _singleton.SaveEditItem(EditItemInsertString.Create(s, _singleton._current));
+
+ // Use Append if possible because Insert at end makes StringBuilder quite slow.
+ if (_singleton._current == _singleton._buffer.Length)
+ {
+ _singleton._buffer.Append(s);
+ }
+ else
+ {
+ _singleton._buffer.Insert(_singleton._current, s);
+ }
+ _singleton._current += s.Length;
+ _singleton.Render();
+ }
+
+ ///
+ /// Delete some text at the given position. Supports undo.
+ ///
+ /// The start position to delete
+ /// The length to delete
+ public static void Delete(int start, int length)
+ {
+ Replace(start, length, null);
+ }
+
+ ///
+ /// Replace some text at the given position. Supports undo.
+ ///
+ /// The start position to replace
+ /// The length to replace
+ /// The replacement text
+ /// The action that initiated the replace (used for undo)
+ /// The argument to the action that initiated the replace (used for undo)
+ public static void Replace(int start, int length, string replacement, Action instigator = null, object instigatorArg = null)
+ {
+ if (start < 0 || start > _singleton._buffer.Length)
+ {
+ throw new ArgumentException(PSReadLineResources.StartOutOfRange, "start");
+ }
+ if (length > (_singleton._buffer.Length - start))
+ {
+ throw new ArgumentException(PSReadLineResources.ReplacementLengthTooBig, "length");
+ }
+
+ bool useEditGroup = (_singleton._editGroupStart == -1);
+
+ if (useEditGroup)
+ {
+ _singleton.StartEditGroup();
+ }
+
+ var str = _singleton._buffer.ToString(start, length);
+ _singleton.SaveEditItem(EditItemDelete.Create(str, start));
+ _singleton._buffer.Remove(start, length);
+ if (replacement != null)
+ {
+ _singleton.SaveEditItem(EditItemInsertString.Create(replacement, start));
+ _singleton._buffer.Insert(start, replacement);
+ _singleton._current = start + replacement.Length;
+ }
+ else
+ {
+ _singleton._current = start;
+ }
+
+ if (useEditGroup)
+ {
+ _singleton.EndEditGroup(instigator, instigatorArg); // Instigator is needed for VI undo
+ _singleton.Render();
+ }
+ }
+
+ ///
+ /// Get the state of the buffer - the current input and the position of the cursor
+ ///
+ public static void GetBufferState(out string input, out int cursor)
+ {
+ input = _singleton._buffer.ToString();
+ cursor = _singleton._current;
+ }
+
+ ///
+ /// Get the state of the buffer - the ast, tokens, errors, and position of the cursor
+ ///
+ public static void GetBufferState(out Ast ast, out Token[] tokens, out ParseError[] parseErrors, out int cursor)
+ {
+ _singleton.ParseInput();
+ ast = _singleton._ast;
+ tokens = _singleton._tokens;
+ parseErrors = _singleton._parseErrors;
+ cursor = _singleton._current;
+ }
+
+ ///
+ /// Get the selection state of the buffer
+ ///
+ /// The start of the current selection or -1 if nothing is selected.
+ /// The length of the current selection or -1 if nothing is selected.
+ public static void GetSelectionState(out int start, out int length)
+ {
+ if (_singleton._visualSelectionCommandCount == 0)
+ {
+ start = -1;
+ length = -1;
+ }
+ else
+ {
+ _singleton.GetRegion(out start, out length);
+ }
+ }
+
+ ///
+ /// Set the position of the cursor.
+ ///
+ public static void SetCursorPosition(int cursor)
+ {
+ if (cursor > _singleton._buffer.Length + ViEndOfLineFactor)
+ {
+ cursor = _singleton._buffer.Length + ViEndOfLineFactor;
+ }
+ if (cursor < 0)
+ {
+ cursor = 0;
+ }
+
+ _singleton._current = cursor;
+ _singleton.PlaceCursor();
+ }
+
+ ///
+ /// A helper method when your function expects an optional int argument (e.g. from DigitArgument)
+ /// If there is not argument (it's null), returns true and sets numericArg to defaultNumericArg.
+ /// Dings and returns false if the argument is not an int (no conversion is attempted)
+ /// Otherwise returns true, and numericArg has the result.
+ ///
+ public static bool TryGetArgAsInt(object arg, out int numericArg, int defaultNumericArg)
+ {
+ if (arg == null)
+ {
+ numericArg = defaultNumericArg;
+ return true;
+ }
+
+ if (arg is int)
+ {
+ numericArg = (int)arg;
+ return true;
+ }
+
+ Ding();
+ numericArg = 0;
+ return false;
+ }
+ }
+}
diff --git a/src/Microsoft.PowerShell.PSReadLine/ReadLine.cs b/src/Microsoft.PowerShell.PSReadLine/ReadLine.cs
new file mode 100644
index 00000000000..0aede96f921
--- /dev/null
+++ b/src/Microsoft.PowerShell.PSReadLine/ReadLine.cs
@@ -0,0 +1,957 @@
+/********************************************************************++
+Copyright (c) Microsoft Corporation. All rights reserved.
+--********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+using System.Management.Automation;
+using System.Management.Automation.Language;
+using System.Management.Automation.Runspaces;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading;
+using Microsoft.PowerShell.Commands;
+using Microsoft.PowerShell.Internal;
+
+[module: SuppressMessage("Microsoft.Design", "CA1014:MarkAssembliesWithClsCompliant")]
+
+namespace Microsoft.PowerShell
+{
+ [SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors")]
+ [SuppressMessage("Microsoft.Usage", "CA2237:MarkISerializableTypesWithSerializable")]
+ class ExitException : Exception { }
+
+ public partial class PSConsoleReadLine : IPSConsoleReadLineMockableMethods
+ {
+ private static readonly PSConsoleReadLine _singleton = new PSConsoleReadLine();
+
+ private bool _delayedOneTimeInitCompleted;
+
+ private IPSConsoleReadLineMockableMethods _mockableMethods;
+ private IConsole _console;
+
+ private EngineIntrinsics _engineIntrinsics;
+#if !UNIX
+ private static GCHandle _breakHandlerGcHandle;
+#endif
+ private Thread _readKeyThread;
+ private AutoResetEvent _readKeyWaitHandle;
+ private AutoResetEvent _keyReadWaitHandle;
+ private ManualResetEvent _closingWaitHandle;
+ private WaitHandle[] _threadProcWaitHandles;
+ private WaitHandle[] _requestKeyWaitHandles;
+ private object _savedConsoleInputMode;
+
+ private readonly StringBuilder _buffer;
+ private readonly StringBuilder _statusBuffer;
+ private bool _statusIsErrorMessage;
+ private string _statusLinePrompt;
+ private List _edits;
+ private int _editGroupStart;
+ private int _undoEditIndex;
+ private int _mark;
+ private bool _inputAccepted;
+ private readonly Queue _queuedKeys;
+ private Stopwatch _lastRenderTime;
+ private static Stopwatch _readkeyStopwatch = new Stopwatch();
+
+ // Save a fixed # of keys so we can reconstruct a repro after a crash
+ private readonly static HistoryQueue _lastNKeys = new HistoryQueue(200);
+
+ // Tokens etc.
+ private Token[] _tokens;
+ private Ast _ast;
+ private ParseError[] _parseErrors;
+
+ bool IPSConsoleReadLineMockableMethods.RunspaceIsRemote(Runspace runspace)
+ {
+ return runspace != null && runspace.ConnectionInfo != null;
+ }
+
+ private void ReadOneOrMoreKeys()
+ {
+ _readkeyStopwatch.Restart();
+ while (_console.KeyAvailable)
+ {
+ var key = _console.ReadKey();
+ _lastNKeys.Enqueue(key);
+ _queuedKeys.Enqueue(key);
+ if (_readkeyStopwatch.ElapsedMilliseconds > 2)
+ {
+ // Don't spend too long in this loop if there are lots of queued keys
+ break;
+ }
+ }
+
+ if (_queuedKeys.Count == 0)
+ {
+ var key = _console.ReadKey();
+ _lastNKeys.Enqueue(key);
+ _queuedKeys.Enqueue(key);
+ }
+ }
+
+ private void ReadKeyThreadProc()
+ {
+ while (true)
+ {
+ // Wait until ReadKey tells us to read a key (or it's time to exit).
+ int handleId = WaitHandle.WaitAny(_singleton._threadProcWaitHandles);
+ if (handleId == 1) // It was the _closingWaitHandle that was signaled.
+ break;
+
+ ReadOneOrMoreKeys();
+
+ // One or more keys were read - let ReadKey know we're done.
+ _keyReadWaitHandle.Set();
+ }
+ }
+
+ private static ConsoleKeyInfo ReadKey()
+ {
+ // Reading a key is handled on a different thread. During process shutdown,
+ // PowerShell will wait in it's ConsoleCtrlHandler until the pipeline has completed.
+ // If we're running, we're most likely blocked waiting for user input.
+ // This is a problem for two reasons. First, exiting takes a long time (5 seconds
+ // on Win8) because PowerShell is waiting forever, but the OS will forcibly terminate
+ // the console. Also - if there are any event handlers for the engine event
+ // PowerShell.Exiting, those handlers won't get a chance to run.
+ //
+ // By waiting for a key on a different thread, our pipeline execution thread
+ // (the thread Readline is called from) avoid being blocked in code that can't
+ // be unblocked and instead blocks on events we control.
+
+ // First, set an event so the thread to read a key actually attempts to read a key.
+ _singleton._readKeyWaitHandle.Set();
+
+ int handleId;
+ System.Management.Automation.PowerShell ps = null;
+
+ try
+ {
+ while (true)
+ {
+ // Next, wait for one of three things:
+ // - a key is pressed
+ // - the console is exiting
+ // - 300ms - to process events if we're idle
+
+ handleId = WaitHandle.WaitAny(_singleton._requestKeyWaitHandles, 300);
+ if (handleId != WaitHandle.WaitTimeout)
+ break;
+ if (_singleton._engineIntrinsics == null)
+ continue;
+
+ // If we timed out, check for event subscribers (which is just
+ // a hint that there might be an event waiting to be processed.)
+ var eventSubscribers = _singleton._engineIntrinsics.Events.Subscribers;
+ if (eventSubscribers.Count > 0)
+ {
+ bool runPipelineForEventProcessing = false;
+ foreach (var sub in eventSubscribers)
+ {
+ if (sub.SourceIdentifier.Equals("PowerShell.OnIdle", StringComparison.OrdinalIgnoreCase))
+ {
+ // There is an OnIdle event. We're idle because we timed out. Normally
+ // PowerShell generates this event, but PowerShell assumes the engine is not
+ // idle because it called PSConsoleHostReadline which isn't returning.
+ // So we generate the event instead.
+ _singleton._engineIntrinsics.Events.GenerateEvent("PowerShell.OnIdle", null, null, null);
+ runPipelineForEventProcessing = true;
+ break;
+ }
+
+ // If there are any event subscribers that have an action (which might
+ // write to the console) and have a source object (i.e. aren't engine
+ // events), run a tiny useless bit of PowerShell so that the events
+ // can be processed.
+ if (sub.Action != null && sub.SourceObject != null)
+ {
+ runPipelineForEventProcessing = true;
+ break;
+ }
+ }
+
+ if (runPipelineForEventProcessing)
+ {
+ if (ps == null)
+ {
+ ps = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace);
+ ps.AddScript("0");
+ }
+
+ // To detect output during possible event processing, see if the cursor moved
+ // and rerender if so.
+ var console = _singleton._console;
+ var y = console.CursorTop;
+ ps.Invoke();
+ if (y != console.CursorTop)
+ {
+ _singleton._initialY = console.CursorTop;
+ _singleton.Render();
+ }
+ }
+ }
+ }
+ }
+ finally
+ {
+ if (ps != null) { ps.Dispose(); }
+ }
+
+ if (handleId == 1)
+ {
+ // The console is exiting - throw an exception to unwind the stack to the point
+ // where we can return from ReadLine.
+ if (_singleton.Options.HistorySaveStyle == HistorySaveStyle.SaveAtExit)
+ {
+ _singleton.SaveHistoryAtExit();
+ }
+ _singleton._historyFileMutex.Dispose();
+
+ throw new OperationCanceledException();
+ }
+
+ var key = _singleton._queuedKeys.Dequeue();
+ return key;
+ }
+
+ private void PrependQueuedKeys(ConsoleKeyInfo key)
+ {
+ if (_queuedKeys.Count > 0)
+ {
+ // This should almost never happen so being inefficient is fine.
+ var list = new List(_queuedKeys);
+ _queuedKeys.Clear();
+ _queuedKeys.Enqueue(key);
+ list.ForEach(k => _queuedKeys.Enqueue(k));
+ }
+ else
+ {
+ _queuedKeys.Enqueue(key);
+ }
+ }
+
+ private bool BreakHandler(ConsoleBreakSignal signal)
+ {
+ if (signal == ConsoleBreakSignal.Close || signal == ConsoleBreakSignal.Shutdown)
+ {
+ // Set the event so ReadKey throws an exception to unwind.
+ _closingWaitHandle.Set();
+ }
+
+ return false;
+ }
+
+ ///
+ /// Entry point - called from the PowerShell function PSConsoleHostReadline
+ /// after the prompt has been displayed.
+ ///
+ /// The complete command line.
+ public static string ReadLine(Runspace runspace, EngineIntrinsics engineIntrinsics)
+ {
+ var console = _singleton._console;
+
+ // If either stdin or stdout is redirected, PSReadline doesn't really work, so throw
+ // and let PowerShell call Console.ReadLine or do whatever else it decides to do.
+ if (Console.IsInputRedirected || Console.IsOutputRedirected)
+ {
+ throw new NotSupportedException();
+ }
+
+ _singleton._savedConsoleInputMode = _singleton._console.GetConsoleInputMode();
+ bool firstTime = true;
+ while (true)
+ {
+ try
+ {
+ _singleton._console.SetConsoleInputMode(_singleton._savedConsoleInputMode);
+
+ if (firstTime)
+ {
+ firstTime = false;
+ _singleton.Initialize(runspace, engineIntrinsics);
+ }
+
+ return _singleton.InputLoop();
+ }
+ catch (OperationCanceledException)
+ {
+ // Console is exiting - return value isn't too critical - null or 'exit' could work equally well.
+ return "";
+ }
+ catch (ExitException)
+ {
+ return "exit";
+ }
+ catch (CustomHandlerException e)
+ {
+ var oldColor = console.ForegroundColor;
+ console.ForegroundColor = ConsoleColor.Red;
+ console.WriteLine(
+ string.Format(CultureInfo.CurrentUICulture, PSReadLineResources.OopsCustomHandlerException, e.InnerException.Message));
+ console.ForegroundColor = oldColor;
+
+ var lineBeforeCrash = _singleton._buffer.ToString();
+ _singleton.Initialize(runspace, _singleton._engineIntrinsics);
+ InvokePrompt();
+ Insert(lineBeforeCrash);
+ }
+ catch (Exception e)
+ {
+ // If we're running tests, just throw.
+ if (_singleton._mockableMethods != _singleton)
+ {
+ throw;
+ }
+
+ while (e.InnerException != null)
+ {
+ e = e.InnerException;
+ }
+ var oldColor = console.ForegroundColor;
+ console.ForegroundColor = ConsoleColor.Red;
+ console.WriteLine(PSReadLineResources.OopsAnErrorMessage1);
+ console.ForegroundColor = oldColor;
+ var sb = new StringBuilder();
+ for (int i = 0; i < _lastNKeys.Count; i++)
+ {
+ sb.Append(' ');
+ sb.Append(_lastNKeys[i].ToGestureString());
+
+ KeyHandler handler;
+ if (_singleton._dispatchTable.TryGetValue(_lastNKeys[i], out handler) &&
+ "AcceptLine".Equals(handler.BriefDescription, StringComparison.OrdinalIgnoreCase))
+ {
+ // Make it a little easier to see the keys
+ sb.Append('\n');
+ }
+ }
+
+ console.WriteLine(string.Format(CultureInfo.CurrentUICulture, PSReadLineResources.OopsAnErrorMessage2, _lastNKeys.Count, sb, e));
+ var lineBeforeCrash = _singleton._buffer.ToString();
+ _singleton.Initialize(runspace, _singleton._engineIntrinsics);
+ InvokePrompt();
+ Insert(lineBeforeCrash);
+ }
+ finally
+ {
+ _singleton._console.RestoreConsoleInputMode(_singleton._savedConsoleInputMode);
+ }
+ }
+ }
+
+ private string InputLoop()
+ {
+ ProcessViVisualEditing();
+
+ while (true)
+ {
+ var killCommandCount = _killCommandCount;
+ var yankCommandCount = _yankCommandCount;
+ var tabCommandCount = _tabCommandCount;
+ var searchHistoryCommandCount = _searchHistoryCommandCount;
+ var recallHistoryCommandCount = _recallHistoryCommandCount;
+ var yankLastArgCommandCount = _yankLastArgCommandCount;
+ var visualSelectionCommandCount = _visualSelectionCommandCount;
+ var movingAtEndOfLineCount = _moveToLineCommandCount;
+
+ var key = ReadKey();
+ ProcessOneKey(key, _dispatchTable, ignoreIfNoAction: false, arg: null);
+ if (_inputAccepted)
+ {
+ return MaybeAddToHistory(_buffer.ToString(), _edits, _undoEditIndex, readingHistoryFile: false, fromDifferentSession: false);
+ }
+
+ if (killCommandCount == _killCommandCount)
+ {
+ // Reset kill command count if it didn't change
+ _killCommandCount = 0;
+ }
+ if (yankCommandCount == _yankCommandCount)
+ {
+ // Reset yank command count if it didn't change
+ _yankCommandCount = 0;
+ }
+ if (yankLastArgCommandCount == _yankLastArgCommandCount)
+ {
+ // Reset yank last arg command count if it didn't change
+ _yankLastArgCommandCount = 0;
+ _yankLastArgState = null;
+ }
+ if (tabCommandCount == _tabCommandCount)
+ {
+ // Reset tab command count if it didn't change
+ _tabCommandCount = 0;
+ _tabCompletions = null;
+ }
+ if (searchHistoryCommandCount == _searchHistoryCommandCount)
+ {
+ if (_searchHistoryCommandCount > 0)
+ {
+ _emphasisStart = -1;
+ _emphasisLength = 0;
+ Render();
+ _currentHistoryIndex = _history.Count;
+ }
+ _searchHistoryCommandCount = 0;
+ _searchHistoryPrefix = null;
+ }
+ if (recallHistoryCommandCount == _recallHistoryCommandCount)
+ {
+ if (_recallHistoryCommandCount > 0)
+ {
+ _currentHistoryIndex = _history.Count;
+ }
+ _recallHistoryCommandCount = 0;
+ }
+ if (searchHistoryCommandCount == _searchHistoryCommandCount &&
+ recallHistoryCommandCount == _recallHistoryCommandCount)
+ {
+ _hashedHistory = null;
+ }
+ if (visualSelectionCommandCount == _visualSelectionCommandCount && _visualSelectionCommandCount > 0)
+ {
+ _visualSelectionCommandCount = 0;
+ Render(); // Clears the visual selection
+ }
+ if (movingAtEndOfLineCount == _moveToLineCommandCount)
+ {
+ _moveToLineCommandCount = 0;
+ }
+ }
+ }
+
+ T CalloutUsingDefaultConsoleMode(Func func)
+ {
+ var currentMode = _console.GetConsoleInputMode();
+ try
+ {
+ _console.RestoreConsoleInputMode(_savedConsoleInputMode);
+ return func();
+ }
+ finally
+ {
+ _console.RestoreConsoleInputMode(currentMode);
+ }
+ }
+
+ void CalloutUsingDefaultConsoleMode(Action action)
+ {
+ CalloutUsingDefaultConsoleMode