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

llvm-svn: 222163
This commit is contained in:
Kate Stone 2014-11-17 19:06:59 +00:00
parent 278ddec22c
commit e30f11d9ee
10 changed files with 1961 additions and 1008 deletions

View File

@ -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.
/// 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 LineStatus
IOHandlerLinesUpdated (IOHandler &io_handler,
StringList &lines,
uint32_t line_idx,
Error &error)
virtual bool
IOHandlerIsInputComplete (IOHandler &io_handler,
StringList &lines)
{
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.
// 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)
@ -441,12 +537,30 @@ namespace lldb_private {
return m_delegate.IOHandlerGetControlSequence (ch);
}
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;
}
std::stack<lldb::IOHandlerSP> m_stack;
const char *
GetTopIOHandlerHelpPrologue()
{
if (m_top)
return m_top->GetHelpPrologue();
return NULL;
}
protected:
typedef std::vector<lldb::IOHandlerSP> collection;
collection m_stack;
mutable Mutex m_mutex;
IOHandler *m_top;

View File

@ -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 "lldb/lldb-private.h"
#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);
// 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
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);
typedef int (* EditlineGetCharCallbackType)(::EditLine * editline, EditLineCharType * c);
typedef unsigned char (* EditlineCommandCallbackType)(::EditLine * editline, int ch);
typedef const char * (* EditlinePromptCallbackType)(::EditLine * editline);
~Editline();
class EditlineHistory;
typedef std::shared_ptr<EditlineHistory> EditlineHistorySP;
Error
GetLine (std::string &line,
bool &interrupted);
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,
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);
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;
/// 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);
using namespace line_editor;
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);
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_

View File

@ -627,6 +627,9 @@ public:
return m_quit_requested;
}
lldb::IOHandlerSP
GetIOHandler(bool force_create = false, CommandInterpreterRunOptions *options = NULL);
bool
GetStoppedForCrash () const
{

View File

@ -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));

View File

@ -425,11 +425,15 @@ CommandObjectExpression::GetMultilineExpression ()
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));

View File

@ -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 (IOHandler &io_handler,
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 (Debugger &debugger,
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 &line, bool &interrupted)
#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 &line, bool &interrupted)
{
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 &line, bool &interrupted)
#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 *p)
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
if (m_editline_ap)
m_editline_ap->ShowLineNumbers (true, line);
#endif
}
uint32_t
IOHandlerEditline::GetCurrentLineIndex () const
{
#ifdef LLDB_DISABLE_LIBEDIT
if (m_editline_ap)
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 &lines, bool &interrupted)
::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)
{
}

File diff suppressed because it is too large Load Diff

View File

@ -2737,13 +2737,16 @@ CommandInterpreter::HandleCommandsFromFile (FileSpec &cmd_file,
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::GetLLDBCommandsFromIOHandler (const char *prompt,
{
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::GetPythonCommandsFromIOHandler (const char *prompt,
{
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::RunCommandInterpreter(bool auto_handle_events,
else
{
m_debugger.ExecuteIOHanders();
if (auto_handle_events)
m_debugger.StopEventHandlerThread();
}
}

View File

@ -728,7 +728,7 @@ public:
IOHandlerPythonInterpreter (Debugger &debugger,
ScriptInterpreterPython *python) :
IOHandler (debugger),
IOHandler (debugger, IOHandler::Type::PythonInterpreter),
m_python(python)
{

View File

@ -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),