LLDB  mainline
Editline.h
Go to the documentation of this file.
1 //===-- Editline.h ----------------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 // TODO: wire up window size changes
10 
11 // If we ever get a private copy of libedit, there are a number of defects that
12 // would be nice to fix;
13 // a) Sometimes text just disappears while editing. In an 80-column editor
14 // paste the following text, without
15 // the quotes:
16 // "This is a test of the input system missing Hello, World! Do you
17 // disappear when it gets to a particular length?"
18 // Now press ^A to move to the start and type 3 characters, and you'll see a
19 // good amount of the text will
20 // disappear. It's still in the buffer, just invisible.
21 // b) The prompt printing logic for dealing with ANSI formatting characters is
22 // broken, which is why we're
23 // working around it here.
24 // c) When resizing the terminal window, if the cursor moves between rows
25 // libedit will get confused. d) The incremental search uses escape to cancel
26 // input, so it's confused by
27 // ANSI sequences starting with escape.
28 // e) Emoji support is fairly terrible, presumably it doesn't understand
29 // composed characters?
30 
31 #ifndef liblldb_Editline_h_
32 #define liblldb_Editline_h_
33 #if defined(__cplusplus)
34 
35 #if LLDB_EDITLINE_USE_WCHAR
36 #include <codecvt>
37 #endif
38 #include <locale>
39 #include <sstream>
40 #include <vector>
41 
43 #include "lldb/lldb-private.h"
44 
45 #if defined(_WIN32)
47 #elif !defined(__ANDROID__)
48 #include <histedit.h>
49 #endif
50 
51 #include <mutex>
52 #include <string>
53 #include <vector>
54 
56 #include "lldb/Utility/FileSpec.h"
57 #include "lldb/Utility/Predicate.h"
58 
59 namespace lldb_private {
60 namespace line_editor {
61 
62 // type alias's to help manage 8 bit and wide character versions of libedit
63 #if LLDB_EDITLINE_USE_WCHAR
64 using EditLineStringType = std::wstring;
65 using EditLineStringStreamType = std::wstringstream;
66 using EditLineCharType = wchar_t;
67 #else
68 using EditLineStringType = std::string;
69 using EditLineStringStreamType = std::stringstream;
70 using EditLineCharType = char;
71 #endif
72 
73 // At one point the callback type of el_set getchar callback changed from char
74 // to wchar_t. It is not possible to detect differentiate between the two
75 // versions exactly, but this is a pretty good approximation and allows us to
76 // build against almost any editline version out there.
77 #if LLDB_EDITLINE_USE_WCHAR || defined(EL_CLIENTDATA) || LLDB_HAVE_EL_RFUNC_T
78 using EditLineGetCharType = wchar_t;
79 #else
80 using EditLineGetCharType = char;
81 #endif
82 
83 typedef int (*EditlineGetCharCallbackType)(::EditLine *editline,
84  EditLineGetCharType *c);
85 typedef unsigned char (*EditlineCommandCallbackType)(::EditLine *editline,
86  int ch);
87 typedef const char *(*EditlinePromptCallbackType)(::EditLine *editline);
88 
89 class EditlineHistory;
90 
91 typedef std::shared_ptr<EditlineHistory> EditlineHistorySP;
92 
93 typedef bool (*IsInputCompleteCallbackType)(Editline *editline,
94  StringList &lines, void *baton);
95 
96 typedef int (*FixIndentationCallbackType)(Editline *editline,
97  const StringList &lines,
98  int cursor_position, void *baton);
99 
100 typedef int (*CompleteCallbackType)(const char *current_line,
101  const char *cursor, const char *last_char,
102  int skip_first_n_matches, int max_matches,
103  StringList &matches,
104  StringList &descriptions, void *baton);
105 
106 /// Status used to decide when and how to start editing another line in
107 /// multi-line sessions
108 enum class EditorStatus {
109 
110  /// The default state proceeds to edit the current line
111  Editing,
112 
113  /// Editing complete, returns the complete set of edited lines
114  Complete,
115 
116  /// End of input reported
117  EndOfInput,
118 
119  /// Editing interrupted
120  Interrupted
121 };
122 
123 /// Established locations that can be easily moved among with MoveCursor
124 enum class CursorLocation {
125  /// The start of the first line in a multi-line edit session
126  BlockStart,
127 
128  /// The start of the current line in a multi-line edit session
129  EditingPrompt,
130 
131  /// The location of the cursor on the current line in a multi-line edit
132  /// session
133  EditingCursor,
134 
135  /// The location immediately after the last character in a multi-line edit
136  /// session
137  BlockEnd
138 };
139 }
140 
141 using namespace line_editor;
142 
143 /// Instances of Editline provide an abstraction over libedit's EditLine
144 /// facility. Both
145 /// single- and multi-line editing are supported.
146 class Editline {
147 public:
148  Editline(const char *editor_name, FILE *input_file, FILE *output_file,
149  FILE *error_file, bool color_prompts);
150 
151  ~Editline();
152 
153  /// Uses the user data storage of EditLine to retrieve an associated instance
154  /// of Editline.
155  static Editline *InstanceFor(::EditLine *editline);
156 
157  /// Sets a string to be used as a prompt, or combined with a line number to
158  /// form a prompt.
159  void SetPrompt(const char *prompt);
160 
161  /// Sets an alternate string to be used as a prompt for the second line and
162  /// beyond in multi-line
163  /// editing scenarios.
164  void SetContinuationPrompt(const char *continuation_prompt);
165 
166  /// Required to update the width of the terminal registered for I/O. It is
167  /// critical that this
168  /// be correct at all times.
169  void TerminalSizeChanged();
170 
171  /// Returns the prompt established by SetPrompt()
172  const char *GetPrompt();
173 
174  /// Returns the index of the line currently being edited
175  uint32_t GetCurrentLine();
176 
177  /// Interrupt the current edit as if ^C was pressed
178  bool Interrupt();
179 
180  /// Cancel this edit and oblitarate all trace of it
181  bool Cancel();
182 
183  /// Register a callback for the tab key
184  void SetAutoCompleteCallback(CompleteCallbackType callback, void *baton);
185 
186  /// Register a callback for testing whether multi-line input is complete
187  void SetIsInputCompleteCallback(IsInputCompleteCallbackType callback,
188  void *baton);
189 
190  /// Register a callback for determining the appropriate indentation for a line
191  /// when creating a newline. An optional set of insertable characters can
192  /// also
193  /// trigger the callback.
194  bool SetFixIndentationCallback(FixIndentationCallbackType callback,
195  void *baton, const char *indent_chars);
196 
197  /// Prompts for and reads a single line of user input.
198  bool GetLine(std::string &line, bool &interrupted);
199 
200  /// Prompts for and reads a multi-line batch of user input.
201  bool GetLines(int first_line_number, StringList &lines, bool &interrupted);
202 
203  void PrintAsync(Stream *stream, const char *s, size_t len);
204 
205 private:
206  /// Sets the lowest line number for multi-line editing sessions. A value of
207  /// zero suppresses
208  /// line number printing in the prompt.
209  void SetBaseLineNumber(int line_number);
210 
211  /// Returns the complete prompt by combining the prompt or continuation prompt
212  /// with line numbers
213  /// as appropriate. The line index is a zero-based index into the current
214  /// multi-line session.
215  std::string PromptForIndex(int line_index);
216 
217  /// Sets the current line index between line edits to allow free movement
218  /// between lines. Updates
219  /// the prompt to match.
220  void SetCurrentLine(int line_index);
221 
222  /// Determines the width of the prompt in characters. The width is guaranteed
223  /// to be the same for
224  /// all lines of the current multi-line session.
225  int GetPromptWidth();
226 
227  /// Returns true if the underlying EditLine session's keybindings are
228  /// Emacs-based, or false if
229  /// they are VI-based.
230  bool IsEmacs();
231 
232  /// Returns true if the current EditLine buffer contains nothing but spaces,
233  /// or is empty.
234  bool IsOnlySpaces();
235 
236  /// Helper method used by MoveCursor to determine relative line position.
237  int GetLineIndexForLocation(CursorLocation location, int cursor_row);
238 
239  /// Move the cursor from one well-established location to another using
240  /// relative line positioning
241  /// and absolute column positioning.
242  void MoveCursor(CursorLocation from, CursorLocation to);
243 
244  /// Clear from cursor position to bottom of screen and print input lines
245  /// including prompts, optionally
246  /// starting from a specific line. Lines are drawn with an extra space at the
247  /// end to reserve room for
248  /// the rightmost cursor position.
249  void DisplayInput(int firstIndex = 0);
250 
251  /// Counts the number of rows a given line of content will end up occupying,
252  /// taking into account both
253  /// the preceding prompt and a single trailing space occupied by a cursor when
254  /// at the end of the line.
255  int CountRowsForLine(const EditLineStringType &content);
256 
257  /// Save the line currently being edited
258  void SaveEditedLine();
259 
260  /// Convert the current input lines into a UTF8 StringList
261  StringList GetInputAsStringList(int line_count = UINT32_MAX);
262 
263  /// Replaces the current multi-line session with the next entry from history.
264  /// When the parameter is
265  /// true it will take the next earlier entry from history, when it is false it
266  /// takes the next most
267  /// recent.
268  unsigned char RecallHistory(bool earlier);
269 
270  /// Character reading implementation for EditLine that supports our multi-line
271  /// editing trickery.
272  int GetCharacter(EditLineGetCharType *c);
273 
274  /// Prompt implementation for EditLine.
275  const char *Prompt();
276 
277  /// Line break command used when meta+return is pressed in multi-line mode.
278  unsigned char BreakLineCommand(int ch);
279 
280  /// Command used when return is pressed in multi-line mode.
281  unsigned char EndOrAddLineCommand(int ch);
282 
283  /// Delete command used when delete is pressed in multi-line mode.
284  unsigned char DeleteNextCharCommand(int ch);
285 
286  /// Delete command used when backspace is pressed in multi-line mode.
287  unsigned char DeletePreviousCharCommand(int ch);
288 
289  /// Line navigation command used when ^P or up arrow are pressed in multi-line
290  /// mode.
291  unsigned char PreviousLineCommand(int ch);
292 
293  /// Line navigation command used when ^N or down arrow are pressed in
294  /// multi-line mode.
295  unsigned char NextLineCommand(int ch);
296 
297  /// History navigation command used when Alt + up arrow is pressed in
298  /// multi-line mode.
299  unsigned char PreviousHistoryCommand(int ch);
300 
301  /// History navigation command used when Alt + down arrow is pressed in
302  /// multi-line mode.
303  unsigned char NextHistoryCommand(int ch);
304 
305  /// Buffer start command used when Esc < is typed in multi-line emacs mode.
306  unsigned char BufferStartCommand(int ch);
307 
308  /// Buffer end command used when Esc > is typed in multi-line emacs mode.
309  unsigned char BufferEndCommand(int ch);
310 
311  /// Context-sensitive tab insertion or code completion command used when the
312  /// tab key is typed.
313  unsigned char TabCommand(int ch);
314 
315  /// Respond to normal character insertion by fixing line indentation
316  unsigned char FixIndentationCommand(int ch);
317 
318  /// Revert line command used when moving between lines.
319  unsigned char RevertLineCommand(int ch);
320 
321  /// Ensures that the current EditLine instance is properly configured for
322  /// single or multi-line editing.
323  void ConfigureEditor(bool multiline);
324 
325  bool CompleteCharacter(char ch, EditLineGetCharType &out);
326 
327 private:
328 #if LLDB_EDITLINE_USE_WCHAR
329  std::wstring_convert<std::codecvt_utf8<wchar_t>> m_utf8conv;
330 #endif
331  ::EditLine *m_editline = nullptr;
332  EditlineHistorySP m_history_sp;
333  bool m_in_history = false;
334  std::vector<EditLineStringType> m_live_history_lines;
335  bool m_multiline_enabled = false;
336  std::vector<EditLineStringType> m_input_lines;
337  EditorStatus m_editor_status;
338  bool m_color_prompts = true;
339  int m_terminal_width = 0;
340  int m_base_line_number = 0;
341  unsigned m_current_line_index = 0;
342  int m_current_line_rows = -1;
343  int m_revert_cursor_index = 0;
344  int m_line_number_digits = 3;
345  std::string m_set_prompt;
346  std::string m_set_continuation_prompt;
347  std::string m_current_prompt;
348  bool m_needs_prompt_repaint = false;
349  std::string m_editor_name;
350  FILE *m_input_file;
351  FILE *m_output_file;
352  FILE *m_error_file;
353  ConnectionFileDescriptor m_input_connection;
354  IsInputCompleteCallbackType m_is_input_complete_callback = nullptr;
355  void *m_is_input_complete_callback_baton = nullptr;
356  FixIndentationCallbackType m_fix_indentation_callback = nullptr;
357  void *m_fix_indentation_callback_baton = nullptr;
358  const char *m_fix_indentation_callback_chars = nullptr;
359  CompleteCallbackType m_completion_callback = nullptr;
360  void *m_completion_callback_baton = nullptr;
361 
362  std::mutex m_output_mutex;
363 };
364 }
365 
366 #endif // #if defined(__cplusplus)
367 #endif // liblldb_Editline_h_
Enumerations for broadcasting.
Definition: SBLaunchInfo.h:14
bool IsOnlySpaces(const EditLineStringType &content)
Definition: Editline.cpp:91
#define UINT32_MAX
Definition: lldb-defines.h:31