[Lldb-commits] [lldb] r222163 - Complete rewrite of interactive editing support for single- and multi-line input.

Kate Stone katherine.stone at apple.com
Mon Nov 17 11:07:00 PST 2014


Author: kate
Date: Mon Nov 17 13:06:59 2014
New Revision: 222163

URL: http://llvm.org/viewvc/llvm-project?rev=222163&view=rev
Log:
Complete rewrite of interactive editing support for single- and multi-line input.

Improvements include:
* Use of libedit's wide character support, which is imperfect but a distinct improvement over ASCII-only
* Fallback for ASCII editing path
* Support for a "faint" prompt clearly distinguished from input
* Breaking lines and insert new lines in the middle of a batch by simply pressing return
* Joining lines with forward and backward character deletion
* Detection of paste to suppress automatic formatting and statement completion tests
* Correctly reformatting when lines grow or shrink to occupy different numbers of rows
* Saving multi-line history, and correctly preserving the "tip" of history during editing
* Displaying visible ^C and ^D indications when interrupting input or sending EOF
* Fledgling VI support for multi-line editing
* General correctness and reliability improvements

Modified:
    lldb/trunk/include/lldb/Core/IOHandler.h
    lldb/trunk/include/lldb/Host/Editline.h
    lldb/trunk/include/lldb/Interpreter/CommandInterpreter.h
    lldb/trunk/source/Commands/CommandObjectCommands.cpp
    lldb/trunk/source/Commands/CommandObjectExpression.cpp
    lldb/trunk/source/Core/IOHandler.cpp
    lldb/trunk/source/Host/common/Editline.cpp
    lldb/trunk/source/Interpreter/CommandInterpreter.cpp
    lldb/trunk/source/Interpreter/ScriptInterpreterPython.cpp
    lldb/trunk/source/Target/Process.cpp

Modified: lldb/trunk/include/lldb/Core/IOHandler.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Core/IOHandler.h?rev=222163&r1=222162&r2=222163&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Core/IOHandler.h (original)
+++ lldb/trunk/include/lldb/Core/IOHandler.h Mon Nov 17 13:06:59 2014
@@ -19,9 +19,11 @@
 #include "lldb/Core/ConstString.h"
 #include "lldb/Core/Error.h"
 #include "lldb/Core/Flags.h"
+#include "lldb/Core/Stream.h"
 #include "lldb/Core/StringList.h"
 #include "lldb/Core/ValueObjectList.h"
 #include "lldb/Host/Mutex.h"
