Skip to content

Commit 8691e88

Browse files
committed
Initial refactor console api usage
To get the unit tests passing in VS2015, and to help w/ possible future hosts, I've moved most of the console api usage to a class, which is then mocked in the unit tests.
1 parent 2c780b9 commit 8691e88

22 files changed

+669
-460
lines changed

PSReadLine/BasicEditing.cs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using System.Management.Automation;
1010
using System.Management.Automation.Language;
1111
using System.Management.Automation.Runspaces;
12+
using Microsoft.PowerShell.Internal;
1213

1314
namespace Microsoft.PowerShell
1415
{
@@ -98,14 +99,16 @@ public static void CancelLine(ConsoleKeyInfo? key = null, object arg = null)
9899
// Now that we've rendered with this extra spaces, go back and replace the spaces
99100
// with ^C colored in red (so it stands out.)
100101
var coordinates = _singleton.ConvertOffsetToCoordinates(_singleton._current);
101-
int i = (coordinates.Y - _singleton._initialY) * Console.BufferWidth + coordinates.X;
102-
_singleton._consoleBuffer[i].UnicodeChar = '^';
103-
_singleton._consoleBuffer[i].ForegroundColor = ConsoleColor.Red;
104-
_singleton._consoleBuffer[i].BackgroundColor = Console.BackgroundColor;
105-
_singleton._consoleBuffer[i+1].UnicodeChar = 'C';
106-
_singleton._consoleBuffer[i+1].ForegroundColor = ConsoleColor.Red;
107-
_singleton._consoleBuffer[i+1].BackgroundColor = Console.BackgroundColor;
108-
WriteBufferLines(_singleton._consoleBuffer, ref _singleton._initialY);
102+
var console = _singleton._console;
103+
var consoleBuffer = _singleton._consoleBuffer;
104+
int i = (coordinates.Y - _singleton._initialY) * console.BufferWidth + coordinates.X;
105+
consoleBuffer[i].UnicodeChar = '^';
106+
consoleBuffer[i].ForegroundColor = ConsoleColor.Red;
107+
consoleBuffer[i].BackgroundColor = console.BackgroundColor;
108+
consoleBuffer[i+1].UnicodeChar = 'C';
109+
consoleBuffer[i+1].ForegroundColor = ConsoleColor.Red;
110+
consoleBuffer[i+1].BackgroundColor = console.BackgroundColor;
111+
console.WriteBufferLines(consoleBuffer, ref _singleton._initialY);
109112

110113
var y = coordinates.Y + 1;
111114
_singleton.PlaceCursor(0, ref y);

PSReadLine/Completion.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ private static void InvertSelectedCompletion(CHAR_INFO[] buffer, int selectedIte
297297
{
298298
var selectedX = selectedItem / menuRows;
299299
var selectedY = selectedItem - (selectedX * menuRows);
300-
var start = selectedY * Console.BufferWidth + selectedX * menuColumnWidth;
300+
var start = selectedY * _singleton._console.BufferWidth + selectedX * menuColumnWidth;
301301
for (int i = 0; i < menuColumnWidth; i++)
302302
{
303303
int j = i + start;
@@ -349,15 +349,15 @@ private void PossibleCompletionsImpl(CommandCompletion completions, bool menuSel
349349
var menuColumnWidth = minColWidth;
350350

351351
int displayRows;
352-
var bufferWidth = Console.BufferWidth;
352+
var bufferWidth = _console.BufferWidth;
353353
ConsoleBufferBuilder cb;
354354
if (Options.ShowToolTips)
355355
{
356356
const string seperator = "- ";
357357
var maxTooltipWidth = bufferWidth - minColWidth - seperator.Length;
358358

359359
displayRows = matches.Count;
360-
cb = new ConsoleBufferBuilder(displayRows * bufferWidth);
360+
cb = new ConsoleBufferBuilder(displayRows * bufferWidth, _console);
361361
for (int index = 0; index < matches.Count; index++)
362362
{
363363
var match = matches[index];
@@ -389,7 +389,7 @@ private void PossibleCompletionsImpl(CommandCompletion completions, bool menuSel
389389
var screenColumns = bufferWidth;
390390
var displayColumns = Math.Max(1, screenColumns / minColWidth);
391391
displayRows = (completions.CompletionMatches.Count + displayColumns - 1) / displayColumns;
392-
cb = new ConsoleBufferBuilder(displayRows * bufferWidth);
392+
cb = new ConsoleBufferBuilder(displayRows * bufferWidth, _console);
393393
for (var row = 0; row < displayRows; row++)
394394
{
395395
for (var col = 0; col < displayColumns; col++)
@@ -421,7 +421,7 @@ private void PossibleCompletionsImpl(CommandCompletion completions, bool menuSel
421421

422422
var endBufferCoords = ConvertOffsetToCoordinates(_buffer.Length);
423423
var bufferLines = endBufferCoords.Y - _initialY + 1;
424-
if ((bufferLines + displayRows) > Console.WindowHeight)
424+
if ((bufferLines + displayRows) > _console.WindowHeight)
425425
{
426426
menuSelect = false;
427427
}
@@ -444,7 +444,7 @@ private void PossibleCompletionsImpl(CommandCompletion completions, bool menuSel
444444
var previousMenuTop = menuAreaTop;
445445

446446
InvertSelectedCompletion(menuBuffer, selectedItem, menuColumnWidth, displayRows);
447-
WriteBufferLines(menuBuffer, ref menuAreaTop);
447+
_console.WriteBufferLines(menuBuffer, ref menuAreaTop);
448448

449449
// Showing the menu may have scrolled the screen or moved the cursor, update initialY to reflect that.
450450
_initialY -= (previousMenuTop - menuAreaTop);
@@ -505,7 +505,7 @@ private void PossibleCompletionsImpl(CommandCompletion completions, bool menuSel
505505

506506
InvertSelectedCompletion(menuBuffer, previousItem, menuColumnWidth, displayRows);
507507
InvertSelectedCompletion(menuBuffer, selectedItem, menuColumnWidth, displayRows);
508-
WriteBufferLines(menuBuffer, ref menuAreaTop);
508+
_console.WriteBufferLines(menuBuffer, ref menuAreaTop);
509509
previousItem = selectedItem;
510510

511511
if (previousMenuTop > menuAreaTop)
@@ -544,7 +544,7 @@ private void PossibleCompletionsImpl(CommandCompletion completions, bool menuSel
544544
var endBufferCoords = ConvertOffsetToCoordinates(_buffer.Length);
545545
var menuAreaTop = endBufferCoords.Y + 1;
546546

547-
WriteBufferLines(menuBuffer, ref menuAreaTop);
547+
_console.WriteBufferLines(menuBuffer, ref menuAreaTop);
548548
_initialY = menuAreaTop + displayRows;
549549
Render();
550550
}

PSReadLine/ConsoleBufferBuilder.cs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,30 +4,28 @@
44

55
using System;
66
using System.Collections.Generic;
7+
using Microsoft.PowerShell.Internal;
78

89
namespace Microsoft.PowerShell
910
{
1011
internal class ConsoleBufferBuilder
1112
{
1213
private List<CHAR_INFO> buffer;
14+
private IConsole _console;
1315

14-
public ConsoleBufferBuilder()
15-
{
16-
buffer = new List<CHAR_INFO>();
17-
}
18-
19-
public ConsoleBufferBuilder(int capacity)
16+
public ConsoleBufferBuilder(int capacity, IConsole console)
2017
{
2118
buffer = new List<CHAR_INFO>(capacity);
19+
_console = console;
2220
}
2321

2422
CHAR_INFO NewCharInfo(char c)
2523
{
2624
return new CHAR_INFO
2725
{
2826
UnicodeChar = c,
29-
BackgroundColor = Console.BackgroundColor,
30-
ForegroundColor = Console.ForegroundColor
27+
BackgroundColor = _console.BackgroundColor,
28+
ForegroundColor = _console.ForegroundColor
3129
};
3230
}
3331

PSReadLine/ConsoleLib.cs

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@
33
--********************************************************************/
44

55
using System;
6+
using System.ComponentModel;
67
using System.Diagnostics.CodeAnalysis;
78
using System.Globalization;
89
using System.Runtime.InteropServices;
910
using System.Text;
11+
using Microsoft.PowerShell.Internal;
12+
using Microsoft.Win32.SafeHandles;
1013

1114
namespace Microsoft.PowerShell
1215
{
@@ -403,4 +406,206 @@ public static string ToGestureString(this ConsoleKeyInfo key)
403406
return sb.ToString();
404407
}
405408
}
409+
410+
internal class ConhostConsole : IConsole
411+
{
412+
private readonly Lazy<SafeFileHandle> _inputHandle = new Lazy<SafeFileHandle>(() =>
413+
{
414+
// We use CreateFile here instead of GetStdWin32Handle, as GetStdWin32Handle will return redirected handles
415+
var handle = NativeMethods.CreateFile(
416+
"CONIN$",
417+
(UInt32)(AccessQualifiers.GenericRead | AccessQualifiers.GenericWrite),
418+
(UInt32)ShareModes.ShareWrite,
419+
(IntPtr)0,
420+
(UInt32)CreationDisposition.OpenExisting,
421+
0,
422+
(IntPtr)0);
423+
424+
if (handle == NativeMethods.INVALID_HANDLE_VALUE)
425+
{
426+
int err = Marshal.GetLastWin32Error();
427+
Win32Exception innerException = new Win32Exception(err);
428+
throw new Exception("Failed to retreive the input console handle.", innerException);
429+
}
430+
431+
return new SafeFileHandle(handle, true);
432+
});
433+
434+
public uint GetConsoleInputMode()
435+
{
436+
var handle = _inputHandle.Value.DangerousGetHandle();
437+
uint result;
438+
NativeMethods.GetConsoleMode(handle, out result);
439+
return result;
440+
}
441+
442+
public void SetConsoleInputMode(uint mode)
443+
{
444+
var handle = _inputHandle.Value.DangerousGetHandle();
445+
NativeMethods.SetConsoleMode(handle, mode);
446+
}
447+
448+
public ConsoleKeyInfo ReadKey()
449+
{
450+
return Console.ReadKey(true);
451+
}
452+
453+
public bool KeyAvailable
454+
{
455+
get { return Console.KeyAvailable; }
456+
}
457+
458+
public int CursorLeft
459+
{
460+
get { return Console.CursorLeft; }
461+
set { Console.CursorLeft = value; }
462+
}
463+
464+
public int CursorTop
465+
{
466+
get { return Console.CursorTop; }
467+
set { Console.CursorTop = value; }
468+
}
469+
470+
public int CursorSize
471+
{
472+
get { return Console.CursorSize; }
473+
set { Console.CursorSize = value; }
474+
}
475+
476+
public int BufferWidth
477+
{
478+
get { return Console.BufferWidth; }
479+
set { Console.BufferWidth = value; }
480+
}
481+
482+
public int BufferHeight
483+
{
484+
get { return Console.BufferHeight; }
485+
set { Console.BufferHeight = value; }
486+
}
487+
488+
public int WindowWidth
489+
{
490+
get { return Console.WindowWidth; }
491+
set { Console.WindowWidth = value; }
492+
}
493+
494+
public int WindowHeight
495+
{
496+
get { return Console.WindowHeight; }
497+
set { Console.WindowHeight = value; }
498+
}
499+
500+
public int WindowTop
501+
{
502+
get { return Console.WindowTop; }
503+
set { Console.WindowTop = value; }
504+
}
505+
506+
public ConsoleColor BackgroundColor
507+
{
508+
get { return Console.BackgroundColor; }
509+
set { Console.BackgroundColor = value; }
510+
}
511+
512+
public ConsoleColor ForegroundColor
513+
{
514+
get { return Console.ForegroundColor; }
515+
set { Console.ForegroundColor = value; }
516+
}
517+
518+
public void SetWindowPosition(int left, int top)
519+
{
520+
Console.SetWindowPosition(left, top);
521+
}
522+
523+
public void SetCursorPosition(int left, int top)
524+
{
525+
Console.SetCursorPosition(left, top);
526+
}
527+
528+
public void Write(string value)
529+
{
530+
Console.Write(value);
531+
}
532+
533+
public void WriteLine(string value)
534+
{
535+
Console.WriteLine(value);
536+
}
537+
538+
public void WriteBufferLines(CHAR_INFO[] buffer, ref int top)
539+
{
540+
var handle = NativeMethods.GetStdHandle((uint) StandardHandleId.Output);
541+
542+
int bufferWidth = Console.BufferWidth;
543+
int bufferLineCount = buffer.Length / bufferWidth;
544+
if ((top + bufferLineCount) > Console.BufferHeight)
545+
{
546+
var scrollCount = (top + bufferLineCount) - Console.BufferHeight;
547+
ScrollBuffer(scrollCount);
548+
top -= scrollCount;
549+
}
550+
var bufferSize = new COORD
551+
{
552+
X = (short) bufferWidth,
553+
Y = (short) bufferLineCount
554+
};
555+
var bufferCoord = new COORD {X = 0, Y = 0};
556+
var bottom = top + bufferLineCount - 1;
557+
var writeRegion = new SMALL_RECT
558+
{
559+
Top = (short) top,
560+
Left = 0,
561+
Bottom = (short) bottom,
562+
Right = (short) (bufferWidth - 1)
563+
};
564+
NativeMethods.WriteConsoleOutput(handle, buffer,
565+
bufferSize, bufferCoord, ref writeRegion);
566+
567+
// Now make sure the bottom line is visible
568+
if (bottom >= (Console.WindowTop + Console.WindowHeight))
569+
{
570+
Console.CursorTop = bottom;
571+
}
572+
}
573+
574+
public void ScrollBuffer(int lines)
575+
{
576+
var handle = NativeMethods.GetStdHandle((uint) StandardHandleId.Output);
577+
578+
var scrollRectangle = new SMALL_RECT
579+
{
580+
Top = (short) lines,
581+
Left = 0,
582+
Bottom = (short)(Console.BufferHeight - 1),
583+
Right = (short)Console.BufferWidth
584+
};
585+
var destinationOrigin = new COORD {X = 0, Y = 0};
586+
var fillChar = new CHAR_INFO(' ', Console.ForegroundColor, Console.BackgroundColor);
587+
NativeMethods.ScrollConsoleScreenBuffer(handle, ref scrollRectangle, IntPtr.Zero, destinationOrigin, ref fillChar);
588+
}
589+
590+
public CHAR_INFO[] ReadBufferLines(int top, int count)
591+
{
592+
var result = new CHAR_INFO[BufferWidth * count];
593+
var handle = NativeMethods.GetStdHandle((uint) StandardHandleId.Output);
594+
595+
var readBufferSize = new COORD {
596+
X = (short)BufferWidth,
597+
Y = (short)count};
598+
var readBufferCoord = new COORD {X = 0, Y = 0};
599+
var readRegion = new SMALL_RECT
600+
{
601+
Top = (short)top,
602+
Left = 0,
603+
Bottom = (short)(top + count),
604+
Right = (short)(BufferWidth - 1)
605+
};
606+
NativeMethods.ReadConsoleOutput(handle, result,
607+
readBufferSize, readBufferCoord, ref readRegion);
608+
return result;
609+
}
610+
}
406611
}

0 commit comments

Comments
 (0)