diff --git a/PSReadLine/BasicEditing.cs b/PSReadLine/BasicEditing.cs
index 0f99ff1..9aac1e9 100644
--- a/PSReadLine/BasicEditing.cs
+++ b/PSReadLine/BasicEditing.cs
@@ -88,7 +88,8 @@ namespace PSConsoleUtilities
_singleton._consoleBuffer[i+1].BackgroundColor = Console.BackgroundColor;
WriteBufferLines(_singleton._consoleBuffer, ref _singleton._initialY);
- _singleton.PlaceCursor(0, coordinates.Y + 1);
+ var y = coordinates.Y + 1;
+ _singleton.PlaceCursor(0, ref y);
_singleton._buffer.Clear(); // Clear so we don't actually run the input
_singleton._currentHistoryIndex = _singleton._history.Count;
_singleton._inputAccepted = true;
@@ -216,7 +217,8 @@ namespace PSConsoleUtilities
ReallyRender();
}
var coordinates = ConvertOffsetToCoordinates(_current);
- PlaceCursor(0, coordinates.Y + 1);
+ var y = coordinates.Y + 1;
+ PlaceCursor(0, ref y);
_inputAccepted = true;
return true;
}
diff --git a/PSReadLine/Changes.txt b/PSReadLine/Changes.txt
index de1b007..051de94 100644
--- a/PSReadLine/Changes.txt
+++ b/PSReadLine/Changes.txt
@@ -1,3 +1,9 @@
+
+### Version 1.0.0.9
+
+Bug fixes:
+* Items loaded from history work with RevertLine
+
### Version 1.0.0.8
New features:
diff --git a/PSReadLine/Completion.cs b/PSReadLine/Completion.cs
index 1588e65..cd56cd6 100644
--- a/PSReadLine/Completion.cs
+++ b/PSReadLine/Completion.cs
@@ -5,7 +5,6 @@ using System.Linq;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Text;
-using System.Text.RegularExpressions;
using PSConsoleUtilities.Internal;
namespace PSConsoleUtilities
@@ -83,11 +82,27 @@ namespace PSConsoleUtilities
///
public static void Complete(ConsoleKeyInfo? key = null, object arg = null)
{
- var completions = _singleton.GetCompletions();
+ _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.
+ ///
+ public static void MenuComplete(ConsoleKeyInfo? key = null, object arg = null)
+ {
+ _singleton.CompleteImpl(key, arg, true);
+ }
+
+ private void CompleteImpl(ConsoleKeyInfo? key, object arg, bool menuSelect)
+ {
+ var completions = GetCompletions();
if (completions == null || completions.CompletionMatches.Count == 0)
return;
- if (_singleton._tabCommandCount > 0)
+ if (_tabCommandCount > 0)
{
if (completions.CompletionMatches.Count == 1)
{
@@ -95,7 +110,7 @@ namespace PSConsoleUtilities
}
else
{
- PossibleCompletions();
+ PossibleCompletionsImpl(completions, menuSelect);
}
return;
}
@@ -107,7 +122,13 @@ namespace PSConsoleUtilities
// completions, then we'll be showing the possible completions where it's very
// unlikely that we would add a trailing backslash.
- _singleton.DoReplacementForCompletion(completions.CompletionMatches[0], completions);
+ DoReplacementForCompletion(completions.CompletionMatches[0], completions);
+ return;
+ }
+
+ if (menuSelect)
+ {
+ PossibleCompletionsImpl(completions, true);
return;
}
@@ -159,10 +180,10 @@ namespace PSConsoleUtilities
{
// No common prefix, don't wait for a second tab, just show the possible completions
// right away.
- PossibleCompletions();
+ PossibleCompletionsImpl(completions, false);
}
- _singleton._tabCommandCount += 1;
+ _tabCommandCount += 1;
}
private CommandCompletion GetCompletions()
@@ -259,96 +280,247 @@ namespace PSConsoleUtilities
return replacementText;
}
+ private static void InvertSelectedCompletion(CHAR_INFO[] buffer, int selectedItem, int menuColumnWidth, int menuRows)
+ {
+ var selectedX = selectedItem / menuRows;
+ var selectedY = selectedItem - (selectedX * menuRows);
+ var start = selectedY * Console.BufferWidth + selectedX * menuColumnWidth;
+ for (int i = 0; i < menuColumnWidth; i++)
+ {
+ int j = i + start;
+ buffer[j].ForegroundColor = (ConsoleColor)((int)buffer[j].ForegroundColor ^ 7);
+ buffer[j].BackgroundColor = (ConsoleColor)((int)buffer[j].BackgroundColor ^ 7);
+ }
+ }
+
///
/// Display the list of possible completions.
///
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 >= _singleton._options.CompletionQueryItems)
+ if (completions.CompletionMatches.Count >= _options.CompletionQueryItems)
{
- if (!_singleton.PromptYesOrNo(string.Format(PSReadLineResources.DisplayAllPossibilities, completions.CompletionMatches.Count)))
+ if (!PromptYesOrNo(string.Format(PSReadLineResources.DisplayAllPossibilities, completions.CompletionMatches.Count)))
{
return;
}
}
- // 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);
- _singleton.PlaceCursor(0, coords.Y + 1);
-
- var sb = new StringBuilder();
- var matches = completions.CompletionMatches.Select(
- completion =>
- new {ListItemText = Regex.Replace(completion.ListItemText, "\n.*", "..."),
- ToolTip = Regex.Replace(completion.ToolTip, "\n.*", "...")})
- .ToArray();
+ var matches = completions.CompletionMatches;
var minColWidth = matches.Max(c => c.ListItemText.Length);
minColWidth += 2;
+ var menuColumnWidth = minColWidth;
- if (_singleton.Options.ShowToolTips)
+ int displayRows;
+ var bufferWidth = Console.BufferWidth;
+ ConsoleBufferBuilder cb;
+ if (Options.ShowToolTips)
{
const string seperator = "- ";
- var maxTooltipWidth = Console.BufferWidth - minColWidth - seperator.Length;
+ var maxTooltipWidth = bufferWidth - minColWidth - seperator.Length;
- foreach (var match in matches)
+ displayRows = matches.Count;
+ cb = new ConsoleBufferBuilder(displayRows * bufferWidth);
+ for (int index = 0; index < matches.Count; index++)
{
- sb.Append(match.ListItemText);
- var spacesNeeded = minColWidth - match.ListItemText.Length;
- if (spacesNeeded > 0)
- sb.Append(' ', spacesNeeded);
- sb.Append(seperator);
- var toolTip = match.ToolTip.Length <= maxTooltipWidth
- ? match.ToolTip
- : match.ToolTip.Substring(0, maxTooltipWidth);
- sb.Append(toolTip.Trim());
-
- // Make sure we always write out exactly 1 buffer width
- spacesNeeded = Console.BufferWidth - sb.Length;
+ var match = matches[index];
+ var listItemText = HandleNewlinesForPossibleCompletions(match.ListItemText);
+ cb.Append(listItemText);
+ var spacesNeeded = minColWidth - listItemText.Length;
if (spacesNeeded > 0)
{
- sb.Append(' ', spacesNeeded);
+ 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);
}
- Console.Write(sb.ToString());
- sb.Clear();
}
+ menuColumnWidth = bufferWidth;
}
else
{
- var screenColumns = Console.BufferWidth;
+ var screenColumns = bufferWidth;
var displayColumns = Math.Max(1, screenColumns / minColWidth);
- var displayRows = (completions.CompletionMatches.Count + displayColumns - 1) / displayColumns;
+ displayRows = (completions.CompletionMatches.Count + displayColumns - 1) / displayColumns;
+ cb = new ConsoleBufferBuilder(displayRows * bufferWidth);
for (var row = 0; row < displayRows; row++)
{
for (var col = 0; col < displayColumns; col++)
{
var index = row + (displayRows * col);
- if (index >= matches.Length)
+ if (index >= matches.Count)
break;
- var item = matches[index].ListItemText;
- sb.Append(item);
- sb.Append(' ', minColWidth - item.Length);
+ 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 = Console.BufferWidth - sb.Length;
+ var spacesNeeded = (bufferWidth * (row + 1)) - cb.Length;
if (spacesNeeded > 0)
{
- sb.Append(' ', spacesNeeded);
+ cb.Append(' ', spacesNeeded);
}
- Console.Write(sb.ToString());
- sb.Clear();
}
}
- _singleton._initialY = Console.CursorTop;
- _singleton.Render();
+ 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)
+ {
+ StartEditGroup();
+
+ 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);
+ WriteBufferLines(menuBuffer, ref menuAreaTop);
+
+ if (previousMenuTop != menuAreaTop)
+ {
+ // Showing the menu scrolled the screen, 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);
+ WriteBufferLines(menuBuffer, ref menuAreaTop);
+ previousItem = selectedItem;
+
+ if (previousMenuTop > menuAreaTop)
+ {
+ WriteBlankLines(previousMenuTop - menuAreaTop, menuAreaTop + displayRows);
+ }
+ }
+ }
+
+ WriteBlankLines(displayRows, menuAreaTop);
+
+ EndEditGroup();
+
+ if (undo)
+ {
+ // Pretend it never happened.
+ Undo();
+ }
+ }
+ else
+ {
+ var endBufferCoords = ConvertOffsetToCoordinates(_buffer.Length);
+ var menuAreaTop = endBufferCoords.Y + 1;
+
+ WriteBufferLines(menuBuffer, ref menuAreaTop);
+ _initialY = menuAreaTop + displayRows;
+ Render();
+ }
}
}
}
diff --git a/PSReadLine/ConsoleBufferBuilder.cs b/PSReadLine/ConsoleBufferBuilder.cs
new file mode 100644
index 0000000..34331a6
--- /dev/null
+++ b/PSReadLine/ConsoleBufferBuilder.cs
@@ -0,0 +1,61 @@
+using System;
+using System.Collections.Generic;
+
+namespace PSConsoleUtilities
+{
+ internal class ConsoleBufferBuilder
+ {
+ private List buffer;
+
+ public ConsoleBufferBuilder()
+ {
+ buffer = new List();
+ }
+
+ public ConsoleBufferBuilder(int capacity)
+ {
+ buffer = new List(capacity);
+ }
+
+ CHAR_INFO NewCharInfo(char c)
+ {
+ return new CHAR_INFO
+ {
+ UnicodeChar = c,
+ BackgroundColor = Console.BackgroundColor,
+ ForegroundColor = Console.ForegroundColor
+ };
+ }
+ public void Append(char c)
+ {
+ buffer.Add(NewCharInfo(c));
+ }
+
+ 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 CHAR_INFO[] ToArray()
+ {
+ return buffer.ToArray();
+ }
+
+ public int Length
+ {
+ get { return buffer.Count; }
+ }
+ }
+}
+
diff --git a/PSReadLine/ConsoleLib.cs b/PSReadLine/ConsoleLib.cs
index 18ab214..4e9570d 100644
--- a/PSReadLine/ConsoleLib.cs
+++ b/PSReadLine/ConsoleLib.cs
@@ -33,7 +33,7 @@ namespace PSConsoleUtilities
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool ScrollConsoleScreenBuffer(IntPtr hConsoleOutput,
ref SMALL_RECT lpScrollRectangle,
- ref SMALL_RECT lpClipRectangle,
+ IntPtr lpClipRectangle,
COORD dwDestinationOrigin,
ref CHAR_INFO lpFill);
diff --git a/PSReadLine/History.cs b/PSReadLine/History.cs
index 85d329c..b46728d 100644
--- a/PSReadLine/History.cs
+++ b/PSReadLine/History.cs
@@ -63,7 +63,8 @@ namespace PSConsoleUtilities
public static void AddToHistory(string command)
{
command = command.Replace("\r\n", "\n");
- _singleton.MaybeAddToHistory(command, new List(), 0);
+ var editItems = new List {EditItemInsertString.Create(command, 0)};
+ _singleton.MaybeAddToHistory(command, editItems, 0);
}
///
@@ -127,7 +128,7 @@ namespace PSConsoleUtilities
while (true)
{
newHistoryIndex = newHistoryIndex + direction;
- if (newHistoryIndex < 0 && newHistoryIndex >= _history.Count)
+ if (newHistoryIndex < 0 || newHistoryIndex >= _history.Count)
{
break;
}
@@ -149,12 +150,11 @@ namespace PSConsoleUtilities
newHistoryIndex = _currentHistoryIndex + direction;
}
_recallHistoryCommandCount += 1;
- if (newHistoryIndex >= 0 && newHistoryIndex < _history.Count)
+ if (newHistoryIndex >= 0 && newHistoryIndex <= _history.Count)
{
_currentHistoryIndex = newHistoryIndex;
UpdateFromHistory(moveCursor: true);
}
-
}
///
@@ -189,9 +189,9 @@ namespace PSConsoleUtilities
}
_searchHistoryCommandCount += 1;
- for (int i = _currentHistoryIndex + direction; i >=0 && i < _history.Count; i += direction)
+ for (int i = _currentHistoryIndex + direction; i >= 0 && i <= _history.Count; i += direction)
{
- var line = _history[i]._line;
+ var line = i == _history.Count ? _savedCurrentLine._line : _history[i]._line;
if (line.StartsWith(_searchHistoryPrefix, Options.HistoryStringComparison))
{
if (Options.HistoryNoDuplicates)
@@ -376,18 +376,7 @@ namespace PSConsoleUtilities
}
else if (EndInteractiveHistorySearch(key, function))
{
- 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);
- }
+ PrependQueuedKeys(key);
break;
}
else
@@ -441,6 +430,7 @@ namespace PSConsoleUtilities
InteractiveHistorySearchLoop(direction);
_hashedHistory = null;
+ _currentHistoryIndex = _history.Count;
// Remove our status line
_statusBuffer.Clear();
diff --git a/PSReadLine/KeyBindings.cs b/PSReadLine/KeyBindings.cs
index 1422dca..fa1e0b8 100644
--- a/PSReadLine/KeyBindings.cs
+++ b/PSReadLine/KeyBindings.cs
@@ -108,7 +108,6 @@ namespace PSConsoleUtilities
{ Keys.CtrlSpace, MakeKeyHandler(PossibleCompletions, "PossibleCompletions") },
{ Keys.Tab, MakeKeyHandler(TabCompleteNext, "TabCompleteNext") },
{ Keys.ShiftTab, MakeKeyHandler(TabCompletePrevious, "TabCompletePrevious") },
- { Keys.CtrlV, MakeKeyHandler(Paste, "Paste") },
{ Keys.VolumeDown, MakeKeyHandler(Ignore, "Ignore") },
{ Keys.VolumeUp, MakeKeyHandler(Ignore, "Ignore") },
{ Keys.VolumeMute, MakeKeyHandler(Ignore, "Ignore") },
@@ -118,6 +117,7 @@ namespace PSConsoleUtilities
{ 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") },
@@ -275,7 +275,8 @@ namespace PSConsoleUtilities
// 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);
- _singleton.PlaceCursor(0, coords.Y + 1);
+ var y = coords.Y + 1;
+ _singleton.PlaceCursor(0, ref y);
Console.WriteLine(buffer.ToString());
_singleton._initialY = Console.CursorTop;
@@ -334,7 +335,8 @@ namespace PSConsoleUtilities
// 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);
- _singleton.PlaceCursor(0, coords.Y + 1);
+ var y = coords.Y + 1;
+ _singleton.PlaceCursor(0, ref y);
Console.WriteLine(buffer.ToString());
_singleton._initialY = Console.CursorTop;
diff --git a/PSReadLine/PSReadLine.csproj b/PSReadLine/PSReadLine.csproj
index 277968a..c67ac5b 100644
--- a/PSReadLine/PSReadLine.csproj
+++ b/PSReadLine/PSReadLine.csproj
@@ -53,6 +53,7 @@
+
@@ -85,9 +86,9 @@
ResXFileCodeGenerator
- PSReadLineResources.Designer.cs
PSConsoleUtilities
Designer
+ PSReadLineResources.Designer.cs
diff --git a/PSReadLine/PSReadLineResources.Designer.cs b/PSReadLine/PSReadLineResources.Designer.cs
index 65b4914..159ba9d 100644
--- a/PSReadLine/PSReadLineResources.Designer.cs
+++ b/PSReadLine/PSReadLineResources.Designer.cs
@@ -591,6 +591,15 @@ namespace PSConsoleUtilities {
}
}
+ ///
+ /// 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..
///
diff --git a/PSReadLine/PSReadLineResources.resx b/PSReadLine/PSReadLineResources.resx
index 64cb7da..8f6f3fe 100644
--- a/PSReadLine/PSReadLineResources.resx
+++ b/PSReadLine/PSReadLineResources.resx
@@ -534,4 +534,7 @@
Replace the previous word.
+
+ Complete the input if there is a single completion, otherwise complete the input by selecting from a menu of possible completions.
+
\ No newline at end of file
diff --git a/PSReadLine/ReadLine.cs b/PSReadLine/ReadLine.cs
index 85e84ea..fae21ce 100644
--- a/PSReadLine/ReadLine.cs
+++ b/PSReadLine/ReadLine.cs
@@ -31,7 +31,6 @@ namespace PSConsoleUtilities
private readonly StringBuilder _statusBuffer;
private string _statusLinePrompt;
private List _edits;
- private int _editGroupCount;
private readonly Stack _pushedEditGroupCount;
private int _undoEditIndex;
private int _mark;
@@ -124,6 +123,22 @@ namespace PSConsoleUtilities
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)
@@ -215,12 +230,17 @@ namespace PSConsoleUtilities
_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 &&
@@ -315,7 +335,6 @@ namespace PSConsoleUtilities
_buffer.Clear();
_edits = new List();
_undoEditIndex = 0;
- _editGroupCount = 0;
_pushedEditGroupCount.Clear();
_current = 0;
_mark = 0;
diff --git a/PSReadLine/Render.cs b/PSReadLine/Render.cs
index 76176a7..2c7f73a 100644
--- a/PSReadLine/Render.cs
+++ b/PSReadLine/Render.cs
@@ -255,15 +255,34 @@ namespace PSConsoleUtilities
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) (top + bufferLineCount - 1),
+ Bottom = (short) bottom,
Right = (short) bufferWidth
};
NativeMethods.WriteConsoleOutput(handle, buffer,
bufferSize, bufferCoord, ref writeRegion);
+
+ // Now make sure the bottom line is visible
+ if (bottom >= (Console.WindowTop + Console.WindowHeight))
+ {
+ Console.CursorTop = bottom;
+ }
+ }
+
+ private static void WriteBlankLines(int count, int top)
+ {
+ var blanks = new CHAR_INFO[count * Console.BufferWidth];
+ for (int i = 0; i < count; i++)
+ {
+ blanks[i].BackgroundColor = Console.BackgroundColor;
+ blanks[i].ForegroundColor = Console.ForegroundColor;
+ blanks[i].UnicodeChar = ' ';
+ }
+ WriteBufferLines(blanks, ref top);
}
private static CHAR_INFO[] ReadBufferLines(int top, int count)
@@ -420,15 +439,15 @@ namespace PSConsoleUtilities
{
Top = (short) lines,
Left = 0,
- Bottom = (short) (lines + Console.BufferHeight - 1),
+ Bottom = (short) (Console.BufferHeight - 1),
Right = (short)Console.BufferWidth
};
var destinationOrigin = new COORD {X = 0, Y = 0};
var fillChar = new CHAR_INFO(' ', Console.ForegroundColor, Console.BackgroundColor);
- NativeMethods.ScrollConsoleScreenBuffer(handle, ref scrollRectangle, ref scrollRectangle, destinationOrigin, ref fillChar);
+ NativeMethods.ScrollConsoleScreenBuffer(handle, ref scrollRectangle, IntPtr.Zero, destinationOrigin, ref fillChar);
}
- private void PlaceCursor(int x, int y)
+ private void PlaceCursor(int x, ref int y)
{
int statusLineCount = GetStatusLineCount();
if ((y + _demoWindowLineCount + statusLineCount) >= Console.BufferHeight)
@@ -442,7 +461,8 @@ namespace PSConsoleUtilities
private void PlaceCursor()
{
var coordinates = ConvertOffsetToCoordinates(_current);
- PlaceCursor(coordinates.X, coordinates.Y);
+ int y = coordinates.Y;
+ PlaceCursor(coordinates.X, ref y);
}
private COORD ConvertOffsetToCoordinates(int offset)
diff --git a/PSReadLine/SamplePSReadlineProfile.ps1 b/PSReadLine/SamplePSReadlineProfile.ps1
index 1f99189..9628b40 100644
--- a/PSReadLine/SamplePSReadlineProfile.ps1
+++ b/PSReadLine/SamplePSReadlineProfile.ps1
@@ -355,3 +355,50 @@ Set-PSReadlineKeyHandler -Key F1 `
}
}
}
+
+
+#
+# Ctrl+Shift+j then type a key to mark the current directory.
+# Ctrj+j then the same key will change back to that directory without
+# needing to type cd and won't change the command line.
+
+#
+$global:PSReadlineMarks = @{}
+
+Set-PSReadlineKeyHandler -Key Ctrl+Shift+j `
+ -BriefDescription MarkDirectory `
+ -LongDescription "Mark the current directory" `
+ -ScriptBlock {
+ param($key, $arg)
+
+ $key = [Console]::ReadKey($true)
+ $global:PSReadlineMarks[$key.KeyChar] = $pwd
+}
+
+Set-PSReadlineKeyHandler -Key Ctrl+j `
+ -BriefDescription JumpDirectory `
+ -LongDescription "Goto the marked directory" `
+ -ScriptBlock {
+ param($key, $arg)
+
+ $key = [Console]::ReadKey()
+ $dir = $global:PSReadlineMarks[$key.KeyChar]
+ if ($dir)
+ {
+ cd $dir
+ [PSConsoleUtilities.PSConsoleReadLine]::InvokePrompt()
+ }
+}
+
+Set-PSReadlineKeyHandler -Key Alt+j `
+ -BriefDescription ShowDirectoryMarks `
+ -LongDescription "Show the currently marked directories" `
+ -ScriptBlock {
+ param($key, $arg)
+
+ $global:PSReadlineMarks.GetEnumerator() | % {
+ [PSCustomObject]@{Key = $_.Key; Dir = $_.Value} } |
+ Format-Table -AutoSize | Out-Host
+
+ [PSConsoleUtilities.PSConsoleReadLine]::InvokePrompt()
+}
diff --git a/PSReadLine/UndoRedo.cs b/PSReadLine/UndoRedo.cs
index f3997db..1423f27 100644
--- a/PSReadLine/UndoRedo.cs
+++ b/PSReadLine/UndoRedo.cs
@@ -9,7 +9,7 @@ namespace PSConsoleUtilities
private void SaveEditItem(EditItem editItem)
{
// If there is some sort of edit after an undo, forget
- // the
+ // any edit items that were undone.
int removeCount = _edits.Count - _undoEditIndex;
if (removeCount > 0)
{
@@ -17,23 +17,20 @@ namespace PSConsoleUtilities
}
_edits.Add(editItem);
_undoEditIndex = _edits.Count;
- _editGroupCount++;
}
private void StartEditGroup()
{
- _pushedEditGroupCount.Push(_editGroupCount);
- _editGroupCount = 0;
+ _pushedEditGroupCount.Push(_edits.Count);
}
private void EndEditGroup(Action instigator = null, object instigatorArg = null)
{
- // The last _editGroupCount edits are treated as a single item for undo
- var start = _edits.Count - _editGroupCount;
- var groupedEditItems = _edits.GetRange(start, _editGroupCount);
- _edits.RemoveRange(start, _editGroupCount);
- SaveEditItem(GroupedEdit.Create(groupedEditItems, instigator, instigatorArg));
- _editGroupCount = _pushedEditGroupCount.Pop();
+ var groupEditStart = _pushedEditGroupCount.Pop();
+ var groupEditCount = _edits.Count - groupEditStart;
+ var groupedEditItems = _edits.GetRange(groupEditStart, groupEditCount);
+ _edits.RemoveRange(groupEditStart, groupEditCount);
+ SaveEditItem(GroupedEdit.Create(groupedEditItems));
}
///
diff --git a/PSReadLine/en-US/about_PSReadline.help.txt b/PSReadLine/en-US/about_PSReadline.help.txt
index 8c129c6..98e01bc 100644
--- a/PSReadLine/en-US/about_PSReadline.help.txt
+++ b/PSReadLine/en-US/about_PSReadline.help.txt
@@ -247,6 +247,15 @@ LONG DESCRIPTION
prefix is used for completion. If trying to complete the longest
unambiguous completion, a list of possible completions is displayed.
+ MenuComplete (Cmd: unbound Emacs: unbound)
+
+ Attempt to perform completion on the text surrounding the cursor.
+ If there are multiple possible completions, a list of possible
+ completions is displayed and you can select the correct completion
+ using the arrow keys or Tab/Shift+Tab. Escape and Ctrl+G cancel
+ the menu selection and reverts the line to the state before invoking
+ MenuComplete.
+
PossibleCompletions (Cmd: Emacs: )
Display the list of possible completions.
diff --git a/TestPSReadLine/Program.cs b/TestPSReadLine/Program.cs
index 173863e..860637c 100644
--- a/TestPSReadLine/Program.cs
+++ b/TestPSReadLine/Program.cs
@@ -100,10 +100,13 @@ namespace TestPSReadLine
PSConsoleReadLine.SetOptions(new SetPSReadlineOption
{
- EditMode = EditMode.Emacs,
+ EditMode = EditMode.Windows,
+ HistoryNoDuplicates = true,
});
- //PSConsoleReadLine.SetKeyHandler(new[] {"UpArrow"}, PSConsoleReadLine.HistorySearchBackward, "", "");
- //PSConsoleReadLine.SetKeyHandler(new[] {"DownArrow"}, PSConsoleReadLine.HistorySearchForward, "", "");
+ PSConsoleReadLine.SetKeyHandler(new[] {"Ctrl+LeftArrow"}, PSConsoleReadLine.ShellBackwardWord, "", "");
+ PSConsoleReadLine.SetKeyHandler(new[] {"Ctrl+RightArrow"}, PSConsoleReadLine.ShellNextWord, "", "");
+ PSConsoleReadLine.SetKeyHandler(new[] {"UpArrow"}, PSConsoleReadLine.HistorySearchBackward, "", "");
+ PSConsoleReadLine.SetKeyHandler(new[] {"DownArrow"}, PSConsoleReadLine.HistorySearchForward, "", "");
//PSConsoleReadLine.SetKeyHandler(new[] {"Ctrl+D,Ctrl+E"}, PSConsoleReadLine.EnableDemoMode, "", "");
//PSConsoleReadLine.SetKeyHandler(new[] {"Ctrl+D,Ctrl+D"}, PSConsoleReadLine.DisableDemoMode, "", "");
//PSConsoleReadLine.SetKeyHandler(new[] {"Ctrl+D,Ctrl+C"}, PSConsoleReadLine.CaptureScreen, "", "");
diff --git a/UnitTestPSReadLine/HistoryTest.cs b/UnitTestPSReadLine/HistoryTest.cs
index 0fbbcd9..5948607 100644
--- a/UnitTestPSReadLine/HistoryTest.cs
+++ b/UnitTestPSReadLine/HistoryTest.cs
@@ -34,6 +34,26 @@ namespace UnitTestPSReadLine
Test("dir c*", Keys(_.UpArrow, _.UpArrow, _.DownArrow));
}
+ [TestMethod]
+ public void TestHistoryRecallCurrentLine()
+ {
+ TestSetup(KeyMode.Cmd);
+
+ SetHistory("echo foo", "echo bar");
+ Test("ec", Keys("ec", _.UpArrow, _.UpArrow, _.DownArrow, _.DownArrow));
+ }
+
+ [TestMethod]
+ public void TestHistorySearchCurrentLine()
+ {
+ TestSetup(KeyMode.Cmd,
+ new KeyHandler("UpArrow", PSConsoleReadLine.HistorySearchBackward),
+ new KeyHandler("DownArrow", PSConsoleReadLine.HistorySearchForward));
+
+ SetHistory("echo foo", "echo bar");
+ Test("ec", Keys("ec", _.UpArrow, _.UpArrow, _.DownArrow, _.DownArrow));
+ }
+
[TestMethod]
public void TestSearchHistory()
{
@@ -422,6 +442,10 @@ namespace UnitTestPSReadLine
Test("cccc", Keys(
Enumerable.Repeat(_.UpArrow, 3),
Enumerable.Repeat(_.DownArrow, 2)));
+
+ // No history
+ SetHistory();
+ Test("", Keys(_.UpArrow, _.DownArrow));
}
[TestMethod]
diff --git a/UnitTestPSReadLine/HistoryTests.playlist b/UnitTestPSReadLine/HistoryTests.playlist
index faaa7b3..d1da242 100644
--- a/UnitTestPSReadLine/HistoryTests.playlist
+++ b/UnitTestPSReadLine/HistoryTests.playlist
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/UnitTestPSReadLine/UnitTestReadLine.cs b/UnitTestPSReadLine/UnitTestReadLine.cs
index 6fcb770..9d9ba7f 100644
--- a/UnitTestPSReadLine/UnitTestReadLine.cs
+++ b/UnitTestPSReadLine/UnitTestReadLine.cs
@@ -455,7 +455,7 @@ namespace UnitTestPSReadLine
};
var destinationOrigin = new COORD {X = 0, Y = 0};
var fillChar = new CHAR_INFO(' ', Console.ForegroundColor, Console.BackgroundColor);
- NativeMethods.ScrollConsoleScreenBuffer(handle, ref scrollRectangle, ref scrollRectangle, destinationOrigin, ref fillChar);
+ NativeMethods.ScrollConsoleScreenBuffer(handle, ref scrollRectangle, IntPtr.Zero, destinationOrigin, ref fillChar);
}
private static void WriteBufferLines(CHAR_INFO[] buffer, ref int top)