+#include "lldb/Host/Predicate.h"
 
 namespace curses
 {
@@ -34,9 +36,23 @@ namespace lldb_private {
     class IOHandler
     {
     public:
-        IOHandler (Debugger &debugger);
+        enum class Type {
+            CommandInterpreter,
+            CommandList,
+            Confirm,
+            Curses,
+            Expression,
+            ProcessIO,
+            PythonInterpreter,
+            PythonCode,
+            Other
+        };
+
+        IOHandler (Debugger &debugger,
+                   IOHandler::Type type);
 
         IOHandler (Debugger &debugger,
+                   IOHandler::Type type,
                    const lldb::StreamFileSP &input_sp,
                    const lldb::StreamFileSP &output_sp,
                    const lldb::StreamFileSP &error_sp,
@@ -97,6 +113,12 @@ namespace lldb_private {
             return m_done;
         }
 
+        Type
+        GetType () const
+        {
+            return m_type;
+        }
+
         virtual void
         Activate ()
         {
@@ -128,7 +150,19 @@ namespace lldb_private {
         {
             return ConstString();
         }
+
+        virtual const char *
+        GetCommandPrefix ()
+        {
+            return NULL;
+        }
         
+        virtual const char *
+        GetHelpPrologue()
+        {
+            return NULL;
+        }
+
         int
         GetInputFD();
         
@@ -206,13 +240,21 @@ namespace lldb_private {
         //------------------------------------------------------------------
         bool
         GetIsRealTerminal ();
-
+        
+        void
+        SetPopped (bool b);
+        
+        void
+        WaitForPop ();
+        
     protected:
         Debugger &m_debugger;
         lldb::StreamFileSP m_input_sp;
         lldb::StreamFileSP m_output_sp;
         lldb::StreamFileSP m_error_sp;
+        Predicate<bool> m_popped;
         Flags m_flags;
+        Type m_type;
         void *m_user_data;
         bool m_done;
         bool m_active;
@@ -254,7 +296,12 @@ namespace lldb_private {
         IOHandlerActivated (IOHandler &io_handler)
         {
         }
-        
+
+        virtual void
+        IOHandlerDeactivated (IOHandler &io_handler)
+        {
+        }
+
         virtual int
         IOHandlerComplete (IOHandler &io_handler,
                            const char *current_line,
@@ -264,6 +311,44 @@ namespace lldb_private {
                            int max_matches,
                            StringList &matches);
         
+        virtual const char *
+        IOHandlerGetFixIndentationCharacters ()
+        {
+            return NULL;
+        }
+        
+        //------------------------------------------------------------------
+        /// Called when a new line is created or one of an identifed set of
+        /// indentation characters is typed.
+        ///
+        /// This function determines how much indentation should be added
+        /// or removed to match the recommended amount for the final line.
+        ///
+        /// @param[in] io_handler
+        ///     The IOHandler that responsible for input.
+        ///
+        /// @param[in] lines
+        ///     The current input up to the line to be corrected.  Lines
+        ///     following the line containing the cursor are not included.
+        ///
+        /// @param[in] cursor_position
+        ///     The number of characters preceeding the cursor on the final
+        ///     line at the time.
+        ///
+        /// @return
+        ///     Returns an integer describing the number of spaces needed
+        ///     to correct the indentation level.  Positive values indicate
+        ///     that spaces should be added, while negative values represent
+        ///     spaces that should be removed.
+        //------------------------------------------------------------------
+        virtual int
+        IOHandlerFixIndentation (IOHandler &io_handler,
+                                 const StringList &lines,
+                                 int cursor_position)
+        {
+            return 0;
+        }
+                        
         //------------------------------------------------------------------
         /// Called when a line or lines have been retrieved.
         ///
@@ -275,40 +360,55 @@ namespace lldb_private {
         //------------------------------------------------------------------
         virtual void
         IOHandlerInputComplete (IOHandler &io_handler, std::string &data) = 0;
-        
+
+        virtual void
+        IOHandlerInputInterrupted (IOHandler &io_handler, std::string &data)
+        {
+        }
+
         //------------------------------------------------------------------
-        /// Called when a line in \a lines has been updated when doing
-        /// multi-line input.
+        /// Called to determine whether typing enter after the last line in
+        /// \a lines should end input.  This function will not be called on
+        /// IOHandler objects that are getting single lines.
+        /// @param[in] io_handler
+        ///     The IOHandler that responsible for updating the lines.
+        ///
+        /// @param[in] lines
+        ///     The current multi-line content.  May be altered to provide
+        ///     alternative input when complete.
         ///
         /// @return
-        ///     Return an enumeration to indicate the status of the current
-        ///     line:
-        ///         Success - The line is good and should be added to the
-        ///                   multiple lines
-        ///         Error - There is an error with the current line and it
-        ///                 need to be re-edited before it is acceptable
-        ///         Done - The lines collection is complete and ready to be
-        ///                returned.
-        //------------------------------------------------------------------
-        virtual LineStatus
-        IOHandlerLinesUpdated (IOHandler &io_handler,
-                               StringList &lines,
-                               uint32_t line_idx,
-                               Error &error)
-        {
-            return LineStatus::Done; // Stop getting lines on the first line that is updated
-            // subclasses should do something more intelligent here.
-            // This function will not be called on IOHandler objects
-            // that are getting single lines.
+        ///     Return an boolean to indicate whether input is complete,
+        ///     true indicates that no additional input is necessary, while
+        ///     false indicates that more input is required.
+        //------------------------------------------------------------------
+        virtual bool
+        IOHandlerIsInputComplete (IOHandler &io_handler,
+                                  StringList &lines)
+        {
+            // Impose no requirements for input to be considered
+            // complete.  subclasses should do something more intelligent.
+            return true;
         }
         
-        
         virtual ConstString
         IOHandlerGetControlSequence (char ch)
         {
             return ConstString();
         }
         
+        virtual const char *
+        IOHandlerGetCommandPrefix ()
+        {
+            return NULL;
+        }
+
+        virtual const char *
+        IOHandlerGetHelpPrologue ()
+        {
+            return NULL;
+        }
+
         //------------------------------------------------------------------
         // Intercept the IOHandler::Interrupt() calls and do something.
         //
@@ -356,30 +456,21 @@ namespace lldb_private {
             return ConstString();
         }
 
-        virtual LineStatus
-        IOHandlerLinesUpdated (IOHandler &io_handler,
-                               StringList &lines,
-                               uint32_t line_idx,
-                               Error &error)
+        virtual bool
+        IOHandlerIsInputComplete (IOHandler &io_handler,
+                                  StringList &lines)
         {
-            if (line_idx == UINT32_MAX)
+            // Determine whether the end of input signal has been entered
+            const size_t num_lines = lines.GetSize();
+            if (num_lines > 0 && lines[num_lines - 1] == m_end_line)
             {
-                // Remove the last empty line from "lines" so it doesn't appear
-                // in our final expression and return true to indicate we are done
+                // Remove the terminal line from "lines" so it doesn't appear in
+                // the resulting input and return true to indicate we are done
                 // getting lines
                 lines.PopBack();
-                return LineStatus::Done;
+                return true;
             }
-            else if (line_idx + 1 == lines.GetSize())
-            {
-                // The last line was edited, if this line is empty, then we are done
-                // getting our multiple lines.
-                if (lines[line_idx] == m_end_line)
-                {
-                    return LineStatus::Done;
-                }
-            }
-            return LineStatus::Success;
+            return false;
         }
     protected:
         const std::string m_end_line;
@@ -390,20 +481,26 @@ namespace lldb_private {
     {
     public:
         IOHandlerEditline (Debugger &debugger,
+                           IOHandler::Type type,
                            const char *editline_name, // Used for saving history files
                            const char *prompt,
+                           const char *continuation_prompt,
                            bool multi_line,
+                           bool color_prompts,
                            uint32_t line_number_start, // If non-zero show line numbers starting at 'line_number_start'
                            IOHandlerDelegate &delegate);
 
         IOHandlerEditline (Debugger &debugger,
+                           IOHandler::Type type,
                            const lldb::StreamFileSP &input_sp,
                            const lldb::StreamFileSP &output_sp,
                            const lldb::StreamFileSP &error_sp,
                            uint32_t flags,
                            const char *editline_name, // Used for saving history files
                            const char *prompt,
+                           const char *continuation_prompt,
                            bool multi_line,
+                           bool color_prompts,
                            uint32_t line_number_start, // If non-zero show line numbers starting at 'line_number_start'
                            IOHandlerDelegate &delegate);
         
@@ -429,11 +526,10 @@ namespace lldb_private {
         GotEOF();
         
         virtual void
-        Activate ()
-        {
-            IOHandler::Activate();
-            m_delegate.IOHandlerActivated(*this);
-        }
+        Activate ();
+
+        virtual void
+        Deactivate ();
 
         virtual ConstString
         GetControlSequence (char ch)
@@ -442,11 +538,29 @@ namespace lldb_private {
         }
 
         virtual const char *
+        GetCommandPrefix ()
+        {
+            return m_delegate.IOHandlerGetCommandPrefix ();
+        }
+
+        virtual const char *
+        GetHelpPrologue ()
+        {
+            return m_delegate.IOHandlerGetHelpPrologue ();
+        }
+
+        virtual const char *
         GetPrompt ();
         
         virtual bool
         SetPrompt (const char *prompt);
-
+        
+        const char *
+        GetContinuationPrompt ();
+        
+        void
+        SetContinuationPrompt (const char *prompt);
+        
         bool
         GetLine (std::string &line, bool &interrupted);
         
@@ -456,15 +570,40 @@ namespace lldb_private {
         void
         SetBaseLineNumber (uint32_t line);
         
+        bool
+        GetInterruptExits ()
+        {
+            return m_interrupt_exits;
+        }
+
+        void
+        SetInterruptExits (bool b)
+        {
+            m_interrupt_exits = b;
+        }
+        
+        const StringList *
+        GetCurrentLines () const
+        {
+            return m_current_lines_ptr;
+        }
+        
+        uint32_t
+        GetCurrentLineIndex () const;
+
     private:
 #ifndef LLDB_DISABLE_LIBEDIT
-        static LineStatus
-        LineCompletedCallback (Editline *editline,
-                               StringList &lines,
-                               uint32_t line_idx,
-                               Error &error,
-                               void *baton);
-
+        static bool
+        IsInputCompleteCallback (Editline *editline,
+                                 StringList &lines,
+                                 void *baton);
+        
+        static int
+        FixIndentationCallback (Editline *editline,
+                                const StringList &lines,
+                                int cursor_position,
+                                void *baton);
+        
         static int AutoCompleteCallback (const char *current_line,
                                          const char *cursor,
                                          const char *last_char,
@@ -480,8 +619,13 @@ namespace lldb_private {
 #endif
         IOHandlerDelegate &m_delegate;
         std::string m_prompt;
+        std::string m_continuation_prompt;
+        StringList *m_current_lines_ptr;
         uint32_t m_base_line_number; // If non-zero, then show line numbers in prompt
-        bool m_multi_line;        
+        uint32_t m_curr_line_idx;
+        bool m_multi_line;
+        bool m_color_prompts;
+        bool m_interrupt_exits;
     };
     
     class IOHandlerConfirm :
@@ -611,7 +755,8 @@ namespace lldb_private {
             if (sp)
             {
                 Mutex::Locker locker (m_mutex);
-                m_stack.push (sp);
+                sp->SetPopped (false);
+                m_stack.push_back (sp);
                 // Set m_top the non-locking IsTop() call
                 m_top = sp.get();
             }
@@ -631,7 +776,7 @@ namespace lldb_private {
             {
                 Mutex::Locker locker (m_mutex);
                 if (!m_stack.empty())
-                    sp = m_stack.top();
+                    sp = m_stack.back();
             }
             return sp;
         }
@@ -641,12 +786,16 @@ namespace lldb_private {
         {
             Mutex::Locker locker (m_mutex);
             if (!m_stack.empty())
-                m_stack.pop();
+            {
+                lldb::IOHandlerSP sp (m_stack.back());
+                m_stack.pop_back();
+                sp->SetPopped (true);
+            }
             // Set m_top the non-locking IsTop() call
             if (m_stack.empty())
                 m_top = NULL;
             else
-                m_top = m_stack.top().get();
+                m_top = m_stack.back().get();
         }
 
         Mutex &
@@ -661,6 +810,19 @@ namespace lldb_private {
             return m_top == io_handler_sp.get();
         }
 
+        bool
+        CheckTopIOHandlerTypes (IOHandler::Type top_type, IOHandler::Type second_top_type)
+        {
+            Mutex::Locker locker (m_mutex);
+            const size_t num_io_handlers = m_stack.size();
+            if (num_io_handlers >= 2 &&
+                m_stack[num_io_handlers-1]->GetType() == top_type &&
+                m_stack[num_io_handlers-2]->GetType() == second_top_type)
+            {
+                return true;
+            }
+            return false;
+        }
         ConstString
         GetTopIOHandlerControlSequence (char ch)
         {
@@ -669,9 +831,26 @@ namespace lldb_private {
             return ConstString();
         }
 
-    protected:
+        const char *
+        GetTopIOHandlerCommandPrefix()
+        {
+            if (m_top)
+                return m_top->GetCommandPrefix();
+            return NULL;
+        }
+        
+        const char *
+        GetTopIOHandlerHelpPrologue()
+        {
+            if (m_top)
+                return m_top->GetHelpPrologue();
+            return NULL;
+        }
+
+    protected:        
         
-        std::stack<lldb::IOHandlerSP> m_stack;
+        typedef std::vector<lldb::IOHandlerSP> collection;
+        collection m_stack;
         mutable Mutex m_mutex;
         IOHandler *m_top;
         

Modified: lldb/trunk/include/lldb/Host/Editline.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Host/Editline.h?rev=222163&r1=222162&r2=222163&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Host/Editline.h (original)
+++ lldb/trunk/include/lldb/Host/Editline.h Mon Nov 17 13:06:59 2014
@@ -7,13 +7,40 @@
 //
 //===----------------------------------------------------------------------===//
 
+//TODO: wire up window size changes
+
+// If we ever get a private copy of libedit, there are a number of defects that would be nice to fix;
+// a) Sometimes text just disappears while editing.  In an 80-column editor paste the following text, without
+//    the quotes:
+//    "This is a test of the input system missing Hello, World!  Do you disappear when it gets to a particular length?"
+//    Now press ^A to move to the start and type 3 characters, and you'll see a good amount of the text will
+//    disappear.  It's still in the buffer, just invisible.
+// b) The prompt printing logic for dealing with ANSI formatting characters is broken, which is why we're
+//    working around it here.
+// c) When resizing the terminal window, if the cursor moves between rows libedit will get confused.
+// d) The incremental search uses escape to cancel input, so it's confused by ANSI sequences starting with escape.
+// e) Emoji support is fairly terrible, presumably it doesn't understand composed characters?
+
 #ifndef liblldb_Editline_h_
 #define liblldb_Editline_h_
 #if defined(__cplusplus)
 
+#include <sstream>
+#include <vector>
+
+// components needed to handle wide characters ( <codecvt>, codecvt_utf8, libedit built with '--enable-widec' )
+// are not consistenly available on non-OSX platforms.  The wchar_t versions of libedit functions will only be
+// used in cases where this is true.  This is a compile time dependecy, for now selected per target Platform
+#if defined (__APPLE__)
+#define LLDB_EDITLINE_USE_WCHAR 1
+#include <codecvt>
+#else
+#define LLDB_EDITLINE_USE_WCHAR 0
+#endif
+
 #include "lldb/lldb-private.h"
+#include "lldb/Host/ConnectionFileDescriptor.h"
 
-#include <stdio.h>
 #if defined(_WIN32)
 #include "lldb/Host/windows/editlinewin.h"
 #else
@@ -32,179 +59,308 @@
 #include "lldb/Host/Predicate.h"
 
 namespace lldb_private {
+    namespace line_editor {
 
-//----------------------------------------------------------------------
-/// @class Editline Editline.h "lldb/Host/Editline.h"
-/// @brief A class that encapsulates editline functionality.
-//----------------------------------------------------------------------
-class EditlineHistory;
-    
-typedef std::shared_ptr<EditlineHistory> EditlineHistorySP;
-    
-class Editline
-{
-public:
-    typedef LineStatus (*LineCompletedCallbackType) (
-        Editline *editline,
-        StringList &lines,
-        uint32_t line_idx,
-        Error &error,
-        void *baton);
-    
-    typedef int (*CompleteCallbackType) (
-        const char *current_line,
-        const char *cursor,
-        const char *last_char,
-        int skip_first_n_matches,
-        int max_matches,
-        StringList &matches,
-        void *baton);
-
-    typedef int (*GetCharCallbackType) (
-        ::EditLine *,
-        char *c);
-    
-    Editline(const char *prog,  // Used for the history file and for editrc program name
-             const char *prompt,
-             bool configure_for_multiline,             
-             FILE *fin,
-             FILE *fout,
-             FILE *ferr);
-
-    ~Editline();
-
-    Error
-    GetLine (std::string &line,
-             bool &interrupted);
-
-    Error
-    GetLines (const std::string &end_line,
-              StringList &lines,
-              bool &interrupted);
-
-    bool
-    LoadHistory ();
-    
-    bool
-    SaveHistory ();
-    
-    FILE *
-    GetInputFile ();
-    
-    FILE *
-    GetOutputFile ();
-    
-    FILE *
-    GetErrorFile ();
-
-    bool
-    GettingLine () const
-    {
-        return m_getting_line;
-    }
-
-    void
-    Hide ();
-
-    void
-    Refresh();
-
-    bool
-    Interrupt ();
-
-    void
-    SetAutoCompleteCallback (CompleteCallbackType callback,
-                             void *baton)
-    {
-        m_completion_callback = callback;
-        m_completion_callback_baton = baton;
-    }
-
-    void
-    SetLineCompleteCallback (LineCompletedCallbackType callback,
-                             void *baton)
-    {
-        m_line_complete_callback = callback;
-        m_line_complete_callback_baton = baton;
-    }
-
-    size_t
-    Push (const char *bytes, size_t len);
-    
-    static int
-    GetCharFromInputFileCallback (::EditLine *e, char *c);
+        // type alias's to help manage 8 bit and wide character versions of libedit
+#if LLDB_EDITLINE_USE_WCHAR
+        using EditLineStringType = std::wstring;
+        using EditLineStringStreamType = std::wstringstream;
+        using EditLineCharType = wchar_t;
+#else
+        using EditLineStringType=std::string;
+        using EditLineStringStreamType = std::stringstream;
+        using EditLineCharType = char;
+#endif
 
-    void
-    SetGetCharCallback (GetCharCallbackType callback);
-    
-    const char *
-    GetPrompt();
-    
-    void
-    SetPrompt (const char *p);
-    
-    void
-    ShowLineNumbers (bool enable, uint32_t line_offset)
-    {
-        m_prompt_with_line_numbers = enable;
-        m_line_offset = line_offset;
+        typedef int (* EditlineGetCharCallbackType)(::EditLine * editline, EditLineCharType * c);
+        typedef unsigned char (* EditlineCommandCallbackType)(::EditLine * editline, int ch);
+        typedef const char * (* EditlinePromptCallbackType)(::EditLine * editline);
+
+        class EditlineHistory;
+        
+        typedef std::shared_ptr<EditlineHistory> EditlineHistorySP;
+
+        typedef bool (* IsInputCompleteCallbackType) (
+            Editline * editline,
+            StringList & lines,
+            void * baton);
+        
+        typedef int (* FixIndentationCallbackType) (
+            Editline * editline,
+            const StringList & lines,
+            int cursor_position,
+            void * baton);
+        
+        typedef int (* CompleteCallbackType) (
+            const char * current_line,
+            const char * cursor,
+            const char * last_char,
+            int skip_first_n_matches,
+            int max_matches,
+            StringList & matches,
+            void * baton);
+        
+        /// Status used to decide when and how to start editing another line in multi-line sessions
+        enum class EditorStatus
+        {
+            
+            /// The default state proceeds to edit the current line
+            Editing,
+            
+            /// Editing complete, returns the complete set of edited lines
+            Complete,
+            
+            /// End of input reported
+            EndOfInput,
+
+            /// Editing interrupted
+            Interrupted
+        };
+        
+        /// Established locations that can be easily moved among with MoveCursor
+        enum class CursorLocation
+        {
+            /// The start of the first line in a multi-line edit session
+            BlockStart,
+            
+            /// The start of the current line in a multi-line edit session
+            EditingPrompt,
+            
+            /// The location of the cursor on the current line in a multi-line edit session
+            EditingCursor,
+            
+            /// The location immediately after the last character in a multi-line edit session
+            BlockEnd
+        };
     }
     
-private:
-
-    Error
-    PrivateGetLine(std::string &line);
-    
-    unsigned char
-    HandleCompletion (int ch);
-
-    static unsigned char
-    CallbackEditPrevLine (::EditLine *e, int ch);
-    
-    static unsigned char
-    CallbackEditNextLine (::EditLine *e, int ch);
-    
-    static unsigned char
-    CallbackComplete (::EditLine *e, int ch);
-
-    static const char *
-    GetPromptCallback (::EditLine *e);
-
-    static Editline *
-    GetClientData (::EditLine *e);
+    using namespace line_editor;
     
-    static FILE *
-    GetFilePointer (::EditLine *e, int fd);
-
-    enum class Command
+    /// Instances of Editline provide an abstraction over libedit's EditLine facility.  Both
+    /// single- and multi-line editing are supported.
+    class Editline
     {
-        None = 0,
-        EditPrevLine,
-        EditNextLine,
+    public:
+        Editline (const char * editor_name, FILE * input_file, FILE * output_file, FILE * error_file, bool color_prompts);
+        
+        ~Editline();
+        
+        /// Uses the user data storage of EditLine to retrieve an associated instance of Editline.
+        static Editline *
+        InstanceFor (::EditLine * editline);
+        
+        /// Sets a string to be used as a prompt, or combined with a line number to form a prompt.
+        void
+        SetPrompt (const char * prompt);
+        
+        /// Sets an alternate string to be used as a prompt for the second line and beyond in multi-line
+        /// editing scenarios.
+        void
+        SetContinuationPrompt (const char * continuation_prompt);
+        
+        /// Required to update the width of the terminal registered for I/O.  It is critical that this
+        /// be correct at all times.
+        void
+        TerminalSizeChanged();
+        
+        /// Returns the prompt established by SetPrompt()
+        const char *
+        GetPrompt();
+        
+        /// Returns the index of the line currently being edited
+        uint32_t
+        GetCurrentLine();
+
+        /// Hides the current input session in preparation for output
+        void
+        Hide();
+        
+        /// Prepare to return to editing after a call to Hide()
+        void
+        Refresh();
+
+        /// Interrupt the current edit as if ^C was pressed
+        bool
+        Interrupt();
+        
+        /// Register a callback for the tab key
+        void
+        SetAutoCompleteCallback (CompleteCallbackType callback, void * baton);
+        
+        /// Register a callback for testing whether multi-line input is complete
+        void
+        SetIsInputCompleteCallback (IsInputCompleteCallbackType callback, void * baton);
+        
+        /// Register a callback for determining the appropriate indentation for a line
+        /// when creating a newline.  An optional set of insertable characters can also
+        /// trigger the callback.
+        bool
+        SetFixIndentationCallback (FixIndentationCallbackType callback,
+                                   void * baton,
+                                   const char * indent_chars);
+
+        /// Prompts for and reads a single line of user input.
+        bool
+        GetLine (std::string &line, bool &interrupted);
+        
+        /// Prompts for and reads a multi-line batch of user input.
+        bool
+        GetLines (int first_line_number, StringList &lines, bool &interrupted);
+        
+    private:
+        
+        /// Sets the lowest line number for multi-line editing sessions.  A value of zero suppresses
+        /// line number printing in the prompt.
+        void
+        SetBaseLineNumber (int line_number);
+        
+        /// Returns the complete prompt by combining the prompt or continuation prompt with line numbers
+        /// as appropriate.  The line index is a zero-based index into the current multi-line session.
+        std::string
+        PromptForIndex (int line_index);
+        
+        /// Sets the current line index between line edits to allow free movement between lines.  Updates
+        /// the prompt to match.
+        void
+        SetCurrentLine (int line_index);
+        
+        /// Determines the width of the prompt in characters.  The width is guaranteed to be the same for
+        /// all lines of the current multi-line session.
+        int
+        GetPromptWidth();
+        
+        /// Returns true if the underlying EditLine session's keybindings are Emacs-based, or false if
+        /// they are VI-based.
+        bool
+        IsEmacs();
+        
+        /// Returns true if the current EditLine buffer contains nothing but spaces, or is empty.
+        bool
+        IsOnlySpaces();
+        
+        /// Helper method used by MoveCursor to determine relative line position.
+        int
+        GetLineIndexForLocation (CursorLocation location, int cursor_row);
+        
+        /// Move the cursor from one well-established location to another using relative line positioning
+        /// and absolute column positioning.
+        void
+        MoveCursor (CursorLocation from, CursorLocation to);
+        
+        /// Clear from cursor position to bottom of screen and print input lines including prompts, optionally
+        /// starting from a specific line.  Lines are drawn with an extra space at the end to reserve room for
+        /// the rightmost cursor position.
+        void
+        DisplayInput (int firstIndex = 0);
+        
+        /// Counts the number of rows a given line of content will end up occupying, taking into account both
+        /// the preceding prompt and a single trailing space occupied by a cursor when at the end of the line.
+        int
+        CountRowsForLine (const EditLineStringType & content);
+        
+        /// Save the line currently being edited
+        void
+        SaveEditedLine();
+        
+        /// Convert the current input lines into a UTF8 StringList
+        StringList
+        GetInputAsStringList(int line_count = UINT32_MAX);
+        
+        /// Replaces the current multi-line session with the next entry from history.  When the parameter is
+        /// true it will take the next earlier entry from history, when it is false it takes the next most
+        /// recent.
+        unsigned char
+        RecallHistory (bool earlier);
+        
+        /// Character reading implementation for EditLine that supports our multi-line editing trickery.
+        int
+        GetCharacter (EditLineCharType * c);
+        
+        /// Prompt implementation for EditLine.
+        const char *
+        Prompt();
+        
+        /// Line break command used when return is pressed in multi-line mode.
+        unsigned char
+        BreakLineCommand (int ch);
+        
+        /// Delete command used when delete is pressed in multi-line mode.
+        unsigned char
+        DeleteNextCharCommand (int ch);
+        
+        /// Delete command used when backspace is pressed in multi-line mode.
+        unsigned char
+        DeletePreviousCharCommand (int ch);
+        
+        /// Line navigation command used when ^P or up arrow are pressed in multi-line mode.
+        unsigned char
+        PreviousLineCommand (int ch);
+        
+        /// Line navigation command used when ^N or down arrow are pressed in multi-line mode.
+        unsigned char
+        NextLineCommand (int ch);
+        
+        /// Buffer start command used when Esc < is typed in multi-line emacs mode.
+        unsigned char
+        BufferStartCommand (int ch);
+        
+        /// Buffer end command used when Esc > is typed in multi-line emacs mode.
+        unsigned char
+        BufferEndCommand (int ch);
+
+        /// Context-sensitive tab insertion or code completion command used when the tab key is typed.
+        unsigned char
+        TabCommand (int ch);
+        
+        /// Respond to normal character insertion by fixing line indentation
+        unsigned char
+        FixIndentationCommand (int ch);
+
+        /// Revert line command used when moving between lines.
+        unsigned char
+        RevertLineCommand (int ch);
+
+        /// Ensures that the current EditLine instance is properly configured for single or multi-line editing.
+        void
+        ConfigureEditor (bool multiline);
+        
+    private:
+#if LLDB_EDITLINE_USE_WCHAR
+        std::wstring_convert<std::codecvt_utf8<wchar_t>> m_utf8conv;
+#endif
+        ::EditLine * m_editline = nullptr;
+        EditlineHistorySP m_history_sp;
+        bool m_in_history = false;
+        std::vector<EditLineStringType> m_live_history_lines;
+        bool m_multiline_enabled = false;
+        std::vector<EditLineStringType> m_input_lines;
+        EditorStatus m_editor_status;
+        bool m_editor_getting_char = false;
+        bool m_color_prompts = true;
+        int m_terminal_width = 0;
+        int m_base_line_number = 0;
+        unsigned m_current_line_index = 0;
+        int m_current_line_rows = -1;
+        int m_revert_cursor_index = 0;
+        int m_line_number_digits = 3;
+        std::string m_set_prompt;
+        std::string m_set_continuation_prompt;
+        std::string m_current_prompt;
+        bool m_needs_prompt_repaint = false;
+        std::string m_editor_name;
+        FILE * m_input_file;
+        FILE * m_output_file;
+        FILE * m_error_file;
+        ConnectionFileDescriptor m_input_connection;
+        IsInputCompleteCallbackType m_is_input_complete_callback = nullptr;
+        void * m_is_input_complete_callback_baton = nullptr;
+        FixIndentationCallbackType m_fix_indentation_callback = nullptr;
+        void * m_fix_indentation_callback_baton = nullptr;
+        const char * m_fix_indentation_callback_chars = nullptr;
+        CompleteCallbackType m_completion_callback = nullptr;
+        void * m_completion_callback_baton = nullptr;
     };
-    ::EditLine *m_editline;
-    EditlineHistorySP m_history_sp;
-    std::string m_prompt;
-    std::string m_lines_prompt;
-    lldb_private::Predicate<bool> m_getting_char;
-    CompleteCallbackType m_completion_callback;
-    void *m_completion_callback_baton;
-    LineCompletedCallbackType m_line_complete_callback;
-    void *m_line_complete_callback_baton;
-    Command m_lines_command;
-    uint32_t m_line_offset;
-    uint32_t m_lines_curr_line;
-    uint32_t m_lines_max_line;
-    ConnectionFileDescriptor m_file;
-    bool m_prompt_with_line_numbers;
-    bool m_getting_line;
-    bool m_got_eof;    // Set to true when we detect EOF
-    bool m_interrupted;
-    
-    DISALLOW_COPY_AND_ASSIGN(Editline);
-};
-
-} // namespace lldb_private
+}
 
 #endif  // #if defined(__cplusplus)
-#endif  // liblldb_Host_h_
+#endif  // liblldb_Editline_h_

Modified: lldb/trunk/include/lldb/Interpreter/CommandInterpreter.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Interpreter/CommandInterpreter.h?rev=222163&r1=222162&r2=222163&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Interpreter/CommandInterpreter.h (original)
+++ lldb/trunk/include/lldb/Interpreter/CommandInterpreter.h Mon Nov 17 13:06:59 2014
@@ -627,6 +627,9 @@ public:
         return m_quit_requested;
     }
 
+    lldb::IOHandlerSP
+    GetIOHandler(bool force_create = false, CommandInterpreterRunOptions *options = NULL);
+
     bool
     GetStoppedForCrash () const
     {

Modified: lldb/trunk/source/Commands/CommandObjectCommands.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Commands/CommandObjectCommands.cpp?rev=222163&r1=222162&r2=222163&view=diff
==============================================================================
--- lldb/trunk/source/Commands/CommandObjectCommands.cpp (original)
+++ lldb/trunk/source/Commands/CommandObjectCommands.cpp Mon Nov 17 13:06:59 2014
@@ -1030,11 +1030,15 @@ protected:
             if (argc == 1)
             {
                 Debugger &debugger = m_interpreter.GetDebugger();
+                bool color_prompt = debugger.GetUseColor();
                 const bool multiple_lines = true; // Get multiple lines
                 IOHandlerSP io_handler_sp (new IOHandlerEditline (debugger,
+                                                                  IOHandler::Type::Other,
                                                                   "lldb-regex", // Name of input reader for history
                                                                   "\033[K> ",   // Prompt and clear line
+                                                                  NULL,         // Continuation prompt
                                                                   multiple_lines,
+                                                                  color_prompt,
                                                                   0,            // Don't show line numbers
                                                                   *this));
                 

Modified: lldb/trunk/source/Commands/CommandObjectExpression.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Commands/CommandObjectExpression.cpp?rev=222163&r1=222162&r2=222163&view=diff
==============================================================================
--- lldb/trunk/source/Commands/CommandObjectExpression.cpp (original)
+++ lldb/trunk/source/Commands/CommandObjectExpression.cpp Mon Nov 17 13:06:59 2014
@@ -425,11 +425,15 @@ CommandObjectExpression::GetMultilineExp
     m_expr_line_count = 0;
     
     Debugger &debugger = GetCommandInterpreter().GetDebugger();
+    bool color_prompt = debugger.GetUseColor();
     const bool multiple_lines = true; // Get multiple lines
     IOHandlerSP io_handler_sp (new IOHandlerEditline (debugger,
+                                                      IOHandler::Type::Expression,
                                                       "lldb-expr",      // Name of input reader for history
                                                       NULL,             // No prompt
+                                                      NULL,             // Continuation prompt
                                                       multiple_lines,
+                                                      color_prompt,
                                                       1,                // Show line numbers starting at 1
                                                       *this));
     

Modified: lldb/trunk/source/Core/IOHandler.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Core/IOHandler.cpp?rev=222163&r1=222162&r2=222163&view=diff
==============================================================================
--- lldb/trunk/source/Core/IOHandler.cpp (original)
+++ lldb/trunk/source/Core/IOHandler.cpp Mon Nov 17 13:06:59 2014
@@ -38,8 +38,9 @@
 using namespace lldb;
 using namespace lldb_private;
 
-IOHandler::IOHandler (Debugger &debugger) :
+IOHandler::IOHandler (Debugger &debugger, IOHandler::Type type) :
     IOHandler (debugger,
+               type,
                StreamFileSP(),  // Adopt STDIN from top input reader
                StreamFileSP(),  // Adopt STDOUT from top input reader
                StreamFileSP(),  // Adopt STDERR from top input reader
@@ -49,6 +50,7 @@ IOHandler::IOHandler (Debugger &debugger
 
 
 IOHandler::IOHandler (Debugger &debugger,
+                      IOHandler::Type type,
                       const lldb::StreamFileSP &input_sp,
                       const lldb::StreamFileSP &output_sp,
                       const lldb::StreamFileSP &error_sp,
@@ -57,7 +59,9 @@ IOHandler::IOHandler (Debugger &debugger
     m_input_sp (input_sp),
     m_output_sp (output_sp),
     m_error_sp (error_sp),
+    m_popped (false),
     m_flags (flags),
+    m_type (type),
     m_user_data (NULL),
     m_done (false),
     m_active (false)
@@ -153,13 +157,28 @@ IOHandler::GetIsRealTerminal ()
     return GetInputStreamFile()->GetFile().GetIsRealTerminal();
 }
 
+void
+IOHandler::SetPopped (bool b)
+{
+    m_popped.SetValue(b, eBroadcastOnChange);
+}
+
+void
+IOHandler::WaitForPop ()
+{
+    m_popped.WaitForValueEqualTo(true);
+}
+
 IOHandlerConfirm::IOHandlerConfirm (Debugger &debugger,
                                     const char *prompt,
                                     bool default_response) :
     IOHandlerEditline(debugger,
+                      IOHandler::Type::Confirm,
                       NULL,     // NULL editline_name means no history loaded/saved
-                      NULL,
+                      NULL,     // No prompt
+                      NULL,     // No continuation prompt
                       false,    // Multi-line
+                      false,    // Don't colorize the prompt (i.e. the confirm message.)
                       0,
                       *this),
     m_default_response (default_response),
@@ -312,42 +331,56 @@ IOHandlerDelegate::IOHandlerComplete (IO
 
 
 IOHandlerEditline::IOHandlerEditline (Debugger &debugger,
+                                      IOHandler::Type type,
                                       const char *editline_name, // Used for saving history files
                                       const char *prompt,
+                                      const char *continuation_prompt,
                                       bool multi_line,
+                                      bool color_prompts,
                                       uint32_t line_number_start,
                                       IOHandlerDelegate &delegate) :
     IOHandlerEditline(debugger,
+                      type,
                       StreamFileSP(), // Inherit input from top input reader
                       StreamFileSP(), // Inherit output from top input reader
                       StreamFileSP(), // Inherit error from top input reader
                       0,              // Flags
                       editline_name,  // Used for saving history files
                       prompt,
+                      continuation_prompt,
                       multi_line,
+                      color_prompts,
                       line_number_start,
                       delegate)
 {
 }
 
 IOHandlerEditline::IOHandlerEditline (Debugger &debugger,
+                                      IOHandler::Type type,
                                       const lldb::StreamFileSP &input_sp,
                                       const lldb::StreamFileSP &output_sp,
                                       const lldb::StreamFileSP &error_sp,
                                       uint32_t flags,
                                       const char *editline_name, // Used for saving history files
                                       const char *prompt,
+                                      const char *continuation_prompt,
                                       bool multi_line,
+                                      bool color_prompts,
                                       uint32_t line_number_start,
                                       IOHandlerDelegate &delegate) :
-    IOHandler (debugger, input_sp, output_sp, error_sp, flags),
+    IOHandler (debugger, type, input_sp, output_sp, error_sp, flags),
 #ifndef LLDB_DISABLE_LIBEDIT
     m_editline_ap (),
 #endif
     m_delegate (delegate),
     m_prompt (),
+    m_continuation_prompt(),
+    m_current_lines_ptr (NULL),
     m_base_line_number (line_number_start),
-    m_multi_line (multi_line)
+    m_curr_line_idx (UINT32_MAX),
+    m_multi_line (multi_line),
+    m_color_prompts (color_prompts),
+    m_interrupt_exits (true)
 {
     SetPrompt(prompt);
 
@@ -364,17 +397,25 @@ IOHandlerEditline::IOHandlerEditline (De
     if (use_editline)
     {
         m_editline_ap.reset(new Editline (editline_name,
-                                          prompt ? prompt : "",
-                                          multi_line,
                                           GetInputFILE (),
                                           GetOutputFILE (),
-                                          GetErrorFILE ()));
-        if (m_base_line_number > 0)
-            m_editline_ap->ShowLineNumbers(true, m_base_line_number);
-        m_editline_ap->SetLineCompleteCallback (LineCompletedCallback, this);
+                                          GetErrorFILE (),
+                                          m_color_prompts));
+        m_editline_ap->SetIsInputCompleteCallback (IsInputCompleteCallback, this);
         m_editline_ap->SetAutoCompleteCallback (AutoCompleteCallback, this);
+        // See if the delegate supports fixing indentation
+        const char *indent_chars = delegate.IOHandlerGetFixIndentationCharacters();
+        if (indent_chars)
+        {
+            // The delegate does support indentation, hook it up so when any indentation
+            // character is typed, the delegate gets a chance to fix it
+            m_editline_ap->SetFixIndentationCallback (FixIndentationCallback, this, indent_chars);
+        }
     }
 #endif
+    SetBaseLineNumber (m_base_line_number);
+    SetPrompt(prompt ? prompt : "");
+    SetContinuationPrompt(continuation_prompt);    
 }
 
 IOHandlerEditline::~IOHandlerEditline ()
@@ -384,6 +425,20 @@ IOHandlerEditline::~IOHandlerEditline ()
 #endif
 }
 
+void
+IOHandlerEditline::Activate ()
+{
+    IOHandler::Activate();
+    m_delegate.IOHandlerActivated(*this);
+}
+
+void
+IOHandlerEditline::Deactivate ()
+{
+    IOHandler::Deactivate();
+    m_delegate.IOHandlerDeactivated(*this);
+}
+
 
 bool
 IOHandlerEditline::GetLine (std::string &line, bool &interrupted)
@@ -391,7 +446,7 @@ IOHandlerEditline::GetLine (std::string
 #ifndef LLDB_DISABLE_LIBEDIT
     if (m_editline_ap)
     {
-        return m_editline_ap->GetLine(line, interrupted).Success();
+        return m_editline_ap->GetLine (line, interrupted);
     }
     else
     {
@@ -403,7 +458,14 @@ IOHandlerEditline::GetLine (std::string
         {
             if (GetIsInteractive())
             {
-                const char *prompt = GetPrompt();
+                const char *prompt = NULL;
+                
+                if (m_multi_line && m_curr_line_idx > 0)
+                    prompt = GetContinuationPrompt();
+                
+                if (prompt == NULL)
+                    prompt = GetPrompt();
+                
                 if (prompt && prompt[0])
                 {
                     FILE *out = GetOutputFILE();
@@ -468,15 +530,23 @@ IOHandlerEditline::GetLine (std::string
 
 
 #ifndef LLDB_DISABLE_LIBEDIT
-LineStatus
-IOHandlerEditline::LineCompletedCallback (Editline *editline,
+bool
+IOHandlerEditline::IsInputCompleteCallback (Editline *editline,
                                           StringList &lines,
-                                          uint32_t line_idx,
-                                          Error &error,
                                           void *baton)
 {
     IOHandlerEditline *editline_reader = (IOHandlerEditline *) baton;
-    return editline_reader->m_delegate.IOHandlerLinesUpdated(*editline_reader, lines, line_idx, error);
+    return editline_reader->m_delegate.IOHandlerIsInputComplete(*editline_reader, lines);
+}
+
+int
+IOHandlerEditline::FixIndentationCallback (Editline *editline,
+                                           const StringList &lines,
+                                           int cursor_position,
+                                           void *baton)
+{
+    IOHandlerEditline *editline_reader = (IOHandlerEditline *) baton;
+    return editline_reader->m_delegate.IOHandlerFixIndentation(*editline_reader, lines, cursor_position);
 }
 
 int
@@ -534,33 +604,62 @@ IOHandlerEditline::SetPrompt (const char
     return true;
 }
 
+const char *
+IOHandlerEditline::GetContinuationPrompt ()
+{
+    if (m_continuation_prompt.empty())
+        return NULL;
+    return m_continuation_prompt.c_str();
+}
+
+
+void
+IOHandlerEditline::SetContinuationPrompt (const char *p)
+{
+    if (p && p[0])
+        m_continuation_prompt = p;
+    else
+        m_continuation_prompt.clear();
+    
+    if (m_editline_ap)
+        m_editline_ap->SetContinuationPrompt (m_continuation_prompt.empty() ? NULL : m_continuation_prompt.c_str());
+}
+
+
 void
 IOHandlerEditline::SetBaseLineNumber (uint32_t line)
 {
     m_base_line_number = line;
-#ifndef LLDB_DISABLE_LIBEDIT
+}
+
+uint32_t
+IOHandlerEditline::GetCurrentLineIndex () const
+{
+#ifdef LLDB_DISABLE_LIBEDIT
     if (m_editline_ap)
-        m_editline_ap->ShowLineNumbers (true, line);
+        return m_editline_ap->GetCurrentLine();
 #endif
-    
+    return m_curr_line_idx;
 }
+
 bool
 IOHandlerEditline::GetLines (StringList &lines, bool &interrupted)
 {
+    m_current_lines_ptr = &lines;
+    
     bool success = false;
 #ifndef LLDB_DISABLE_LIBEDIT
     if (m_editline_ap)
     {
-        std::string end_token;
-        success = m_editline_ap->GetLines(end_token, lines, interrupted).Success();
+        return m_editline_ap->GetLines (m_base_line_number, lines, interrupted);
     }
     else
     {
 #endif
-        LineStatus lines_status = LineStatus::Success;
+        bool done = false;
         Error error;
 
-        while (lines_status == LineStatus::Success)
+        while (!done)
         {
             // Show line numbers if we are asked to
             std::string line;
@@ -571,29 +670,19 @@ IOHandlerEditline::GetLines (StringList
                     ::fprintf(out, "%u%s", m_base_line_number + (uint32_t)lines.GetSize(), GetPrompt() == NULL ? " " : "");
             }
             
+            m_curr_line_idx = lines.GetSize();
+            
             bool interrupted = false;
-            if (GetLine(line, interrupted))
+            if (GetLine(line, interrupted) && !interrupted)
             {
-                if (interrupted)
-                {
-                    lines_status = LineStatus::Done;
-                }
-                else
-                {
-                    lines.AppendString(line);
-                    lines_status = m_delegate.IOHandlerLinesUpdated(*this, lines, lines.GetSize() - 1, error);
-                }
+                lines.AppendString(line);
+                done = m_delegate.IOHandlerIsInputComplete(*this, lines);
             }
             else
             {
-                lines_status = LineStatus::Done;
+                done = true;
             }
         }
-        
-        // Call the IOHandlerLinesUpdated function with UINT32_MAX as the line
-        // number to indicate all lines are complete
-        m_delegate.IOHandlerLinesUpdated(*this, lines, UINT32_MAX, error);
-
         success = lines.GetSize() > 0;
 #ifndef LLDB_DISABLE_LIBEDIT
     }
@@ -618,12 +707,14 @@ IOHandlerEditline::Run ()
             {
                 if (interrupted)
                 {
-                    m_done = true;
+                    m_done = m_interrupt_exits;
+                    m_delegate.IOHandlerInputInterrupted (*this, line);
+
                 }
                 else
                 {
                     line = lines.CopyList();
-                    m_delegate.IOHandlerInputComplete(*this, line);
+                    m_delegate.IOHandlerInputComplete (*this, line);
                 }
             }
             else
@@ -635,8 +726,10 @@ IOHandlerEditline::Run ()
         {
             if (GetLine(line, interrupted))
             {
-                if (!interrupted)
-                    m_delegate.IOHandlerInputComplete(*this, line);
+                if (interrupted)
+                    m_delegate.IOHandlerInputInterrupted (*this, line);
+                else
+                    m_delegate.IOHandlerInputComplete (*this, line);
             }
             else
             {
@@ -5375,7 +5468,7 @@ protected:
 DisplayOptions ValueObjectListDelegate::g_options = { true };
 
 IOHandlerCursesGUI::IOHandlerCursesGUI (Debugger &debugger) :
-    IOHandler (debugger)
+    IOHandler (debugger, IOHandler::Type::Curses)
 {
 }
 

Modified: lldb/trunk/source/Host/common/Editline.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Host/common/Editline.cpp?rev=222163&r1=222162&r2=222163&view=diff
==============================================================================
--- lldb/trunk/source/Host/common/Editline.cpp (original)
+++ lldb/trunk/source/Host/common/Editline.cpp Mon Nov 17 13:06:59 2014
@@ -7,527 +7,881 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include <iomanip>
+#include <iostream>
+#include <limits.h>
 
 #include "lldb/Host/Editline.h"
-
+#include "lldb/Host/ConnectionFileDescriptor.h"
 #include "lldb/Core/Error.h"
-#include "lldb/Core/StreamString.h"
 #include "lldb/Core/StringList.h"
+#include "lldb/Core/StreamString.h"
+#include "lldb/Host/FileSpec.h"
+#include "lldb/Host/FileSystem.h"
 #include "lldb/Host/Host.h"
+#include "lldb/Host/Mutex.h"
 
-#include <limits.h>
-
-using namespace lldb;
 using namespace lldb_private;
+using namespace lldb_private::line_editor;
 
-namespace lldb_private {
-    typedef std::weak_ptr<EditlineHistory> EditlineHistoryWP;
-    
-    
-    // EditlineHistory objects are sometimes shared between multiple
-    // Editline instances with the same program name. This class allows
-    // multiple editline instances to
-    //
-    
-    class EditlineHistory
-    {
-    private:
-        // Use static GetHistory() function to get a EditlineHistorySP to one of these objects
-        EditlineHistory(const std::string &prefix, uint32_t size, bool unique_entries) :
-            m_history (NULL),
-            m_event (),
-            m_prefix (prefix),
-            m_path ()
-        {
-            m_history = ::history_init();
-            ::history (m_history, &m_event, H_SETSIZE, size);
-            if (unique_entries)
-                ::history (m_history, &m_event, H_SETUNIQUE, 1);
-        }
-        
-        const char *
-        GetHistoryFilePath()
+// Editline uses careful cursor management to achieve the illusion of editing a multi-line block of text
+// with a single line editor.  Preserving this illusion requires fairly careful management of cursor
+// state.  Read and understand the relationship between DisplayInput(), MoveCursor(), SetCurrentLine(),
+// and SaveEditedLine() before making changes.
+
+#define ESCAPE "\x1b"
+#define ANSI_FAINT ESCAPE "[2m"
+#define ANSI_UNFAINT ESCAPE "[22m"
+#define ANSI_CLEAR_BELOW ESCAPE "[J"
+#define ANSI_CLEAR_RIGHT ESCAPE "[K"
+#define ANSI_SET_COLUMN_N ESCAPE "[%dG"
+#define ANSI_UP_N_ROWS ESCAPE "[%dA"
+#define ANSI_DOWN_N_ROWS ESCAPE "[%dB"
+
+#if LLDB_EDITLINE_USE_WCHAR
+
+#define EditLineConstString(str) L##str
+#define EditLineStringFormatSpec "%ls"
+
+#else
+
+#define EditLineConstString(str) str
+#define EditLineStringFormatSpec "%s"
+
+// use #defines so wide version functions and structs will resolve to old versions
+// for case of libedit not built with wide char support
+#define history_w history
+#define history_winit history_init
+#define history_wend history_end
+#define HistoryW History
+#define HistEventW HistEvent
+#define LineInfoW LineInfo
+
+#define el_wgets el_gets
+#define el_wgetc el_getc
+#define el_wpush el_push
+#define el_wparse el_parse
+#define el_wset  el_set
+#define el_wget  el_get
+#define el_wline el_line
+#define el_winsertstr el_insertstr
+#define  el_wdeletestr el_deletestr
+
+#endif // #if LLDB_EDITLINE_USE_WCHAR
+
+bool
+IsOnlySpaces (const EditLineStringType & content)
+{
+    for (wchar_t ch : content)
+    {
+        if (ch != EditLineCharType(' ')) return false;
+    }
+    return true;
+}
+
+EditLineStringType
+CombineLines (const std::vector<EditLineStringType> & lines)
+{
+    EditLineStringStreamType combined_stream;
+    for (EditLineStringType line : lines)
+    {
+        combined_stream << line.c_str() << "\n";
+    }
+    return combined_stream.str();
+}
+
+std::vector<EditLineStringType>
+SplitLines (const EditLineStringType & input)
+{
+    std::vector<EditLineStringType> result;
+    size_t start = 0;
+    while (start < input.length()) {
+        size_t end = input.find ('\n', start);
+        if (end == std::string::npos)
         {
-            if (m_path.empty() && m_history && !m_prefix.empty())
-            {
-                char history_path[PATH_MAX];
-                ::snprintf (history_path, sizeof(history_path), "~/.%s-history", m_prefix.c_str());
-                m_path = std::move(FileSpec(history_path, true).GetPath());
-            }
-            if (m_path.empty())
-                return NULL;
-            return m_path.c_str();
+            result.insert (result.end(), input.substr (start));
+            break;
         }
+        result.insert (result.end(), input.substr (start, end - start));
+        start = end + 1;
+    }
+    return result;
+}
+
+EditLineStringType
+FixIndentation (const EditLineStringType & line, int indent_correction)
+{
+    if (indent_correction == 0) return line;
+    if (indent_correction < 0) return line.substr (-indent_correction);
+    return EditLineStringType (indent_correction, EditLineCharType(' ')) + line;
+}
+
+int
+GetIndentation (const EditLineStringType & line)
+{
+    int space_count = 0;
+    for (EditLineCharType ch : line)
+    {
+        if (ch != EditLineCharType(' ')) break;
+        ++space_count;
+    }
+    return space_count;
+}
+
+bool
+IsInputPending (FILE * file)
+{
+    const int fd = fileno (file);
+    fd_set fds;
+    FD_ZERO (&fds);
+    FD_SET (fd, &fds);
+    timeval timeout = { 0, 0 };
+    return select (fd + 1, &fds, NULL, NULL, &timeout);
+}
+
+namespace lldb_private
+{
+    namespace line_editor
+    {
+        typedef std::weak_ptr<EditlineHistory> EditlineHistoryWP;
+
+        // EditlineHistory objects are sometimes shared between multiple
+        // Editline instances with the same program name.
         
-    public:
-        
-        ~EditlineHistory()
+        class EditlineHistory
         {
-            Save ();
-            
-            if (m_history)
+        private:
+            // Use static GetHistory() function to get a EditlineHistorySP to one of these objects
+            EditlineHistory (const std::string &prefix, uint32_t size, bool unique_entries) :
+                m_history (NULL),
+                m_event (),
+                m_prefix (prefix),
+                m_path ()
             {
-                ::history_end (m_history);
-                m_history = NULL;
+                m_history = history_winit();
+                history_w (m_history, &m_event, H_SETSIZE, size);
+                if (unique_entries)
+                    history_w (m_history, &m_event, H_SETUNIQUE, 1);
             }
-        }
 
-        static EditlineHistorySP
-        GetHistory (const std::string &prefix)
-        {
-            typedef std::map<std::string, EditlineHistoryWP> WeakHistoryMap;
-            static Mutex g_mutex(Mutex::eMutexTypeRecursive);
-            static WeakHistoryMap g_weak_map;
-            Mutex::Locker locker (g_mutex);
-            WeakHistoryMap::const_iterator pos = g_weak_map.find (prefix);
-            EditlineHistorySP history_sp;
-            if (pos != g_weak_map.end())
+            const char *
+            GetHistoryFilePath()
+            {
+                if (m_path.empty() && m_history && !m_prefix.empty())
+                {
+                    std::string parent_path = FileSpec ("~/.lldb", true).GetPath();
+                    char history_path[PATH_MAX];
+                    if (FileSystem::MakeDirectory(parent_path.c_str(), lldb::eFilePermissionsDirectoryDefault).Success())
+                    {
+                        snprintf (history_path, sizeof (history_path), "~/.lldb/%s-history", m_prefix.c_str());
+                    }
+                    else
+                    {
+                        snprintf (history_path, sizeof (history_path), "~/%s-widehistory", m_prefix.c_str());
+                    }
+                    m_path = std::move (FileSpec (history_path, true).GetPath());
+                }
+                if (m_path.empty())
+                    return NULL;
+                return m_path.c_str();
+            }
+            
+        public:
+            
+            ~EditlineHistory()
             {
-                history_sp = pos->second.lock();
-                if (history_sp)
-                    return history_sp;
-                g_weak_map.erase(pos);
+                Save();
+                
+                if (m_history)
+                {
+                    history_wend (m_history);
+                    m_history = NULL;
+                }
             }
-            history_sp.reset(new EditlineHistory(prefix, 800, true));
-            g_weak_map[prefix] = history_sp;
-            return history_sp;
-        }
-        
-        bool IsValid() const
-        {
-            return m_history != NULL;
-        }
-        
-        ::History *
-        GetHistoryPtr ()
-        {
-            return m_history;
-        }
-        
-        void
-        Enter (const char *line_cstr)
-        {
-            if (m_history)
-                ::history (m_history, &m_event, H_ENTER, line_cstr);
-        }
-        
-        bool
-        Load ()
-        {
-            if (m_history)
+            
+            static EditlineHistorySP
+            GetHistory (const std::string &prefix)
             {
-                const char *path = GetHistoryFilePath();
-                if (path)
+                typedef std::map<std::string, EditlineHistoryWP> WeakHistoryMap;
+                static Mutex g_mutex (Mutex::eMutexTypeRecursive);
+                static WeakHistoryMap g_weak_map;
+                Mutex::Locker locker (g_mutex);
+                WeakHistoryMap::const_iterator pos = g_weak_map.find (prefix);
+                EditlineHistorySP history_sp;
+                if (pos != g_weak_map.end())
                 {
-                    ::history (m_history, &m_event, H_LOAD, path);
-                    return true;
+                    history_sp = pos->second.lock();
+                    if (history_sp)
+                        return history_sp;
+                    g_weak_map.erase (pos);
                 }
+                history_sp.reset (new EditlineHistory (prefix, 800, true));
+                g_weak_map[prefix] = history_sp;
+                return history_sp;
             }
-            return false;
-        }
-        
-        bool
-        Save ()
-        {
-            if (m_history)
+            
+            bool IsValid() const
+            {
+                return m_history != NULL;
+            }
+            
+            HistoryW *
+            GetHistoryPtr ()
+            {
+                return m_history;
+            }
+            
+            void
+            Enter (const EditLineCharType *line_cstr)
             {
-                const char *path = GetHistoryFilePath();
-                if (path)
+                if (m_history)
+                    history_w (m_history, &m_event, H_ENTER, line_cstr);
+            }
+            
+            bool
+            Load ()
+            {
+                if (m_history)
                 {
-                    ::history (m_history, &m_event, H_SAVE, path);
-                    return true;
+                    const char *path = GetHistoryFilePath();
+                    if (path)
+                    {
+                        history_w (m_history, &m_event, H_LOAD, path);
+                        return true;
+                    }
                 }
+                return false;
             }
-            return false;
-        }
-        
-    protected:
-        ::History *m_history;       // The history object
-        ::HistEvent m_event;// The history event needed to contain all history events
-        std::string m_prefix;       // The prefix name (usually the editline program name) to use when loading/saving history
-        std::string m_path;         // Path to the history file
-    };
+            
+            bool
+            Save ()
+            {
+                if (m_history)
+                {
+                    const char *path = GetHistoryFilePath();
+                    if (path)
+                    {
+                        history_w (m_history, &m_event, H_SAVE, path);
+                        return true;
+                    }
+                }
+                return false;
+            }
+            
+        protected:
+            HistoryW * m_history;   // The history object
+            HistEventW m_event;      // The history event needed to contain all history events
+            std::string m_prefix;     // The prefix name (usually the editline program name) to use when loading/saving history
+            std::string m_path;       // Path to the history file
+        };
+    }
 }
 
+//------------------------------------------------------------------
+// Editline private methods
+//------------------------------------------------------------------
 
-static const char k_prompt_escape_char = '\1';
+void
+Editline::SetBaseLineNumber (int line_number)
+{
+    std::stringstream line_number_stream;
+    line_number_stream << line_number;
+    m_base_line_number = line_number;
+    m_line_number_digits = std::max (3, (int)line_number_stream.str().length() + 1);
+}
 
-Editline::Editline (const char *prog,       // prog can't be NULL
-                    const char *prompt,     // can be NULL for no prompt
-                    bool configure_for_multiline,
-                    FILE *fin,
-                    FILE *fout,
-                    FILE *ferr) :
-    m_editline (NULL),
-    m_history_sp (),
-    m_prompt (),
-    m_lines_prompt (),
-    m_getting_char (false),
-    m_completion_callback (NULL),
-    m_completion_callback_baton (NULL),
-    m_line_complete_callback (NULL),
-    m_line_complete_callback_baton (NULL),
-    m_lines_command (Command::None),
-    m_line_offset (0),
-    m_lines_curr_line (0),
-    m_lines_max_line (0),
-    m_file (fileno(fin), false),
-    m_prompt_with_line_numbers (false),
-    m_getting_line (false),
-    m_got_eof (false),
-    m_interrupted (false)
+std::string
+Editline::PromptForIndex (int line_index)
 {
-    if (prog && prog[0])
+    bool use_line_numbers = m_multiline_enabled && m_base_line_number > 0;
+    std::string prompt = m_set_prompt;
+    if (use_line_numbers && prompt.length() == 0)
     {
-        m_editline = ::el_init(prog, fin, fout, ferr);
-        
-        // Get a shared history instance
-        m_history_sp = EditlineHistory::GetHistory(prog);
+        prompt = ": ";
     }
-    else
+    std::string continuation_prompt = prompt;
+    if (m_set_continuation_prompt.length() > 0)
     {
-        m_editline = ::el_init("lldb-tmp", fin, fout, ferr);
+        continuation_prompt = m_set_continuation_prompt;
+        
+        // Ensure that both prompts are the same length through space padding
+        while (continuation_prompt.length() < prompt.length())
+        {
+            continuation_prompt += ' ';
+        }
+        while (prompt.length() < continuation_prompt.length())
+        {
+            prompt += ' ';
+        }
     }
     
-    if (prompt && prompt[0])
-        SetPrompt (prompt);
-
-    //::el_set (m_editline, EL_BIND, "^[[A", NULL); // Print binding for up arrow key
-    //::el_set (m_editline, EL_BIND, "^[[B", NULL); // Print binding for up down key
-
-    assert (m_editline);
-    ::el_set (m_editline, EL_CLIENTDATA, this);
-
-    // only defined for newer versions of editline
-#ifdef EL_PROMPT_ESC
-    ::el_set (m_editline, EL_PROMPT_ESC, GetPromptCallback, k_prompt_escape_char);
-#else
-    // fall back on old prompt setting code
-    ::el_set (m_editline, EL_PROMPT, GetPromptCallback);
-#endif
-    ::el_set (m_editline, EL_EDITOR, "emacs");
-    if (m_history_sp && m_history_sp->IsValid())
+    if (use_line_numbers)
     {
-        ::el_set (m_editline, EL_HIST, history, m_history_sp->GetHistoryPtr());
-    }
-    ::el_set (m_editline, EL_ADDFN, "lldb-complete", "Editline completion function", Editline::CallbackComplete);
-    // Keep old "lldb_complete" mapping for older clients that used this in their .editrc. editline also
-    // has a bad bug where if you have a bind command that tries to bind to a function name that doesn't
-    // exist, it will corrupt the heap and probably crash your process later.
-    ::el_set (m_editline, EL_ADDFN, "lldb_complete", "Editline completion function", Editline::CallbackComplete);
-    ::el_set (m_editline, EL_ADDFN, "lldb-edit-prev-line", "Editline edit prev line", Editline::CallbackEditPrevLine);
-    ::el_set (m_editline, EL_ADDFN, "lldb-edit-next-line", "Editline edit next line", Editline::CallbackEditNextLine);
-
-    ::el_set (m_editline, EL_BIND, "^r", "em-inc-search-prev", NULL); // Cycle through backwards search, entering string
-    ::el_set (m_editline, EL_BIND, "^w", "ed-delete-prev-word", NULL); // Delete previous word, behave like bash does.
-    ::el_set (m_editline, EL_BIND, "\033[3~", "ed-delete-next-char", NULL); // Fix the delete key.
-    ::el_set (m_editline, EL_BIND, "\t", "lldb-complete", NULL); // Bind TAB to be auto complete
-    
-    if (configure_for_multiline)
-    {
-        // Use escape sequences for control characters due to bugs in editline
-        // where "-k up" and "-k down" don't always work.
-        ::el_set (m_editline, EL_BIND, "^[[A", "lldb-edit-prev-line", NULL); // Map up arrow
-        ::el_set (m_editline, EL_BIND, "^[[B", "lldb-edit-next-line", NULL); // Map down arrow
-        // Bindings for next/prev history
-        ::el_set (m_editline, EL_BIND, "^P", "ed-prev-history", NULL); // Map up arrow
-        ::el_set (m_editline, EL_BIND, "^N", "ed-next-history", NULL); // Map down arrow
+        StreamString prompt_stream;
+        prompt_stream.Printf("%*d%s", m_line_number_digits, m_base_line_number + line_index,
+                             (line_index == 0) ? prompt.c_str() : continuation_prompt.c_str());
+        return std::move (prompt_stream.GetString());
     }
-    else
-    {
-        // Use escape sequences for control characters due to bugs in editline
-        // where "-k up" and "-k down" don't always work.
-        ::el_set (m_editline, EL_BIND, "^[[A", "ed-prev-history", NULL); // Map up arrow
-        ::el_set (m_editline, EL_BIND, "^[[B", "ed-next-history", NULL); // Map down arrow
-    }
-    
-    // Source $PWD/.editrc then $HOME/.editrc
-    ::el_source (m_editline, NULL);
- 
-    // Always read through our callback function so we don't read
-    // stuff we aren't supposed to. This also stops the extra echoing
-    // that can happen when you have more input than editline can handle
-    // at once.
-    SetGetCharCallback(GetCharFromInputFileCallback);
-
-    LoadHistory();
+    return (line_index == 0) ? prompt : continuation_prompt;
 }
 
-Editline::~Editline()
+void
+Editline::SetCurrentLine (int line_index)
 {
-    // EditlineHistory objects are sometimes shared between multiple
-    // Editline instances with the same program name. So just release
-    // our shared pointer and if we are the last owner, it will save the
-    // history to the history save file automatically.
-    m_history_sp.reset();
-    
-    // Disable edit mode to stop the terminal from flushing all input
-    // during the call to el_end() since we expect to have multiple editline
-    // instances in this program.
-    ::el_set (m_editline, EL_EDITMODE, 0);
-
-    ::el_end(m_editline);
-    m_editline = NULL;
+    m_current_line_index = line_index;
+    m_current_prompt = PromptForIndex (line_index);
 }
 
-void
-Editline::SetGetCharCallback (GetCharCallbackType callback)
+int
+Editline::GetPromptWidth()
 {
-    ::el_set (m_editline, EL_GETCFN, callback);
+    return (int)PromptForIndex (0).length();
 }
 
 bool
-Editline::LoadHistory ()
+Editline::IsEmacs()
 {
-    if (m_history_sp)
-        return m_history_sp->Load();
-    return false;
+    const char * editor;
+    el_get (m_editline, EL_EDITOR, &editor);
+    return editor[0] == 'e';
 }
 
 bool
-Editline::SaveHistory ()
+Editline::IsOnlySpaces()
 {
-    if (m_history_sp)
-        return m_history_sp->Save();
-    return false;
+    const LineInfoW * info = el_wline (m_editline);
+    for (const EditLineCharType * character = info->buffer; character < info->lastchar; character++)
+    {
+        if (*character != ' ') return false;
+    }
+    return true;
 }
 
-
-Error
-Editline::PrivateGetLine(std::string &line)
+int
+Editline::GetLineIndexForLocation (CursorLocation location, int cursor_row)
 {
-    Error error;
-    if (m_interrupted)
-    {
-        error.SetErrorString("interrupted");
-        return error;
-    }
-    
-    line.clear();
-    if (m_editline != NULL)
+    int line = 0;
+    if (location == CursorLocation::EditingPrompt || location == CursorLocation::BlockEnd ||
+        location == CursorLocation::EditingCursor)
     {
-        int line_len = 0;
-        // Call el_gets to prompt the user and read the user's input.
-        const char *line_cstr = ::el_gets (m_editline, &line_len);
-        
-        static int save_errno = (line_len < 0) ? errno : 0;
-        
-        if (save_errno != 0)
+        for (unsigned index = 0; index < m_current_line_index; index++)
         {
-            error.SetError(save_errno, eErrorTypePOSIX);
+            line += CountRowsForLine (m_input_lines[index]);
         }
-        else if (line_cstr)
+        if (location == CursorLocation::EditingCursor)
         {
-            // Decrement the length so we don't have newline characters in "line" for when
-            // we assign the cstr into the std::string
-            llvm::StringRef line_ref (line_cstr);
-            line_ref = line_ref.rtrim("\n\r");
-            
-            if (!line_ref.empty() && !m_interrupted)
+            line += cursor_row;
+        }
+        else if (location == CursorLocation::BlockEnd)
+        {
+            for (unsigned index = m_current_line_index; index < m_input_lines.size(); index++)
             {
-                // We didn't strip the newlines, we just adjusted the length, and
-                // we want to add the history item with the newlines
-                if (m_history_sp)
-                    m_history_sp->Enter(line_cstr);
-                
-                // Copy the part of the c string that we want (removing the newline chars)
-                line = std::move(line_ref.str());
+                line += CountRowsForLine (m_input_lines[index]);
             }
+            --line;
         }
     }
-    else
+    return line;
+}
+
+void
+Editline::MoveCursor (CursorLocation from, CursorLocation to)
+{
+    const LineInfoW * info = el_wline (m_editline);
+    int editline_cursor_position = (int)((info->cursor - info->buffer) + GetPromptWidth());
+    int editline_cursor_row = editline_cursor_position / m_terminal_width;
+    
+    // Determine relative starting and ending lines
+    int fromLine = GetLineIndexForLocation (from, editline_cursor_row);
+    int toLine = GetLineIndexForLocation (to, editline_cursor_row);
+    if (toLine != fromLine)
+    {
+        fprintf (m_output_file, (toLine > fromLine) ? ANSI_DOWN_N_ROWS : ANSI_UP_N_ROWS, std::abs (toLine - fromLine));
+    }
+    
+    // Determine target column
+    int toColumn = 1;
+    if (to == CursorLocation::EditingCursor)
+    {
+        toColumn = editline_cursor_position - (editline_cursor_row * m_terminal_width) + 1;
+    }
+    else if (to == CursorLocation::BlockEnd)
     {
-        error.SetErrorString("the EditLine instance has been deleted");
+        toColumn = ((m_input_lines[m_input_lines.size() - 1].length() + GetPromptWidth()) % 80) + 1;
     }
-    return error;
+    fprintf (m_output_file, ANSI_SET_COLUMN_N, toColumn);
 }
 
-
-Error
-Editline::GetLine(std::string &line, bool &interrupted)
+void
+Editline::DisplayInput (int firstIndex)
 {
-    Error error;
-    interrupted = false;
-    line.clear();
-
-    // Set arrow key bindings for up and down arrows for single line
-    // mode where up and down arrows do prev/next history
-    m_interrupted = false;
-
-    if (!m_got_eof)
+    fprintf (m_output_file, ANSI_SET_COLUMN_N ANSI_CLEAR_BELOW, 1);
+    int line_count = (int)m_input_lines.size();
+    const char *faint  = m_color_prompts ? ANSI_FAINT : "";
+    const char *unfaint  = m_color_prompts ? ANSI_UNFAINT : "";
+    
+    for (int index = firstIndex; index < line_count; index++)
     {
-        if (m_getting_line)
-        {
-            error.SetErrorString("already getting a line");
-            return error;
-        }
-        if (m_lines_curr_line > 0)
-        {
-            error.SetErrorString("already getting lines");
-            return error;
-        }
-        m_getting_line = true;
-        error = PrivateGetLine(line);
-        m_getting_line = false;
+        fprintf (m_output_file, "%s" "%s" "%s" EditLineStringFormatSpec " ",
+                faint,
+                PromptForIndex (index).c_str(),
+                unfaint,
+                m_input_lines[index].c_str());
+        if (index < line_count - 1) fprintf (m_output_file, "\n");
     }
+}
 
-    interrupted = m_interrupted;
 
-    if (m_got_eof && line.empty())
-    {
-        // Only set the error if we didn't get an error back from PrivateGetLine()
-        if (error.Success())
-            error.SetErrorString("end of file");
-    }
+int
+Editline::CountRowsForLine (const EditLineStringType & content)
+{
+    auto prompt = PromptForIndex (0); // Prompt width is constant during an edit session
+    int line_length = (int)(content.length() + prompt.length());
+    return (line_length / m_terminal_width) + 1;
+}
 
-    return error;
+void
+Editline::SaveEditedLine()
+{
+    const LineInfoW * info = el_wline (m_editline);
+    m_input_lines[m_current_line_index] = EditLineStringType (info->buffer, info->lastchar - info->buffer);
 }
 
-size_t
-Editline::Push (const char *bytes, size_t len)
+StringList
+Editline::GetInputAsStringList(int line_count)
 {
-    if (m_editline)
+    StringList lines;
+    for (EditLineStringType line : m_input_lines)
     {
-        // Must NULL terminate the string for el_push() so we stick it
-        // into a std::string first
-        ::el_push(m_editline,
-                  const_cast<char*>(std::string (bytes, len).c_str()));
-        return len;
+        if (line_count == 0) break;
+#if LLDB_EDITLINE_USE_WCHAR
+        lines.AppendString (m_utf8conv.to_bytes (line));
+#else
+        lines.AppendString(line);
+#endif
+        --line_count;
     }
-    return 0;
+    return lines;
 }
 
-
-Error
-Editline::GetLines(const std::string &end_line, StringList &lines, bool &interrupted)
+unsigned char
+Editline::RecallHistory (bool earlier)
 {
-    Error error;
-    interrupted = false;
-    if (m_getting_line)
+    if (!m_history_sp || !m_history_sp->IsValid()) return CC_ERROR;
+    
+    HistoryW * pHistory = m_history_sp->GetHistoryPtr();
+    HistEventW history_event;
+    std::vector<EditLineStringType> new_input_lines;
+    
+    // Treat moving from the "live" entry differently
+    if (!m_in_history)
     {
-        error.SetErrorString("already getting a line");
-        return error;
+        if (earlier == false) return CC_ERROR; // Can't go newer than the "live" entry
+        if (history_w (pHistory, &history_event, H_FIRST) == -1) return CC_ERROR;
+
+        // Save any edits to the "live" entry in case we return by moving forward in history
+        // (it would be more bash-like to save over any current entry, but libedit doesn't
+        // offer the ability to add entries anywhere except the end.)
+        SaveEditedLine();
+        m_live_history_lines = m_input_lines;
+        m_in_history = true;
     }
-    if (m_lines_curr_line > 0)
+    else
     {
-        error.SetErrorString("already getting lines");
-        return error;
+        if (history_w (pHistory, &history_event, earlier ? H_NEXT : H_PREV) == -1)
+        {
+            // Can't move earlier than the earliest entry
+            if (earlier) return CC_ERROR;
+
+            // ... but moving to newer than the newest yields the "live" entry
+            new_input_lines = m_live_history_lines;
+            m_in_history = false;
+        }
     }
     
-    // Set arrow key bindings for up and down arrows for multiple line
-    // mode where up and down arrows do edit prev/next line
-    m_interrupted = false;
+    // If we're pulling the lines from history, split them apart
+    if (m_in_history) new_input_lines = SplitLines (history_event.str);
 
-    LineStatus line_status = LineStatus::Success;
-
-    lines.Clear();
+    // Erase the current edit session and replace it with a new one
+    MoveCursor (CursorLocation::EditingCursor, CursorLocation::BlockStart);
+    m_input_lines = new_input_lines;
+    DisplayInput();
+    
+    // Prepare to edit the last line when moving to previous entry, or the first line
+    // when moving to next entry
+    SetCurrentLine (m_current_line_index = earlier ? (int)m_input_lines.size() - 1 : 0);
+    MoveCursor (CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
+    return CC_NEWLINE;
+}
 
-    FILE *out_file = GetOutputFile();
-    FILE *err_file = GetErrorFile();
-    m_lines_curr_line = 1;
-    while (line_status != LineStatus::Done)
+int
+Editline::GetCharacter (EditLineCharType * c)
+{
+    const LineInfoW * info = el_wline (m_editline);
+    
+    // Paint a faint version of the desired prompt over the version libedit draws
+    // (will only be requested if colors are supported)
+    if (m_needs_prompt_repaint)
+    {
+        MoveCursor (CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
+        fprintf (m_output_file, "%s" "%s" "%s", ANSI_FAINT, Prompt(), ANSI_UNFAINT);
+        MoveCursor (CursorLocation::EditingPrompt, CursorLocation::EditingCursor);
+        m_needs_prompt_repaint = false;
+    }
+    
+    if (m_multiline_enabled)
     {
-        const uint32_t line_idx = m_lines_curr_line-1;
-        if (line_idx >= lines.GetSize())
-            lines.SetSize(m_lines_curr_line);
-        m_lines_max_line = lines.GetSize();
-        m_lines_command = Command::None;
-        assert(line_idx < m_lines_max_line);
-        std::string &line = lines[line_idx];
-        error = PrivateGetLine(line);
-        if (error.Fail())
-        {
-            line_status = LineStatus::Error;
+        // Detect when the number of rows used for this input line changes due to an edit
+        int lineLength = (int)((info->lastchar - info->buffer) + GetPromptWidth());
+        int new_line_rows = (lineLength / m_terminal_width) + 1;
+        if (m_current_line_rows != -1 && new_line_rows != m_current_line_rows)
+        {
+            // Respond by repainting the current state from this line on
+            MoveCursor (CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
+            SaveEditedLine();
+            DisplayInput (m_current_line_index);
+            MoveCursor (CursorLocation::BlockEnd, CursorLocation::EditingCursor);
         }
-        else if (m_interrupted)
-        {
-            interrupted = true;
-            line_status = LineStatus::Done;
+        m_current_line_rows = new_line_rows;
+    }
+    
+    // Read an actual character
+    while (true)
+    {
+        lldb::ConnectionStatus status = lldb::eConnectionStatusSuccess;
+        char ch = 0;
+        m_editor_getting_char = true;
+        int read_count = m_input_connection.Read(&ch, 1, UINT32_MAX, status, NULL);
+        m_editor_getting_char = false;
+        if (read_count)
+        {
+#if LLDB_EDITLINE_USE_WCHAR
+            // After the initial interruptible read, this is guaranteed not to block
+            ungetc (ch, m_input_file);
+            *c = fgetwc (m_input_file);
+            if (*c != WEOF) return 1;
+#else
+            *c = ch;
+            if(*c != EOF) return 1;
+#endif
         }
         else
         {
-            switch (m_lines_command)
+            switch (status)
             {
-                case Command::None:
-                    if (m_line_complete_callback)
-                    {
-                        line_status = m_line_complete_callback (this,
-                                                                lines,
-                                                                line_idx,
-                                                                error,
-                                                                m_line_complete_callback_baton);
-                    }
-                    else if (line == end_line)
-                    {
-                        line_status = LineStatus::Done;
-                    }
-
-                    if (line_status == LineStatus::Success)
-                    {
-                        ++m_lines_curr_line;
-                        // If we already have content for the next line because
-                        // we were editing previous lines, then populate the line
-                        // with the appropriate contents
-                        if (line_idx+1 < lines.GetSize() && !lines[line_idx+1].empty())
-                            ::el_push (m_editline,
-                                       const_cast<char*>(lines[line_idx+1].c_str()));
-                    }
-                    else if (line_status == LineStatus::Error)
-                    {
-                        // Clear to end of line ("ESC[K"), then print the error,
-                        // then go to the next line ("\n") and then move cursor up
-                        // two lines ("ESC[2A").
-                        fprintf (err_file, "\033[Kerror: %s\n\033[2A", error.AsCString());
-                    }
+                case lldb::eConnectionStatusInterrupted:
+                    m_editor_status = EditorStatus::Interrupted;
+                    printf ("^C\n");
+                    return 0;
+                    
+                case lldb::eConnectionStatusSuccess:         // Success
                     break;
-                case Command::EditPrevLine:
-                    if (m_lines_curr_line > 1)
+                    
+                case lldb::eConnectionStatusError:           // Check GetError() for details
+                case lldb::eConnectionStatusTimedOut:        // Request timed out
+                case lldb::eConnectionStatusEndOfFile:       // End-of-file encountered
+                case lldb::eConnectionStatusNoConnection:    // No connection
+                case lldb::eConnectionStatusLostConnection:  // Lost connection while connected to a valid connection
+                    m_editor_status = EditorStatus::EndOfInput;
+                    return 0;
+            }
+        }
+    }
+}
+
+const char *
+Editline::Prompt()
+{
+    if (m_color_prompts) m_needs_prompt_repaint = true;
+    return m_current_prompt.c_str();
+}
+
+unsigned char
+Editline::BreakLineCommand (int ch)
+{
+    // Preserve any content beyond the cursor, truncate and save the current line
+    const LineInfoW * info = el_wline (m_editline);
+    auto current_line = EditLineStringType (info->buffer, info->cursor - info->buffer);
+    auto new_line_fragment = EditLineStringType (info->cursor, info->lastchar - info->cursor);
+    m_input_lines[m_current_line_index] = current_line;
+    
+    // Ignore whitespace-only extra fragments when breaking a line
+    if (::IsOnlySpaces (new_line_fragment)) new_line_fragment = EditLineConstString("");
+
+    // Establish the new cursor position at the start of a line when inserting a line break
+    m_revert_cursor_index = 0;
+
+    // Don't perform end of input detection or automatic formatting when pasting
+    if (!IsInputPending (m_input_file))
+    {
+        // If this is the end of the last line, treat this as a potential exit
+        if (m_current_line_index == m_input_lines.size() - 1 && new_line_fragment.length() == 0)
+        {
+            bool end_of_input = true;
+            if (m_is_input_complete_callback) {
+                SaveEditedLine();
+                auto lines = GetInputAsStringList();
+                end_of_input = m_is_input_complete_callback (this, lines, m_is_input_complete_callback_baton);
+
+                // The completion test is allowed to change the input lines when complete
+                if (end_of_input)
+                {
+                    m_input_lines.clear();
+                    for (unsigned index = 0; index < lines.GetSize(); index++)
                     {
-                        //::fprintf (out_file, "\033[1A\033[%uD\033[2K", (uint32_t)(m_lines_prompt.size() + lines[line_idx].size())); // Make cursor go up a line and clear that line
-                        ::fprintf (out_file, "\033[1A\033[1000D\033[2K");
-                        if (!lines[line_idx-1].empty())
-                            ::el_push (m_editline,
-                                       const_cast<char*>(lines[line_idx-1].c_str()));
-                        --m_lines_curr_line;
+#if LLDB_EDITLINE_USE_WCHAR
+                        m_input_lines.insert (m_input_lines.end(), m_utf8conv.from_bytes (lines[index]));
+#else
+                        m_input_lines.insert (m_input_lines.end(), lines[index]);
+#endif
                     }
-                    break;
-                case Command::EditNextLine:
-                    // Allow the down arrow to create a new line
-                    ++m_lines_curr_line;
-                    //::fprintf (out_file, "\033[1B\033[%uD\033[2K", (uint32_t)(m_lines_prompt.size() + lines[line_idx].size()));
-                    ::fprintf (out_file, "\033[1B\033[1000D\033[2K");
-                    if (line_idx+1 < lines.GetSize() && !lines[line_idx+1].empty())
-                        ::el_push (m_editline,
-                                   const_cast<char*>(lines[line_idx+1].c_str()));
-                    break;
+                }
+            }
+            if (end_of_input)
+            {
+                fprintf (m_output_file, "\n");
+                m_editor_status = EditorStatus::Complete;
+                return CC_NEWLINE;
             }
         }
+        
+        // Apply smart indentation
+        if (m_fix_indentation_callback) {
+            StringList lines = GetInputAsStringList (m_current_line_index + 1);
+#if LLDB_EDITLINE_USE_WCHAR
+            lines.AppendString (m_utf8conv.to_bytes (new_line_fragment));
+#else
+            lines.AppendString (new_line_fragment);
+#endif
+            
+            int indent_correction = m_fix_indentation_callback (this, lines, 0, m_fix_indentation_callback_baton);
+            new_line_fragment = FixIndentation(new_line_fragment, indent_correction);
+            m_revert_cursor_index = GetIndentation(new_line_fragment);
+        }
+    }
+    
+    // Insert the new line and repaint everything from the split line on down
+    m_input_lines.insert (m_input_lines.begin() + m_current_line_index + 1, new_line_fragment);
+    MoveCursor (CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
+    DisplayInput (m_current_line_index);
+    
+    // Reposition the cursor to the right line and prepare to edit the new line
+    SetCurrentLine (m_current_line_index + 1);
+    MoveCursor (CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
+    return CC_NEWLINE;
+}
+
+unsigned char
+Editline::DeleteNextCharCommand (int ch)
+{
+    LineInfoW * info = (LineInfoW *)el_wline (m_editline);
+    
+    // Just delete the next character normally if possible
+    if (info->cursor < info->lastchar) {
+        info->cursor++;
+        el_deletestr (m_editline, 1);
+        return CC_REFRESH;
+    }
+
+    // Fail when at the end of the last line, except when ^D is pressed on
+    // the line is empty, in which case it is treated as EOF
+    if (m_current_line_index == m_input_lines.size() - 1)
+    {
+        if (ch == 4 && info->buffer == info->lastchar)
+        {
+            fprintf (m_output_file, "^D\n");
+            m_editor_status = EditorStatus::EndOfInput;
+            return CC_EOF;
+        }
+        return CC_ERROR;
+    }
+    
+    // Prepare to combine this line with the one below
+    MoveCursor (CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
+    
+    // Insert the next line of text at the cursor and restore the cursor position
+    const EditLineCharType * cursor = info->cursor;
+    el_winsertstr (m_editline, m_input_lines[m_current_line_index + 1].c_str());
+    info->cursor = cursor;
+    SaveEditedLine();
+    
+    // Delete the extra line
+    m_input_lines.erase (m_input_lines.begin() + m_current_line_index + 1);
+    
+    // Clear and repaint from this line on down
+    DisplayInput (m_current_line_index);
+    MoveCursor (CursorLocation::BlockEnd, CursorLocation::EditingCursor);
+    return CC_REFRESH;
+}
+
+unsigned char
+Editline::DeletePreviousCharCommand (int ch)
+{
+    LineInfoW * info = (LineInfoW *)el_wline (m_editline);
+    
+    // Just delete the previous character normally when not at the start of a line
+    if (info->cursor > info->buffer) {
+        el_deletestr (m_editline, 1);
+        return CC_REFRESH;
     }
-    m_lines_curr_line = 0;
-    m_lines_command = Command::None;
+    
+    // No prior line and no prior character?  Let the user know
+    if (m_current_line_index == 0) return CC_ERROR;
+    
+    // No prior character, but prior line?  Combine with the line above
+    SaveEditedLine();
+    SetCurrentLine (m_current_line_index - 1);
+    auto priorLine = m_input_lines[m_current_line_index];
+    m_input_lines.erase (m_input_lines.begin() + m_current_line_index);
+    m_input_lines[m_current_line_index] = priorLine + m_input_lines[m_current_line_index];
+    
+    // Repaint from the new line down
+    fprintf (m_output_file, ANSI_UP_N_ROWS ANSI_SET_COLUMN_N, CountRowsForLine (priorLine), 1);
+    DisplayInput (m_current_line_index);
+    
+    // Put the cursor back where libedit expects it to be before returning to editing
+    // by telling libedit about the newly inserted text
+    MoveCursor (CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
+    el_winsertstr (m_editline, priorLine.c_str());
+    return CC_REDISPLAY;
+}
 
-    // If we have a callback, call it one more time to let the
-    // user know the lines are complete
-    if (m_line_complete_callback && !interrupted)
-        m_line_complete_callback (this,
-                                  lines,
-                                  UINT32_MAX,
-                                  error,
-                                  m_line_complete_callback_baton);
+unsigned char
+Editline::PreviousLineCommand (int ch)
+{
+    SaveEditedLine();
 
-    return error;
+    if (m_current_line_index == 0) {
+        return RecallHistory (true);
+    }
+    
+    // Start from a known location
+    MoveCursor (CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
+    
+    // Treat moving up from a blank last line as a deletion of that line
+    if (m_current_line_index == m_input_lines.size() - 1 && IsOnlySpaces()) {
+        m_input_lines.erase (m_input_lines.begin() + m_current_line_index);
+        fprintf (m_output_file, ANSI_CLEAR_BELOW);
+    }
+    
+    SetCurrentLine (m_current_line_index - 1);
+    fprintf (m_output_file, ANSI_UP_N_ROWS ANSI_SET_COLUMN_N,
+            CountRowsForLine (m_input_lines[m_current_line_index]), 1);
+    return CC_NEWLINE;
 }
 
 unsigned char
-Editline::HandleCompletion (int ch)
+Editline::NextLineCommand (int ch)
 {
-    if (m_completion_callback == NULL)
-        return CC_ERROR;
+    SaveEditedLine();
 
-    const LineInfo *line_info  = ::el_line(m_editline);
+    // Handle attempts to move down from the last line
+    if (m_current_line_index == m_input_lines.size() - 1) {
+        // Don't add an extra line if the existing last line is blank, move through history instead
+        if (IsOnlySpaces()) {
+            return RecallHistory (false);
+        }
+        
+        // Determine indentation for the new line
+        int indentation = 0;
+        if (m_fix_indentation_callback)
+        {
+            StringList lines = GetInputAsStringList();
+            lines.AppendString("");
+            indentation = m_fix_indentation_callback (this, lines, 0, m_fix_indentation_callback_baton);
+        }
+        m_input_lines.insert (m_input_lines.end(), EditLineStringType (indentation, EditLineCharType(' ')));
+    }
+    
+    // Move down past the current line using newlines to force scrolling if needed
+    SetCurrentLine (m_current_line_index + 1);
+    const LineInfoW * info = el_wline (m_editline);
+    int cursor_position = (int)((info->cursor - info->buffer) + GetPromptWidth());
+    int cursor_row = cursor_position / m_terminal_width;
+    for (int line_count = 0; line_count < m_current_line_rows - cursor_row; line_count++) {
+        fprintf (m_output_file, "\n");
+    }
+    return CC_NEWLINE;
+}
+
+unsigned char
+Editline::FixIndentationCommand (int ch)
+{
+    if (!m_fix_indentation_callback) return CC_NORM;
+    
+    // Insert the character by hand prior to correction
+    EditLineCharType inserted[] = { (EditLineCharType)ch, 0 };
+    el_winsertstr (m_editline, inserted);
+    SaveEditedLine();
+    StringList lines = GetInputAsStringList (m_current_line_index + 1);
+
+    // Determine the cursor position
+    LineInfoW * info = (LineInfoW *)el_wline (m_editline);
+    int cursor_position = info->cursor - info->buffer;
+    
+    int indent_correction = m_fix_indentation_callback (this, lines, cursor_position, m_fix_indentation_callback_baton);
+    
+    // Adjust the input buffer to correct indentation
+    if (indent_correction > 0)
+    {
+        info->cursor = info->buffer;
+        el_winsertstr (m_editline, EditLineStringType (indent_correction, EditLineCharType(' ')).c_str());
+    }
+    else if (indent_correction < 0)
+    {
+        info->cursor = info->buffer - indent_correction;
+        el_wdeletestr (m_editline, -indent_correction);
+    }
+    info->cursor = info->buffer + cursor_position + indent_correction;
+    return CC_REFRESH;
+}
+
+unsigned char
+Editline::RevertLineCommand (int ch)
+{
+    el_winsertstr (m_editline, m_input_lines[m_current_line_index].c_str());
+    if (m_revert_cursor_index >= 0)
+    {
+        LineInfoW * info = (LineInfoW *)el_wline (m_editline);
+        info->cursor = info->buffer + m_revert_cursor_index;
+        if (info->cursor > info->lastchar)
+        {
+            info->cursor = info->lastchar;
+        }
+        m_revert_cursor_index = -1;
+    }
+    return CC_REFRESH;
+}
+
+unsigned char
+Editline::BufferStartCommand (int ch)
+{
+    SaveEditedLine();
+    MoveCursor (CursorLocation::EditingCursor, CursorLocation::BlockStart);
+    SetCurrentLine (0);
+    m_revert_cursor_index = 0;
+    return CC_NEWLINE;
+}
+
+unsigned char
+Editline::BufferEndCommand (int ch)
+{
+    SaveEditedLine();
+    MoveCursor (CursorLocation::EditingCursor, CursorLocation::BlockEnd);
+    SetCurrentLine ((int)m_input_lines.size() - 1);
+    MoveCursor (CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
+    return CC_NEWLINE;
+}
+
+unsigned char
+Editline::TabCommand (int ch)
+{
+    if (m_completion_callback == nullptr) return CC_ERROR;
+    
+    const LineInfo *line_info  = el_line (m_editline);
     StringList completions;
     int page_size = 40;
-        
+    
     const int num_completions = m_completion_callback (line_info->buffer,
                                                        line_info->cursor,
                                                        line_info->lastchar,
@@ -536,25 +890,24 @@ Editline::HandleCompletion (int ch)
                                                        completions,
                                                        m_completion_callback_baton);
     
-    FILE *out_file = GetOutputFile();
-
-//    if (num_completions == -1)
-//    {
-//        ::el_insertstr (m_editline, m_completion_key);
-//        return CC_REDISPLAY;
-//    }
-//    else
+    if (num_completions == 0) return CC_ERROR;
+    //    if (num_completions == -1)
+    //    {
+    //        el_insertstr (m_editline, m_completion_key);
+    //        return CC_REDISPLAY;
+    //    }
+    //    else
     if (num_completions == -2)
     {
         // Replace the entire line with the first string...
-        ::el_deletestr (m_editline, line_info->cursor - line_info->buffer);
-        ::el_insertstr (m_editline, completions.GetStringAtIndex(0));
+        el_deletestr (m_editline, line_info->cursor - line_info->buffer);
+        el_insertstr (m_editline, completions.GetStringAtIndex (0));
         return CC_REDISPLAY;
     }
     
     // If we get a longer match display that first.
-    const char *completion_str = completions.GetStringAtIndex(0);
-    if (completion_str != NULL && *completion_str != '\0')
+    const char *completion_str = completions.GetStringAtIndex (0);
+    if (completion_str != nullptr && *completion_str != '\0')
     {
         el_insertstr (m_editline, completion_str);
         return CC_REDISPLAY;
@@ -563,15 +916,15 @@ Editline::HandleCompletion (int ch)
     if (num_completions > 1)
     {
         int num_elements = num_completions + 1;
-        ::fprintf (out_file, "\nAvailable completions:");
+        fprintf (m_output_file, "\n" ANSI_CLEAR_BELOW "Available completions:");
         if (num_completions < page_size)
         {
             for (int i = 1; i < num_elements; i++)
             {
-                completion_str = completions.GetStringAtIndex(i);
-                ::fprintf (out_file, "\n\t%s", completion_str);
+                completion_str = completions.GetStringAtIndex (i);
+                fprintf (m_output_file, "\n\t%s", completion_str);
             }
-            ::fprintf (out_file, "\n");
+            fprintf (m_output_file, "\n");
         }
         else
         {
@@ -585,17 +938,17 @@ Editline::HandleCompletion (int ch)
                     endpoint = num_elements;
                 for (; cur_pos < endpoint; cur_pos++)
                 {
-                    completion_str = completions.GetStringAtIndex(cur_pos);
-                    ::fprintf (out_file, "\n\t%s", completion_str);
+                    completion_str = completions.GetStringAtIndex (cur_pos);
+                    fprintf (m_output_file, "\n\t%s", completion_str);
                 }
                 
                 if (cur_pos >= num_elements)
                 {
-                    ::fprintf (out_file, "\n");
+                    fprintf (m_output_file, "\n");
                     break;
                 }
                 
-                ::fprintf (out_file, "\nMore (Y/n/a): ");
+                fprintf (m_output_file, "\nMore (Y/n/a): ");
                 reply = 'n';
                 got_char = el_getc(m_editline, &reply);
                 if (got_char == -1 || reply == 'n')
@@ -604,247 +957,381 @@ Editline::HandleCompletion (int ch)
                     page_size = num_elements - cur_pos;
             }
         }
-        
+        DisplayInput();
+        MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor);
     }
+    return CC_REDISPLAY;
+}
+
+void
+Editline::ConfigureEditor (bool multiline)
+{
+    if (m_editline && m_multiline_enabled == multiline) return;
+    m_multiline_enabled = multiline;
     
-    if (num_completions == 0)
-        return CC_REFRESH_BEEP;
-    else
-        return CC_REDISPLAY;
+    if (m_editline) {
+        // Disable edit mode to stop the terminal from flushing all input
+        // during the call to el_end() since we expect to have multiple editline
+        // instances in this program.
+        el_set (m_editline, EL_EDITMODE, 0);
+        el_end (m_editline);
+    }
+    
+    m_editline = el_init (m_editor_name.c_str(), m_input_file, m_output_file, m_error_file);
+    TerminalSizeChanged();
+    
+    if (m_history_sp && m_history_sp->IsValid())
+    {
+        m_history_sp->Load();
+        el_wset (m_editline, EL_HIST, history, m_history_sp->GetHistoryPtr());
+    }
+    el_set (m_editline, EL_CLIENTDATA, this);
+    el_set (m_editline, EL_SIGNAL, 0);
+    el_set (m_editline, EL_EDITOR, "emacs");
+    el_set (m_editline, EL_PROMPT, (EditlinePromptCallbackType)([] (EditLine *editline) {
+        return Editline::InstanceFor (editline)->Prompt();
+    }));
+
+    el_wset (m_editline, EL_GETCFN,
+            (EditlineGetCharCallbackType)([] (EditLine * editline, EditLineCharType * c) {
+                return Editline::InstanceFor (editline)->GetCharacter (c);
+            }));
+    
+    // Commands used for multiline support, registered whether or not they're used
+    el_set (m_editline, EL_ADDFN, "lldb-break-line", "Insert a line break",
+           (EditlineCommandCallbackType)([] (EditLine * editline, int ch) {
+               return Editline::InstanceFor (editline)->BreakLineCommand (ch);
+           }));
+    el_set (m_editline, EL_ADDFN, "lldb-delete-next-char", "Delete next character",
+           (EditlineCommandCallbackType)([] (EditLine * editline, int ch) {
+               return Editline::InstanceFor (editline)->DeleteNextCharCommand (ch);
+           }));
+    el_set (m_editline, EL_ADDFN, "lldb-delete-previous-char", "Delete previous character",
+           (EditlineCommandCallbackType)([] (EditLine * editline, int ch) {
+               return Editline::InstanceFor (editline)->DeletePreviousCharCommand (ch);
+           }));
+    el_set (m_editline, EL_ADDFN, "lldb-previous-line", "Move to previous line",
+           (EditlineCommandCallbackType)([] (EditLine * editline, int ch) {
+               return Editline::InstanceFor (editline)->PreviousLineCommand (ch);
+           }));
+    el_set (m_editline, EL_ADDFN, "lldb-next-line", "Move to next line",
+           (EditlineCommandCallbackType)([] (EditLine * editline, int ch) {
+               return Editline::InstanceFor (editline)->NextLineCommand (ch);
+           }));
+    el_set (m_editline, EL_ADDFN, "lldb-buffer-start", "Move to start of buffer",
+           (EditlineCommandCallbackType)([] (EditLine * editline, int ch) {
+               return Editline::InstanceFor (editline)->BufferStartCommand (ch);
+           }));
+    el_set (m_editline, EL_ADDFN, "lldb-buffer-end", "Move to end of buffer",
+           (EditlineCommandCallbackType)([] (EditLine * editline, int ch) {
+                return Editline::InstanceFor (editline)->BufferEndCommand (ch);
+            }));
+    el_set (m_editline, EL_ADDFN, "lldb-fix-indentation", "Fix line indentation",
+           (EditlineCommandCallbackType)([] (EditLine * editline, int ch) {
+                return Editline::InstanceFor (editline)->FixIndentationCommand (ch);
+            }));
+
+    // Register the complete callback under two names for compatibility with older clients using
+    // custom .editrc files (largely becuase libedit has a bad bug where if you have a bind command
+    // that tries to bind to a function name that doesn't exist, it can corrupt the heap and
+    // crash your process later.)
+    EditlineCommandCallbackType complete_callback = [] (EditLine * editline, int ch) {
+        return Editline::InstanceFor (editline)->TabCommand (ch);
+    };
+    el_set (m_editline, EL_ADDFN, "lldb-complete", "Invoke completion", complete_callback);
+    el_set (m_editline, EL_ADDFN, "lldb_complete", "Invoke completion", complete_callback);
+    
+    // General bindings we don't mind being overridden
+    if (!multiline) {
+        el_set (m_editline, EL_BIND, "^r", "em-inc-search-prev", NULL); // Cycle through backwards search, entering string
+    }
+    el_set (m_editline, EL_BIND, "^w", "ed-delete-prev-word", NULL); // Delete previous word, behave like bash in emacs mode
+    el_set (m_editline, EL_BIND, "\t", "lldb-complete", NULL); // Bind TAB to auto complete
+    
+    // Allow user-specific customization prior to registering bindings we absolutely require
+    el_source (m_editline, NULL);
+
+    // Register an internal binding that external developers shouldn't use
+    el_set (m_editline, EL_ADDFN, "lldb-revert-line", "Revert line to saved state",
+           (EditlineCommandCallbackType)([] (EditLine * editline, int ch) {
+               return Editline::InstanceFor (editline)->RevertLineCommand (ch);
+           }));
+
+    // Register keys that perform auto-indent correction
+    if (m_fix_indentation_callback && m_fix_indentation_callback_chars)
+    {
+        char bind_key[2] = { 0, 0 };
+        const char * indent_chars = m_fix_indentation_callback_chars;
+        while (*indent_chars)
+        {
+            bind_key[0] = *indent_chars;
+            el_set (m_editline, EL_BIND, bind_key, "lldb-fix-indentation", NULL);
+            ++indent_chars;
+        }
+    }
+
+    // Multi-line editor bindings
+    if (multiline)
+    {
+        el_set (m_editline, EL_BIND, "\n", "lldb-break-line", NULL);
+        el_set (m_editline, EL_BIND, "\r", "lldb-break-line", NULL);
+        el_set (m_editline, EL_BIND, "^p", "lldb-previous-line", NULL);
+        el_set (m_editline, EL_BIND, "^n", "lldb-next-line", NULL);
+        el_set (m_editline, EL_BIND, "^?", "lldb-delete-previous-char", NULL);
+        el_set (m_editline, EL_BIND, "^d", "lldb-delete-next-char", NULL);
+        el_set (m_editline, EL_BIND, ESCAPE "[3~", "lldb-delete-next-char", NULL);
+        el_set (m_editline, EL_BIND, ESCAPE "[\\^", "lldb-revert-line", NULL);
+
+        // Editor-specific bindings
+        if (IsEmacs())
+        {
+            el_set (m_editline, EL_BIND, ESCAPE "<", "lldb-buffer-start", NULL);
+            el_set (m_editline, EL_BIND, ESCAPE ">", "lldb-buffer-end", NULL);
+            el_set (m_editline, EL_BIND, ESCAPE "[A", "lldb-previous-line", NULL);
+            el_set (m_editline, EL_BIND, ESCAPE "[B", "lldb-next-line", NULL);
+        }
+        else
+        {
+            el_set (m_editline, EL_BIND, "^H", "lldb-delete-previous-char", NULL);
+            
+            el_set (m_editline, EL_BIND, "-a", ESCAPE "[A", "lldb-previous-line", NULL);
+            el_set (m_editline, EL_BIND, "-a", ESCAPE "[B", "lldb-next-line", NULL);
+            el_set (m_editline, EL_BIND, "-a", "x", "lldb-delete-next-char", NULL);
+            el_set (m_editline, EL_BIND, "-a", "^H", "lldb-delete-previous-char", NULL);
+            el_set (m_editline, EL_BIND, "-a", "^?", "lldb-delete-previous-char", NULL);
+            
+            // Escape is absorbed exiting edit mode, so re-register important sequences
+            // without the prefix
+            el_set (m_editline, EL_BIND, "-a", "[A", "lldb-previous-line", NULL);
+            el_set (m_editline, EL_BIND, "-a", "[B", "lldb-next-line", NULL);
+            el_set (m_editline, EL_BIND, "-a", "[\\^", "lldb-revert-line", NULL);
+        }
+    }
 }
 
+//------------------------------------------------------------------
+// Editline public methods
+//------------------------------------------------------------------
+
 Editline *
-Editline::GetClientData (::EditLine *e)
+Editline::InstanceFor (EditLine * editline)
 {
-    Editline *editline = NULL;
-    if (e && ::el_get(e, EL_CLIENTDATA, &editline) == 0)
-        return editline;
-    return NULL;
+    Editline * editor;
+    el_get (editline, EL_CLIENTDATA, &editor);
+    return editor;
+}
+
+Editline::Editline (const char * editline_name, FILE * input_file, FILE * output_file, FILE * error_file, bool color_prompts) :
+    m_editor_status (EditorStatus::Complete),
+    m_color_prompts(color_prompts),
+    m_input_file (input_file),
+    m_output_file (output_file),
+    m_error_file (error_file),
+    m_input_connection (fileno(input_file), false)
+{
+    // Get a shared history instance
+    m_editor_name = (editline_name == nullptr) ? "lldb-tmp" : editline_name;
+    m_history_sp = EditlineHistory::GetHistory (m_editor_name);
 }
 
-FILE *
-Editline::GetInputFile ()
+Editline::~Editline()
 {
-    return GetFilePointer (m_editline, 0);
+    if (m_editline) {
+        // Disable edit mode to stop the terminal from flushing all input
+        // during the call to el_end() since we expect to have multiple editline
+        // instances in this program.
+        el_set (m_editline, EL_EDITMODE, 0);
+        el_end (m_editline);
+        m_editline = nullptr;
+    }
+
+    // EditlineHistory objects are sometimes shared between multiple
+    // Editline instances with the same program name. So just release
+    // our shared pointer and if we are the last owner, it will save the
+    // history to the history save file automatically.
+    m_history_sp.reset();
 }
 
-FILE *
-Editline::GetOutputFile ()
+void
+Editline::SetPrompt (const char * prompt)
 {
-    return GetFilePointer (m_editline, 1);
+    m_set_prompt = prompt == nullptr ? "" : prompt;
 }
 
-FILE *
-Editline::GetErrorFile ()
+void
+Editline::SetContinuationPrompt (const char * continuation_prompt)
 {
-    return GetFilePointer (m_editline, 2);
+    m_set_continuation_prompt = continuation_prompt == nullptr ? "" : continuation_prompt;
 }
 
-const char *
-Editline::GetPrompt()
+void
+Editline::TerminalSizeChanged()
 {
-    if (m_prompt_with_line_numbers && m_lines_curr_line > 0)
-    {
-        StreamString strm;
-        strm.Printf("%3u: ", m_lines_curr_line);
-        m_lines_prompt = std::move(strm.GetString());
-        return m_lines_prompt.c_str();
-    }
-    else
-    {
-        return m_prompt.c_str();
+    if (m_editline != nullptr) {
+        el_resize (m_editline);
+        int columns;
+        // Despite the man page claiming non-zero indicates success, it's actually zero
+        if (el_get (m_editline, EL_GETTC, "co", &columns) == 0) {
+            m_terminal_width = columns;
+            if (m_current_line_rows != -1) {
+                const LineInfoW * info = el_wline (m_editline);
+                int lineLength = (int)((info->lastchar - info->buffer) + GetPromptWidth());
+                m_current_line_rows = (lineLength / columns) + 1;
+            }
+        }
+        else {
+            m_terminal_width = INT_MAX;
+            m_current_line_rows = 1;
+        }
     }
 }
 
-void
-Editline::SetPrompt (const char *p)
+const char *
+Editline::GetPrompt()
 {
-    if (p && p[0])
-        m_prompt = p;
-    else
-        m_prompt.clear();
-    size_t start_pos = 0;
-    size_t escape_pos;
-    while ((escape_pos = m_prompt.find('\033', start_pos)) != std::string::npos)
-    {
-        m_prompt.insert(escape_pos, 1, k_prompt_escape_char);
-        start_pos += 2;
-    }
+    return m_set_prompt.c_str();
 }
 
-FILE *
-Editline::GetFilePointer (::EditLine *e, int fd)
+uint32_t
+Editline::GetCurrentLine()
 {
-    FILE *file_ptr = NULL;
-    if (e && ::el_get(e, EL_GETFP, fd, &file_ptr) == 0)
-        return file_ptr;
-    return NULL;
+    return m_current_line_index;
 }
 
-unsigned char
-Editline::CallbackEditPrevLine (::EditLine *e, int ch)
+void
+Editline::Hide()
 {
-    Editline *editline = GetClientData (e);
-    if (editline->m_lines_curr_line > 1)
+    // Make sure we're at a stable location waiting for input
+    while (m_editor_status == EditorStatus::Editing && !m_editor_getting_char)
     {
-        editline->m_lines_command = Command::EditPrevLine;
-        return CC_NEWLINE;
+        usleep(100000);
     }
-    return CC_ERROR;
-}
-unsigned char
-Editline::CallbackEditNextLine (::EditLine *e, int ch)
-{
-    Editline *editline = GetClientData (e);
-    if (editline->m_lines_curr_line < editline->m_lines_max_line)
+    
+    // Clear the existing input
+    if (m_editor_status == EditorStatus::Editing)
     {
-        editline->m_lines_command = Command::EditNextLine;
-        return CC_NEWLINE;
+        MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
+        fprintf(m_output_file, ANSI_CLEAR_BELOW);
     }
-    return CC_ERROR;
 }
 
-unsigned char
-Editline::CallbackComplete (::EditLine *e, int ch)
+void
+Editline::Refresh()
 {
-    Editline *editline = GetClientData (e);
-    if (editline)
-        return editline->HandleCompletion (ch);
-    return CC_ERROR;
+    if (m_editor_status == EditorStatus::Editing)
+    {
+        DisplayInput();
+        MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor);
+    }
 }
 
-const char *
-Editline::GetPromptCallback (::EditLine *e)
+bool
+Editline::Interrupt()
 {
-    Editline *editline = GetClientData (e);
-    if (editline)
-        return editline->GetPrompt();
-    return "";
+    if (m_editor_status == EditorStatus::Editing)
+    {
+        return m_input_connection.InterruptRead();
+    }
+    return false; // Interrupt not handled as we weren't getting a line or lines
 }
 
-int
-Editline::GetCharFromInputFileCallback (EditLine *e, char *c)
+void
+Editline::SetAutoCompleteCallback (CompleteCallbackType callback, void * baton)
 {
-    Editline *editline = GetClientData (e);
-    if (editline && editline->m_got_eof == false)
-    {
-        FILE *f = editline->GetInputFile();
-        if (f == NULL)
-        {
-            editline->m_got_eof = true;
-            return 0;
-        }
-        
-        
-        while (1)
-        {
-            lldb::ConnectionStatus status = eConnectionStatusSuccess;
-            char ch = 0;
-            // When we start to call el_gets() the editline library needs to
-            // output the prompt
-            editline->m_getting_char.SetValue(true, eBroadcastAlways);
-            const size_t n = editline->m_file.Read(&ch, 1, UINT32_MAX, status, NULL);
-            editline->m_getting_char.SetValue(false, eBroadcastAlways);
-            if (n)
-            {
-                if (ch == '\x04')
-                {
-                    // Only turn a CTRL+D into a EOF if we receive the
-                    // CTRL+D an empty line, otherwise it will forward
-                    // delete the character at the cursor
-                    const LineInfo *line_info = ::el_line(e);
-                    if (line_info != NULL &&
-                        line_info->buffer == line_info->cursor &&
-                        line_info->cursor == line_info->lastchar)
-                    {
-                        editline->m_got_eof = true;
-                        break;
-                    }
-                }
-            
-                if (status == eConnectionStatusEndOfFile)
-                {
-                    editline->m_got_eof = true;
-                    break;
-                }
-                else
-                {
-                    *c = ch;
-                    return 1;
-                }
-            }
-            else
-            {
-                switch (status)
-                {
-                    case eConnectionStatusInterrupted:
-                        editline->m_interrupted = true;
-                        *c = '\n';
-                        return 1;
-
-                    case eConnectionStatusSuccess:         // Success
-                        break;
-                        
-                    case eConnectionStatusError:           // Check GetError() for details
-                    case eConnectionStatusTimedOut:        // Request timed out
-                    case eConnectionStatusEndOfFile:       // End-of-file encountered
-                    case eConnectionStatusNoConnection:    // No connection
-                    case eConnectionStatusLostConnection:  // Lost connection while connected to a valid connection
-                        editline->m_got_eof = true;
-                        break;
-                }
-            }
-        }
-    }
-    return 0;
+    m_completion_callback = callback;
+    m_completion_callback_baton = baton;
 }
 
 void
-Editline::Hide ()
+Editline::SetIsInputCompleteCallback (IsInputCompleteCallbackType callback, void * baton)
 {
-    if (m_getting_line)
-    {
-        // If we are getting a line, we might have started to call el_gets() and
-        // it might be printing the prompt. Here we make sure we are actually getting
-        // a character. This way we know the entire prompt has been printed.
-        TimeValue timeout = TimeValue::Now();
-        timeout.OffsetWithSeconds(1);
-        if (m_getting_char.WaitForValueEqualTo(true, &timeout))
-        {
-            FILE *out_file = GetOutputFile();
-            if (out_file)
-            {
-                const LineInfo *line_info  = ::el_line(m_editline);
-                if (line_info)
-                    ::fprintf (out_file, "\033[%uD\033[K", (uint32_t)(strlen(GetPrompt()) + line_info->cursor - line_info->buffer));
-            }
-        }
-    }
+    m_is_input_complete_callback = callback;
+    m_is_input_complete_callback_baton = baton;
 }
 
+bool
+Editline::SetFixIndentationCallback (FixIndentationCallbackType callback,
+                                     void * baton,
+                                     const char * indent_chars)
+{
+    m_fix_indentation_callback = callback;
+    m_fix_indentation_callback_baton = baton;
+    m_fix_indentation_callback_chars = indent_chars;
+    return false;
+}
 
-void
-Editline::Refresh()
+bool
+Editline::GetLine (std::string &line, bool &interrupted)
 {
-    if (m_getting_line)
+    ConfigureEditor (false);
+    m_input_lines = std::vector<EditLineStringType>();
+    m_input_lines.insert (m_input_lines.begin(), EditLineConstString(""));
+    
+    SetCurrentLine (0);
+    m_in_history = false;
+    m_editor_status = EditorStatus::Editing;
+    m_editor_getting_char = false;
+    m_revert_cursor_index = -1;
+
+    int count;
+    auto input = el_wgets (m_editline, &count);
+
+    interrupted = m_editor_status == EditorStatus::Interrupted;
+    if (!interrupted)
     {
-        // If we are getting a line, we might have started to call el_gets() and
-        // it might be printing the prompt. Here we make sure we are actually getting
-        // a character. This way we know the entire prompt has been printed.
-        TimeValue timeout = TimeValue::Now();
-        timeout.OffsetWithSeconds(1);
-        if (m_getting_char.WaitForValueEqualTo(true, &timeout))
+        if (input == nullptr)
         {
-            ::el_set (m_editline, EL_REFRESH);
+            fprintf (m_output_file, "\n");
+            m_editor_status = EditorStatus::EndOfInput;
+        }
+        else
+        {
+            m_history_sp->Enter (input);
+#if LLDB_EDITLINE_USE_WCHAR
+            line = m_utf8conv.to_bytes (SplitLines (input)[0]);
+#else
+            line = SplitLines (input)[0];
+#endif
+            m_editor_status = EditorStatus::Complete;
         }
     }
+    return m_editor_status != EditorStatus::EndOfInput;
 }
 
 bool
-Editline::Interrupt ()
+Editline::GetLines (int first_line_number, StringList &lines, bool &interrupted)
 {
-    m_interrupted = true;
-    if (m_getting_line || m_lines_curr_line > 0)
-        return m_file.InterruptRead();
-    return false; // Interrupt not handled as we weren't getting a line or lines
+    ConfigureEditor (true);
+    
+    // Print the initial input lines, then move the cursor back up to the start of input
+    SetBaseLineNumber (first_line_number);
+    m_input_lines = std::vector<EditLineStringType>();
+    m_input_lines.insert (m_input_lines.begin(), EditLineConstString(""));
+    
+    // Begin the line editing loop
+    DisplayInput();
+    SetCurrentLine (0);
+    MoveCursor (CursorLocation::BlockEnd, CursorLocation::BlockStart);
+    m_editor_status = EditorStatus::Editing;
+    m_editor_getting_char = false;
+    m_in_history = false;
+
+    m_revert_cursor_index = -1;
+    while (m_editor_status == EditorStatus::Editing)
+    {
+        int count;
+        m_current_line_rows = -1;
+        el_wpush (m_editline, EditLineConstString("\x1b[^")); // Revert to the existing line content
+        el_wgets (m_editline, &count);
+    }
+    
+    interrupted = m_editor_status == EditorStatus::Interrupted;
+    if (!interrupted)
+    {
+        // Save the completed entry in history before returning
+        m_history_sp->Enter (CombineLines (m_input_lines).c_str());
+
+        lines = GetInputAsStringList();
+    }
+    return m_editor_status != EditorStatus::EndOfInput;
 }

Modified: lldb/trunk/source/Interpreter/CommandInterpreter.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Interpreter/CommandInterpreter.cpp?rev=222163&r1=222162&r2=222163&view=diff
==============================================================================
--- lldb/trunk/source/Interpreter/CommandInterpreter.cpp (original)
+++ lldb/trunk/source/Interpreter/CommandInterpreter.cpp Mon Nov 17 13:06:59 2014
@@ -2737,13 +2737,16 @@ CommandInterpreter::HandleCommandsFromFi
             lldb::StreamFileSP empty_stream_sp;
             m_command_source_flags.push_back(flags);
             IOHandlerSP io_handler_sp (new IOHandlerEditline (debugger,
+                                                              IOHandler::Type::CommandInterpreter,
                                                               input_file_sp,
                                                               empty_stream_sp, // Pass in an empty stream so we inherit the top input reader output stream
                                                               empty_stream_sp, // Pass in an empty stream so we inherit the top input reader error stream
                                                               flags,
                                                               nullptr, // Pass in NULL for "editline_name" so no history is saved, or written
                                                               debugger.GetPrompt(),
+                                                              NULL,
                                                               false, // Not multi-line
+                                                              debugger.GetUseColor(),
                                                               0,
                                                               *this));
             const bool old_async_execution = debugger.GetAsyncExecution();
@@ -3181,9 +3184,12 @@ CommandInterpreter::GetLLDBCommandsFromI
 {
     Debugger &debugger = GetDebugger();
     IOHandlerSP io_handler_sp (new IOHandlerEditline (debugger,
+                                                      IOHandler::Type::CommandList,
                                                       "lldb",       // Name of input reader for history
                                                       prompt,       // Prompt
+                                                      NULL,         // Continuation prompt
                                                       true,         // Get multiple lines
+                                                      debugger.GetUseColor(),
                                                       0,            // Don't show line numbers
                                                       delegate));   // IOHandlerDelegate
     
@@ -3207,9 +3213,12 @@ CommandInterpreter::GetPythonCommandsFro
 {
     Debugger &debugger = GetDebugger();
     IOHandlerSP io_handler_sp (new IOHandlerEditline (debugger,
+                                                      IOHandler::Type::PythonCode,
                                                       "lldb-python",    // Name of input reader for history
                                                       prompt,           // Prompt
+                                                      NULL,             // Continuation prompt
                                                       true,             // Get multiple lines
+                                                      debugger.GetUseColor(),
                                                       0,                // Don't show line numbers
                                                       delegate));       // IOHandlerDelegate
     
@@ -3230,47 +3239,65 @@ CommandInterpreter::IsActive ()
     return m_debugger.IsTopIOHandler (m_command_io_handler_sp);
 }
 
+lldb::IOHandlerSP
+CommandInterpreter::GetIOHandler(bool force_create, CommandInterpreterRunOptions *options)
+{
+    // Always re-create the IOHandlerEditline in case the input
+    // changed. The old instance might have had a non-interactive
+    // input and now it does or vice versa.
+    if (force_create || !m_command_io_handler_sp)
+    {
+        // Always re-create the IOHandlerEditline in case the input
+        // changed. The old instance might have had a non-interactive
+        // input and now it does or vice versa.
+        uint32_t flags = 0;
+        
+        if (options)
+        {
+            if (options->m_stop_on_continue == eLazyBoolYes)
+                flags |= eHandleCommandFlagStopOnContinue;
+            if (options->m_stop_on_error == eLazyBoolYes)
+                flags |= eHandleCommandFlagStopOnError;
+            if (options->m_stop_on_crash == eLazyBoolYes)
+                flags |= eHandleCommandFlagStopOnCrash;
+            if (options->m_echo_commands != eLazyBoolNo)
+                flags |= eHandleCommandFlagEchoCommand;
+            if (options->m_print_results != eLazyBoolNo)
+                flags |= eHandleCommandFlagPrintResult;
+        }
+        else
+        {
+            flags = eHandleCommandFlagEchoCommand | eHandleCommandFlagPrintResult;
+        }
+        
+        m_command_io_handler_sp.reset(new IOHandlerEditline (m_debugger,
+                                                             IOHandler::Type::CommandInterpreter,
+                                                             m_debugger.GetInputFile(),
+                                                             m_debugger.GetOutputFile(),
+                                                             m_debugger.GetErrorFile(),
+                                                             flags,
+                                                             "lldb",
+                                                             m_debugger.GetPrompt(),
+                                                             NULL,                      // Continuation prompt
+                                                             false,                     // Don't enable multiple line input, just single line commands
+                                                             m_debugger.GetUseColor(),
+                                                             0,            // Don't show line numbers
+                                                             *this));
+    }
+    return m_command_io_handler_sp;
+}
+
 void
 CommandInterpreter::RunCommandInterpreter(bool auto_handle_events,
                                           bool spawn_thread,
                                           CommandInterpreterRunOptions &options)
 {
-    // Only get one line at a time
-    const bool multiple_lines = false;
-    m_num_errors = 0;
-    m_quit_requested = false;
+    // Always re-create the command intepreter when we run it in case
+    // any file handles have changed.
+    bool force_create = true;
+    m_debugger.PushIOHandler(GetIOHandler(force_create, &options));
     m_stopped_for_crash = false;
     
-    // Always re-create the IOHandlerEditline in case the input
-    // changed. The old instance might have had a non-interactive
-    // input and now it does or vice versa.
-    uint32_t flags= 0;
-
-    if (options.m_stop_on_continue == eLazyBoolYes)
-        flags |= eHandleCommandFlagStopOnContinue;
-    if (options.m_stop_on_error == eLazyBoolYes)
-        flags |= eHandleCommandFlagStopOnError;
-    if (options.m_stop_on_crash == eLazyBoolYes)
-        flags |= eHandleCommandFlagStopOnCrash;
-    if (options.m_echo_commands != eLazyBoolNo)
-      flags |= eHandleCommandFlagEchoCommand;
-    if (options.m_print_results != eLazyBoolNo)
-        flags |= eHandleCommandFlagPrintResult;
-
-
-    m_command_io_handler_sp.reset(new IOHandlerEditline (m_debugger,
-                                                         m_debugger.GetInputFile(),
-                                                         m_debugger.GetOutputFile(),
-                                                         m_debugger.GetErrorFile(),
-                                                         flags,
-                                                         "lldb",
-                                                         m_debugger.GetPrompt(),
-                                                         multiple_lines,
-                                                         0,            // Don't show line numbers
-                                                         *this));
-
-    m_debugger.PushIOHandler(m_command_io_handler_sp);
-    
     if (auto_handle_events)
         m_debugger.StartEventHandlerThread();
     
@@ -3281,10 +3308,10 @@ CommandInterpreter::RunCommandInterprete
     else
     {
         m_debugger.ExecuteIOHanders();
-    
+        
         if (auto_handle_events)
             m_debugger.StopEventHandlerThread();
     }
-
+    
 }
 

Modified: lldb/trunk/source/Interpreter/ScriptInterpreterPython.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Interpreter/ScriptInterpreterPython.cpp?rev=222163&r1=222162&r2=222163&view=diff
==============================================================================
--- lldb/trunk/source/Interpreter/ScriptInterpreterPython.cpp (original)
+++ lldb/trunk/source/Interpreter/ScriptInterpreterPython.cpp Mon Nov 17 13:06:59 2014
@@ -728,7 +728,7 @@ public:
     
     IOHandlerPythonInterpreter (Debugger &debugger,
                                 ScriptInterpreterPython *python) :
-        IOHandler (debugger),
+        IOHandler (debugger, IOHandler::Type::PythonInterpreter),
         m_python(python)
     {
         

Modified: lldb/trunk/source/Target/Process.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Target/Process.cpp?rev=222163&r1=222162&r2=222163&view=diff
==============================================================================
--- lldb/trunk/source/Target/Process.cpp (original)
+++ lldb/trunk/source/Target/Process.cpp Mon Nov 17 13:06:59 2014
@@ -4893,7 +4893,7 @@ class IOHandlerProcessSTDIO :
 public:
     IOHandlerProcessSTDIO (Process *process,
                            int write_fd) :
-        IOHandler(process->GetTarget().GetDebugger()),
+    IOHandler(process->GetTarget().GetDebugger(), IOHandler::Type::ProcessIO),
         m_process (process),
         m_read_file (),
         m_write_file (write_fd, false),





More information about the lldb-commits mailing list