Editline.h 13 KB

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