Commits

Oisin Grehan  committed eec6642

final push for 1.0 - mostly around prompt munging

  • Participants
  • Parent commits ef1758c

Comments (0)

Files changed (8)

File Mono.Terminal.LineEditor/LineEditor.cs

 //
 // Authors:
 //   Miguel de Icaza (miguel@novell.com)
+//   
+// Modifications/bugfixes for PowerShell 3.0 and CLR4 on Windows:
+//   Oisin Grehan (oising@gmail.com)
 //
 // Copyright 2008 Novell, Inc.
 //
 // Dual-licensed under the terms of the MIT X11 license or the
 // Apache License 2.0
 //
-//
-// TODO:
-//    Enter an error (a = 1);  Notice how the prompt is in the wrong line
-//    This is caused by Stderr not being tracked by System.Console.
-//    Completion support
-//    Why is Thread.Interrupt not working?   Currently I resort to Abort which is too much.
-//
-// Limitations in System.Console:
-//    Console needs SIGWINCH support of some sort
-//    Console needs a way of updating its position after things have been written
-//    behind its back (P/Invoke puts for example).
-//    System.Console needs to get the DELETE character, and report accordingly.
-//
 
 using System;
+using System.Collections;
+using System.Diagnostics;
 using System.Text;
 using System.IO;
 using System.Threading;
 
 namespace Nivot.PowerShell
 {
-    public class LineEditor
+    internal class LineEditor
     {
         public class Completion
         {
 
         private string _shownPrompt;
 
+        // powershell already evaluates the prompt, so we should not but still take it into account
+        private bool _shouldShowPrompt;
+
         // The current cursor position, indexes into "text", for an index
         // into rendered_text, use TextToRenderPos
         private int _cursor;
 
             public Handler(ConsoleKey key, KeyHandler h)
             {
-                this.KeyInfo = new ConsoleKeyInfo((char)0, key, false, false, false);
+                this.KeyInfo = new ConsoleKeyInfo((char)0, key, shift: false, alt: false, control: false);
                 KeyHandler = h;
             }
 
             {
                 KeyHandler = h;
                 // Use the "Zoom" as a flag that we only have a character.
-                this.KeyInfo = new ConsoleKeyInfo(c, ConsoleKey.Zoom, false, false, false);
+                this.KeyInfo = new ConsoleKeyInfo(c, ConsoleKey.Zoom, shift: false, alt: false, control: false);
             }
 
             public Handler(ConsoleKeyInfo keyInfo, KeyHandler h)
 
         private void Render()
         {
-            Console.Write(this._shownPrompt);
+            if (_shouldShowPrompt)
+            {
+                Console.Write(this._shownPrompt);
+            }
+
             Console.Write(this._renderedText);
 
             int max = Math.Max(this._renderedText.Length + this._shownPrompt.Length, this._maxRendered);
             try {
                 promptText = _prompt();
             }
-            catch {
+            catch (Exception ex) {
                 // swallow prompt delegate errors
+                Trace.WriteLine("GetPromptSafe error: " + ex);
             }
             return promptText;
         }
                             CmdKillToEOF();
                         }
                         Console.WriteLine();
-                        foreach (string s in completions)
+                        var fg = Console.ForegroundColor;
+                        try
                         {
-                            Console.Write(completion.Prefix);
-                            Console.Write(s);
-                            Console.Write(' ');
+                            Console.ForegroundColor = ConsoleColor.Yellow;
+                            foreach (string s in completions)
+                            {
+                                Console.Write(completion.Prefix);
+                                Console.Write(s);
+                                Console.Write(' ');
+                            }
+                        }
+                        finally
+                        {
+                            Console.ForegroundColor = fg;
                         }
                         Console.WriteLine();
                         Render();
         /// </summary>
         /// <param name="promptHandler"></param>
         /// <param name="defaultPrompt"></param>
+        /// <param name="shouldShowPrompt"> </param>
         /// <param name="initialText"></param>
         /// <returns></returns>
-        public string Edit(Func<string> promptHandler, string initialText = "", string defaultPrompt = "> ")
+        public string Edit(Func<string> promptHandler = null, bool shouldShowPrompt = true, Hashtable options = null, string initialText = "", string defaultPrompt = "PS> ")
         {
             this._defaultPrompt = defaultPrompt;
             this._editThread = Thread.CurrentThread;
             this._maxRendered = 0;
 
             this._prompt = promptHandler;
-            this._shownPrompt = GetPromptSafe();
-            
+            this._shownPrompt = promptHandler != null ?  GetPromptSafe() : String.Empty;
+            this._shouldShowPrompt = shouldShowPrompt;
+
             InitText(initialText);
             this._history.Append(initialText);
 

File Mono.Terminal.LineEditor/Modules/PSReadline/PSReadline.psd1

Binary file modified.

File Mono.Terminal.LineEditor/Modules/PSReadline/psreadline.psm1

Binary file modified.

File Mono.Terminal.LineEditor/Modules/PSReadline/readme.txt

+PSReadline 1.0
+    for PowerShell 3.0
+
+Common Bindings
+=========================================
+Home			Cursor Home
+LeftArrow		Cursor Left
+RightArrow		Cursor Right
+UpArrow			History - Previous
+DownArrow		History - Next
+Enter			Done
+Backspace		Backspace
+Delete			Delete Character
+Tab				Tab / Tab Complete
+
+EMACS Bindings
+=========================================
+Ctrl+A			Home
+Ctrl+E			End
+Ctrl+B			Left
+Ctrl+F			Right
+Ctrl+P			History - Previous
+Ctrl+N			History - Next
+Ctrl+K			Kill to EOL
+Ctrl+Y			Yank
+Ctrl+D			Delete Character
+Ctrl+L			Refresh
+Ctrl+R			Reverse Search History
+
+Alt+B			Word - Backwards
+Alt+F			Word - Forwards
+Alt+D			Word - Delete
+Alt+BkSpc		Word - Delete Backwards
+
+=========================================
+Ctrl+Q			Quote
+=========================================

File Mono.Terminal.LineEditor/Nivot.PowerShell.LineEditor.csproj

     <Compile Include="NativeMethods.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="HotKeyManager.cs" />
+    <Compile Include="PSReadline.cs" />
     <Compile Include="PSReadlineBindingsVariable.cs" />
     <Compile Include="Tracer.cs" />
   </ItemGroup>
     <Folder Include="Commands\" />
   </ItemGroup>
   <ItemGroup>
-    <None Include="Modules\PSReadline\PSReadline.psd1" />
-    <None Include="Modules\PSReadline\psreadline.psm1" />
+    <None Include="Modules\PSReadline\PSReadline.psd1">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+    <None Include="Modules\PSReadline\psreadline.psm1">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="Modules\PSReadline\readme.txt" />
   </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 

File Mono.Terminal.LineEditor/PSReadline.cs

+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Management.Automation;
+using System.Management.Automation.Runspaces;
+using System.Text;
+
+namespace Nivot.PowerShell
+{
+    public sealed class PSReadline
+    {
+        public static readonly PSReadline Current = new PSReadline();
+
+        private readonly LineEditor _editor;
+        private bool _shouldReevaluatePrompt;
+        private string _cachedPromptText;
+
+        private PSReadline()
+        {
+            _editor = new LineEditor("PSReadLine");
+
+            _editor.AutoCompleteEvent += (input, position) =>
+            {
+                // ReSharper disable AccessToDisposedClosure
+                var completion = CommandCompletion.CompleteInput(input, position, null);
+                // ReSharper restore AccessToDisposedClosure
+                string[] matches =
+                    (from match in completion.CompletionMatches select match.CompletionText).ToArray();
+
+                if (matches.Length == 0)
+                {
+                    return new LineEditor.Completion("", null, completion.ReplacementIndex);
+                }
+
+                if (matches.Length == 1)
+                {
+                    return new LineEditor.Completion(
+                        "",
+                        new[] { matches[0].Substring(completion.ReplacementLength) },
+                        completion.ReplacementIndex);
+                }
+
+                return new LineEditor.Completion("", matches, completion.ReplacementIndex);
+            };
+        }
+
+        public string Readline()
+        {
+            return Readline(null);
+        }
+
+        public string Readline(Hashtable options)
+        {
+            // We should only evaluate the prompt function once per readline call
+            _shouldReevaluatePrompt = true;
+            string line = _editor.Edit(
+                promptHandler: EvaluatePrompt, shouldShowPrompt:true, options:options);
+            
+            return line;
+        }
+
+        private string EvaluatePrompt()
+        {
+            if (_shouldReevaluatePrompt)
+            {
+                var ps = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace);
+                _cachedPromptText = ps.AddCommand("PSReadlinePrompt").Invoke<string>().Single();
+
+                // This prevents the LineEditor from reevaluating the prompt when performing bash style
+                // tab completion where the prompt line is reprinted after each match list is shown.
+                // this is needed because user prompt functions might not be idempotent (command counter etc.)
+                _shouldReevaluatePrompt = false;
+            }
+
+            return _cachedPromptText;
+        }
+
+        
+
+        //public static Readon EmacsDefaultBindings
+        //{
+        //    get
+        //    {
+        //        return new Hashtable
+        //                   {
+        //                       {"", ""}
+        //                   };
+        //    }
+        //}
+    }
+
+    public struct HotKey
+    {
+        private HotKey(string sequence) : this()
+        {
+            KeyInfo = HotKeyManager.ToConsoleKeyInfo(sequence);
+            Sequence = sequence;
+        }
+
+        public static implicit operator HotKey(string sequence)
+        {
+            return new HotKey(sequence);
+        }
+
+        public string Sequence { get; private set; }
+
+        internal ConsoleKeyInfo KeyInfo { get; private set; }
+        
+        public override string ToString()
+        {
+            return Sequence;
+        }
+    }
+
+    public class LineEditorCommand
+    {
+    }
+}

File MonoREPL/Program.cs

-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Management.Automation;
-using System.Text;
-using System.Linq;
+//using System;
+//using System.Collections.Generic;
+//using System.Linq;
+//using System.Management.Automation;
+//using System.Text;
+//using System.Linq;
 
-using Nivot.PowerShell;
+//using Nivot.PowerShell;
 
-namespace MonoREPL
-{
-    class Program
-    {
-        static void Main(string[] args)
-        {
-            var editor = new LineEditor("PSREPL");
+//namespace MonoREPL
+//{
+//    class Program
+//    {
+//        static void Main(string[] args)
+//        {
+//            var editor = new LineEditor("PSREPL");
             
-            using (var ps = PowerShell.Create()) {
-                editor.AutoCompleteEvent += (input, position) => {
-                    var completion = CommandCompletion.CompleteInput(input, position, null, ps);
-                    string[] matches =
-                        (from match in completion.CompletionMatches select match.CompletionText).ToArray();
+//            using (var ps = PowerShell.Create()) {
+//                editor.AutoCompleteEvent += (input, position) => {
+//// ReSharper disable AccessToDisposedClosure
+//                    var completion = CommandCompletion.CompleteInput(input, position, null, ps);
+//// ReSharper restore AccessToDisposedClosure
+//                    string[] matches =
+//                        (from match in completion.CompletionMatches select match.CompletionText).ToArray();
 
-                    if (matches.Length == 0) {
-                        return new LineEditor.Completion("", null, completion.ReplacementIndex);
-                    }
+//                    if (matches.Length == 0) {
+//                        return new LineEditor.Completion("", null, completion.ReplacementIndex);
+//                    }
 
-                    if (matches.Length == 1) {
-                        return new LineEditor.Completion(
-                            "",
-                            new[] { matches[0].Substring(completion.ReplacementLength) },
-                            completion.ReplacementIndex);
-                    }
+//                    if (matches.Length == 1) {
+//                        return new LineEditor.Completion(
+//                            "",
+//                            new[] { matches[0].Substring(completion.ReplacementLength) },
+//                            completion.ReplacementIndex);
+//                    }
 
-                    // prefix??
-                    return new LineEditor.Completion("", matches, completion.ReplacementIndex);
-                };
+//                    // prefix??
+//                    return new LineEditor.Completion("", matches, completion.ReplacementIndex);
+//                };
 
-                string line;
-                while ((line = editor.Edit(() => "PS> ", initialText: null, defaultPrompt: "")) != null) {
-                    //Console.WriteLine("You typed: " + line);
-                    ps.AddScript(line);
-                    try
-                    {
-                        var output = ps.Invoke();
-                    }
-                    catch (Exception ex) {
-                        Console.WriteLine("Error: " + ex.Message);
-                    }
-                    ps.Commands.Clear();
-                    ps.Streams.ClearStreams();
-                }
-            }
-        }
-    }
-}
+//                string line;
+//                while ((line = editor.Edit(() => "PS> ", initialText: null, defaultPrompt: "")) != null) {
+//                    //Console.WriteLine("You typed: " + line);
+//                    ps.AddScript(line);
+//                    try
+//                    {
+//                        var output = ps.Invoke();
+//                    }
+//                    catch (Exception ex) {
+//                        Console.WriteLine("Error: " + ex.Message);
+//                    }
+//                    ps.Commands.Clear();
+//                    ps.Streams.ClearStreams();
+//                }
+//            }
+//        }
+//    }
+//}

File PSReadline.sln

+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonoREPL", "MonoREPL\MonoREPL.csproj", "{11C6AEB7-BF97-4CC5-9C1A-13EA089818F0}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nivot.PowerShell.LineEditor", "Mono.Terminal.LineEditor\Nivot.PowerShell.LineEditor.csproj", "{219CB418-7458-416E-996B-8B83C65A1A67}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Debug|Mixed Platforms = Debug|Mixed Platforms
+		Debug|x86 = Debug|x86
+		Release|Any CPU = Release|Any CPU
+		Release|Mixed Platforms = Release|Mixed Platforms
+		Release|x86 = Release|x86
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{11C6AEB7-BF97-4CC5-9C1A-13EA089818F0}.Debug|Any CPU.ActiveCfg = Debug|x86
+		{11C6AEB7-BF97-4CC5-9C1A-13EA089818F0}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
+		{11C6AEB7-BF97-4CC5-9C1A-13EA089818F0}.Debug|Mixed Platforms.Build.0 = Debug|x86
+		{11C6AEB7-BF97-4CC5-9C1A-13EA089818F0}.Debug|x86.ActiveCfg = Debug|x86
+		{11C6AEB7-BF97-4CC5-9C1A-13EA089818F0}.Debug|x86.Build.0 = Debug|x86
+		{11C6AEB7-BF97-4CC5-9C1A-13EA089818F0}.Release|Any CPU.ActiveCfg = Release|x86
+		{11C6AEB7-BF97-4CC5-9C1A-13EA089818F0}.Release|Mixed Platforms.ActiveCfg = Release|x86
+		{11C6AEB7-BF97-4CC5-9C1A-13EA089818F0}.Release|Mixed Platforms.Build.0 = Release|x86
+		{11C6AEB7-BF97-4CC5-9C1A-13EA089818F0}.Release|x86.ActiveCfg = Release|x86
+		{11C6AEB7-BF97-4CC5-9C1A-13EA089818F0}.Release|x86.Build.0 = Release|x86
+		{219CB418-7458-416E-996B-8B83C65A1A67}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{219CB418-7458-416E-996B-8B83C65A1A67}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{219CB418-7458-416E-996B-8B83C65A1A67}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+		{219CB418-7458-416E-996B-8B83C65A1A67}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+		{219CB418-7458-416E-996B-8B83C65A1A67}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{219CB418-7458-416E-996B-8B83C65A1A67}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{219CB418-7458-416E-996B-8B83C65A1A67}.Release|Any CPU.Build.0 = Release|Any CPU
+		{219CB418-7458-416E-996B-8B83C65A1A67}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+		{219CB418-7458-416E-996B-8B83C65A1A67}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+		{219CB418-7458-416E-996B-8B83C65A1A67}.Release|x86.ActiveCfg = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal