Mercurial > hg > CbC > CbC_llvm
comparison lldb/source/Host/common/Editline.cpp @ 150:1d019706d866
LLVM10
author | anatofuz |
---|---|
date | Thu, 13 Feb 2020 15:10:13 +0900 |
parents | |
children | 0572611fdcc8 |
comparison
equal
deleted
inserted
replaced
147:c2174574ed3a | 150:1d019706d866 |
---|---|
1 //===-- Editline.cpp ------------------------------------------------------===// | |
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 #include <iomanip> | |
10 #include <iostream> | |
11 #include <limits.h> | |
12 | |
13 #include "lldb/Host/ConnectionFileDescriptor.h" | |
14 #include "lldb/Host/Editline.h" | |
15 #include "lldb/Host/FileSystem.h" | |
16 #include "lldb/Host/Host.h" | |
17 #include "lldb/Utility/CompletionRequest.h" | |
18 #include "lldb/Utility/FileSpec.h" | |
19 #include "lldb/Utility/LLDBAssert.h" | |
20 #include "lldb/Utility/SelectHelper.h" | |
21 #include "lldb/Utility/Status.h" | |
22 #include "lldb/Utility/StreamString.h" | |
23 #include "lldb/Utility/StringList.h" | |
24 #include "lldb/Utility/Timeout.h" | |
25 | |
26 #include "llvm/Support/FileSystem.h" | |
27 #include "llvm/Support/Threading.h" | |
28 | |
29 using namespace lldb_private; | |
30 using namespace lldb_private::line_editor; | |
31 | |
32 // Workaround for what looks like an OS X-specific issue, but other platforms | |
33 // may benefit from something similar if issues arise. The libedit library | |
34 // doesn't explicitly initialize the curses termcap library, which it gets away | |
35 // with until TERM is set to VT100 where it stumbles over an implementation | |
36 // assumption that may not exist on other platforms. The setupterm() function | |
37 // would normally require headers that don't work gracefully in this context, | |
38 // so the function declaraction has been hoisted here. | |
39 #if defined(__APPLE__) | |
40 extern "C" { | |
41 int setupterm(char *term, int fildes, int *errret); | |
42 } | |
43 #define USE_SETUPTERM_WORKAROUND | |
44 #endif | |
45 | |
46 // Editline uses careful cursor management to achieve the illusion of editing a | |
47 // multi-line block of text with a single line editor. Preserving this | |
48 // illusion requires fairly careful management of cursor state. Read and | |
49 // understand the relationship between DisplayInput(), MoveCursor(), | |
50 // SetCurrentLine(), and SaveEditedLine() before making changes. | |
51 | |
52 #define ESCAPE "\x1b" | |
53 #define ANSI_FAINT ESCAPE "[2m" | |
54 #define ANSI_UNFAINT ESCAPE "[22m" | |
55 #define ANSI_CLEAR_BELOW ESCAPE "[J" | |
56 #define ANSI_CLEAR_RIGHT ESCAPE "[K" | |
57 #define ANSI_SET_COLUMN_N ESCAPE "[%dG" | |
58 #define ANSI_UP_N_ROWS ESCAPE "[%dA" | |
59 #define ANSI_DOWN_N_ROWS ESCAPE "[%dB" | |
60 | |
61 #if LLDB_EDITLINE_USE_WCHAR | |
62 | |
63 #define EditLineConstString(str) L##str | |
64 #define EditLineStringFormatSpec "%ls" | |
65 | |
66 #else | |
67 | |
68 #define EditLineConstString(str) str | |
69 #define EditLineStringFormatSpec "%s" | |
70 | |
71 // use #defines so wide version functions and structs will resolve to old | |
72 // versions for case of libedit not built with wide char support | |
73 #define history_w history | |
74 #define history_winit history_init | |
75 #define history_wend history_end | |
76 #define HistoryW History | |
77 #define HistEventW HistEvent | |
78 #define LineInfoW LineInfo | |
79 | |
80 #define el_wgets el_gets | |
81 #define el_wgetc el_getc | |
82 #define el_wpush el_push | |
83 #define el_wparse el_parse | |
84 #define el_wset el_set | |
85 #define el_wget el_get | |
86 #define el_wline el_line | |
87 #define el_winsertstr el_insertstr | |
88 #define el_wdeletestr el_deletestr | |
89 | |
90 #endif // #if LLDB_EDITLINE_USE_WCHAR | |
91 | |
92 bool IsOnlySpaces(const EditLineStringType &content) { | |
93 for (wchar_t ch : content) { | |
94 if (ch != EditLineCharType(' ')) | |
95 return false; | |
96 } | |
97 return true; | |
98 } | |
99 | |
100 static int GetOperation(HistoryOperation op) { | |
101 // The naming used by editline for the history operations is counter | |
102 // intuitive to how it's used here. | |
103 // | |
104 // - The H_PREV operation returns the previous element in the history, which | |
105 // is newer than the current one. | |
106 // | |
107 // - The H_NEXT operation returns the next element in the history, which is | |
108 // older than the current one. | |
109 // | |
110 // The naming of the enum entries match the semantic meaning. | |
111 switch(op) { | |
112 case HistoryOperation::Oldest: | |
113 return H_FIRST; | |
114 case HistoryOperation::Older: | |
115 return H_NEXT; | |
116 case HistoryOperation::Current: | |
117 return H_CURR; | |
118 case HistoryOperation::Newer: | |
119 return H_PREV; | |
120 case HistoryOperation::Newest: | |
121 return H_LAST; | |
122 } | |
123 llvm_unreachable("Fully covered switch!"); | |
124 } | |
125 | |
126 | |
127 EditLineStringType CombineLines(const std::vector<EditLineStringType> &lines) { | |
128 EditLineStringStreamType combined_stream; | |
129 for (EditLineStringType line : lines) { | |
130 combined_stream << line.c_str() << "\n"; | |
131 } | |
132 return combined_stream.str(); | |
133 } | |
134 | |
135 std::vector<EditLineStringType> SplitLines(const EditLineStringType &input) { | |
136 std::vector<EditLineStringType> result; | |
137 size_t start = 0; | |
138 while (start < input.length()) { | |
139 size_t end = input.find('\n', start); | |
140 if (end == std::string::npos) { | |
141 result.insert(result.end(), input.substr(start)); | |
142 break; | |
143 } | |
144 result.insert(result.end(), input.substr(start, end - start)); | |
145 start = end + 1; | |
146 } | |
147 return result; | |
148 } | |
149 | |
150 EditLineStringType FixIndentation(const EditLineStringType &line, | |
151 int indent_correction) { | |
152 if (indent_correction == 0) | |
153 return line; | |
154 if (indent_correction < 0) | |
155 return line.substr(-indent_correction); | |
156 return EditLineStringType(indent_correction, EditLineCharType(' ')) + line; | |
157 } | |
158 | |
159 int GetIndentation(const EditLineStringType &line) { | |
160 int space_count = 0; | |
161 for (EditLineCharType ch : line) { | |
162 if (ch != EditLineCharType(' ')) | |
163 break; | |
164 ++space_count; | |
165 } | |
166 return space_count; | |
167 } | |
168 | |
169 bool IsInputPending(FILE *file) { | |
170 // FIXME: This will be broken on Windows if we ever re-enable Editline. You | |
171 // can't use select | |
172 // on something that isn't a socket. This will have to be re-written to not | |
173 // use a FILE*, but instead use some kind of yet-to-be-created abstraction | |
174 // that select-like functionality on non-socket objects. | |
175 const int fd = fileno(file); | |
176 SelectHelper select_helper; | |
177 select_helper.SetTimeout(std::chrono::microseconds(0)); | |
178 select_helper.FDSetRead(fd); | |
179 return select_helper.Select().Success(); | |
180 } | |
181 | |
182 namespace lldb_private { | |
183 namespace line_editor { | |
184 typedef std::weak_ptr<EditlineHistory> EditlineHistoryWP; | |
185 | |
186 // EditlineHistory objects are sometimes shared between multiple Editline | |
187 // instances with the same program name. | |
188 | |
189 class EditlineHistory { | |
190 private: | |
191 // Use static GetHistory() function to get a EditlineHistorySP to one of | |
192 // these objects | |
193 EditlineHistory(const std::string &prefix, uint32_t size, bool unique_entries) | |
194 : m_history(nullptr), m_event(), m_prefix(prefix), m_path() { | |
195 m_history = history_winit(); | |
196 history_w(m_history, &m_event, H_SETSIZE, size); | |
197 if (unique_entries) | |
198 history_w(m_history, &m_event, H_SETUNIQUE, 1); | |
199 } | |
200 | |
201 const char *GetHistoryFilePath() { | |
202 // Compute the history path lazily. | |
203 if (m_path.empty() && m_history && !m_prefix.empty()) { | |
204 llvm::SmallString<128> lldb_history_file; | |
205 llvm::sys::path::home_directory(lldb_history_file); | |
206 llvm::sys::path::append(lldb_history_file, ".lldb"); | |
207 | |
208 // LLDB stores its history in ~/.lldb/. If for some reason this directory | |
209 // isn't writable or cannot be created, history won't be available. | |
210 if (!llvm::sys::fs::create_directory(lldb_history_file)) { | |
211 #if LLDB_EDITLINE_USE_WCHAR | |
212 std::string filename = m_prefix + "-widehistory"; | |
213 #else | |
214 std::string filename = m_prefix + "-history"; | |
215 #endif | |
216 llvm::sys::path::append(lldb_history_file, filename); | |
217 m_path = std::string(lldb_history_file.str()); | |
218 } | |
219 } | |
220 | |
221 if (m_path.empty()) | |
222 return nullptr; | |
223 | |
224 return m_path.c_str(); | |
225 } | |
226 | |
227 public: | |
228 ~EditlineHistory() { | |
229 Save(); | |
230 | |
231 if (m_history) { | |
232 history_wend(m_history); | |
233 m_history = nullptr; | |
234 } | |
235 } | |
236 | |
237 static EditlineHistorySP GetHistory(const std::string &prefix) { | |
238 typedef std::map<std::string, EditlineHistoryWP> WeakHistoryMap; | |
239 static std::recursive_mutex g_mutex; | |
240 static WeakHistoryMap g_weak_map; | |
241 std::lock_guard<std::recursive_mutex> guard(g_mutex); | |
242 WeakHistoryMap::const_iterator pos = g_weak_map.find(prefix); | |
243 EditlineHistorySP history_sp; | |
244 if (pos != g_weak_map.end()) { | |
245 history_sp = pos->second.lock(); | |
246 if (history_sp) | |
247 return history_sp; | |
248 g_weak_map.erase(pos); | |
249 } | |
250 history_sp.reset(new EditlineHistory(prefix, 800, true)); | |
251 g_weak_map[prefix] = history_sp; | |
252 return history_sp; | |
253 } | |
254 | |
255 bool IsValid() const { return m_history != nullptr; } | |
256 | |
257 HistoryW *GetHistoryPtr() { return m_history; } | |
258 | |
259 void Enter(const EditLineCharType *line_cstr) { | |
260 if (m_history) | |
261 history_w(m_history, &m_event, H_ENTER, line_cstr); | |
262 } | |
263 | |
264 bool Load() { | |
265 if (m_history) { | |
266 const char *path = GetHistoryFilePath(); | |
267 if (path) { | |
268 history_w(m_history, &m_event, H_LOAD, path); | |
269 return true; | |
270 } | |
271 } | |
272 return false; | |
273 } | |
274 | |
275 bool Save() { | |
276 if (m_history) { | |
277 const char *path = GetHistoryFilePath(); | |
278 if (path) { | |
279 history_w(m_history, &m_event, H_SAVE, path); | |
280 return true; | |
281 } | |
282 } | |
283 return false; | |
284 } | |
285 | |
286 protected: | |
287 HistoryW *m_history; // The history object | |
288 HistEventW m_event; // The history event needed to contain all history events | |
289 std::string m_prefix; // The prefix name (usually the editline program name) | |
290 // to use when loading/saving history | |
291 std::string m_path; // Path to the history file | |
292 }; | |
293 } | |
294 } | |
295 | |
296 // Editline private methods | |
297 | |
298 void Editline::SetBaseLineNumber(int line_number) { | |
299 std::stringstream line_number_stream; | |
300 line_number_stream << line_number; | |
301 m_base_line_number = line_number; | |
302 m_line_number_digits = | |
303 std::max(3, (int)line_number_stream.str().length() + 1); | |
304 } | |
305 | |
306 std::string Editline::PromptForIndex(int line_index) { | |
307 bool use_line_numbers = m_multiline_enabled && m_base_line_number > 0; | |
308 std::string prompt = m_set_prompt; | |
309 if (use_line_numbers && prompt.length() == 0) { | |
310 prompt = ": "; | |
311 } | |
312 std::string continuation_prompt = prompt; | |
313 if (m_set_continuation_prompt.length() > 0) { | |
314 continuation_prompt = m_set_continuation_prompt; | |
315 | |
316 // Ensure that both prompts are the same length through space padding | |
317 while (continuation_prompt.length() < prompt.length()) { | |
318 continuation_prompt += ' '; | |
319 } | |
320 while (prompt.length() < continuation_prompt.length()) { | |
321 prompt += ' '; | |
322 } | |
323 } | |
324 | |
325 if (use_line_numbers) { | |
326 StreamString prompt_stream; | |
327 prompt_stream.Printf( | |
328 "%*d%s", m_line_number_digits, m_base_line_number + line_index, | |
329 (line_index == 0) ? prompt.c_str() : continuation_prompt.c_str()); | |
330 return std::string(std::move(prompt_stream.GetString())); | |
331 } | |
332 return (line_index == 0) ? prompt : continuation_prompt; | |
333 } | |
334 | |
335 void Editline::SetCurrentLine(int line_index) { | |
336 m_current_line_index = line_index; | |
337 m_current_prompt = PromptForIndex(line_index); | |
338 } | |
339 | |
340 int Editline::GetPromptWidth() { return (int)PromptForIndex(0).length(); } | |
341 | |
342 bool Editline::IsEmacs() { | |
343 const char *editor; | |
344 el_get(m_editline, EL_EDITOR, &editor); | |
345 return editor[0] == 'e'; | |
346 } | |
347 | |
348 bool Editline::IsOnlySpaces() { | |
349 const LineInfoW *info = el_wline(m_editline); | |
350 for (const EditLineCharType *character = info->buffer; | |
351 character < info->lastchar; character++) { | |
352 if (*character != ' ') | |
353 return false; | |
354 } | |
355 return true; | |
356 } | |
357 | |
358 int Editline::GetLineIndexForLocation(CursorLocation location, int cursor_row) { | |
359 int line = 0; | |
360 if (location == CursorLocation::EditingPrompt || | |
361 location == CursorLocation::BlockEnd || | |
362 location == CursorLocation::EditingCursor) { | |
363 for (unsigned index = 0; index < m_current_line_index; index++) { | |
364 line += CountRowsForLine(m_input_lines[index]); | |
365 } | |
366 if (location == CursorLocation::EditingCursor) { | |
367 line += cursor_row; | |
368 } else if (location == CursorLocation::BlockEnd) { | |
369 for (unsigned index = m_current_line_index; index < m_input_lines.size(); | |
370 index++) { | |
371 line += CountRowsForLine(m_input_lines[index]); | |
372 } | |
373 --line; | |
374 } | |
375 } | |
376 return line; | |
377 } | |
378 | |
379 void Editline::MoveCursor(CursorLocation from, CursorLocation to) { | |
380 const LineInfoW *info = el_wline(m_editline); | |
381 int editline_cursor_position = | |
382 (int)((info->cursor - info->buffer) + GetPromptWidth()); | |
383 int editline_cursor_row = editline_cursor_position / m_terminal_width; | |
384 | |
385 // Determine relative starting and ending lines | |
386 int fromLine = GetLineIndexForLocation(from, editline_cursor_row); | |
387 int toLine = GetLineIndexForLocation(to, editline_cursor_row); | |
388 if (toLine != fromLine) { | |
389 fprintf(m_output_file, | |
390 (toLine > fromLine) ? ANSI_DOWN_N_ROWS : ANSI_UP_N_ROWS, | |
391 std::abs(toLine - fromLine)); | |
392 } | |
393 | |
394 // Determine target column | |
395 int toColumn = 1; | |
396 if (to == CursorLocation::EditingCursor) { | |
397 toColumn = | |
398 editline_cursor_position - (editline_cursor_row * m_terminal_width) + 1; | |
399 } else if (to == CursorLocation::BlockEnd && !m_input_lines.empty()) { | |
400 toColumn = | |
401 ((m_input_lines[m_input_lines.size() - 1].length() + GetPromptWidth()) % | |
402 80) + | |
403 1; | |
404 } | |
405 fprintf(m_output_file, ANSI_SET_COLUMN_N, toColumn); | |
406 } | |
407 | |
408 void Editline::DisplayInput(int firstIndex) { | |
409 fprintf(m_output_file, ANSI_SET_COLUMN_N ANSI_CLEAR_BELOW, 1); | |
410 int line_count = (int)m_input_lines.size(); | |
411 const char *faint = m_color_prompts ? ANSI_FAINT : ""; | |
412 const char *unfaint = m_color_prompts ? ANSI_UNFAINT : ""; | |
413 | |
414 for (int index = firstIndex; index < line_count; index++) { | |
415 fprintf(m_output_file, "%s" | |
416 "%s" | |
417 "%s" EditLineStringFormatSpec " ", | |
418 faint, PromptForIndex(index).c_str(), unfaint, | |
419 m_input_lines[index].c_str()); | |
420 if (index < line_count - 1) | |
421 fprintf(m_output_file, "\n"); | |
422 } | |
423 } | |
424 | |
425 int Editline::CountRowsForLine(const EditLineStringType &content) { | |
426 auto prompt = | |
427 PromptForIndex(0); // Prompt width is constant during an edit session | |
428 int line_length = (int)(content.length() + prompt.length()); | |
429 return (line_length / m_terminal_width) + 1; | |
430 } | |
431 | |
432 void Editline::SaveEditedLine() { | |
433 const LineInfoW *info = el_wline(m_editline); | |
434 m_input_lines[m_current_line_index] = | |
435 EditLineStringType(info->buffer, info->lastchar - info->buffer); | |
436 } | |
437 | |
438 StringList Editline::GetInputAsStringList(int line_count) { | |
439 StringList lines; | |
440 for (EditLineStringType line : m_input_lines) { | |
441 if (line_count == 0) | |
442 break; | |
443 #if LLDB_EDITLINE_USE_WCHAR | |
444 lines.AppendString(m_utf8conv.to_bytes(line)); | |
445 #else | |
446 lines.AppendString(line); | |
447 #endif | |
448 --line_count; | |
449 } | |
450 return lines; | |
451 } | |
452 | |
453 unsigned char Editline::RecallHistory(HistoryOperation op) { | |
454 assert(op == HistoryOperation::Older || op == HistoryOperation::Newer); | |
455 if (!m_history_sp || !m_history_sp->IsValid()) | |
456 return CC_ERROR; | |
457 | |
458 HistoryW *pHistory = m_history_sp->GetHistoryPtr(); | |
459 HistEventW history_event; | |
460 std::vector<EditLineStringType> new_input_lines; | |
461 | |
462 // Treat moving from the "live" entry differently | |
463 if (!m_in_history) { | |
464 switch (op) { | |
465 case HistoryOperation::Newer: | |
466 return CC_ERROR; // Can't go newer than the "live" entry | |
467 case HistoryOperation::Older: { | |
468 if (history_w(pHistory, &history_event, | |
469 GetOperation(HistoryOperation::Newest)) == -1) | |
470 return CC_ERROR; | |
471 // Save any edits to the "live" entry in case we return by moving forward | |
472 // in history (it would be more bash-like to save over any current entry, | |
473 // but libedit doesn't offer the ability to add entries anywhere except | |
474 // the end.) | |
475 SaveEditedLine(); | |
476 m_live_history_lines = m_input_lines; | |
477 m_in_history = true; | |
478 } break; | |
479 default: | |
480 llvm_unreachable("unsupported history direction"); | |
481 } | |
482 } else { | |
483 if (history_w(pHistory, &history_event, GetOperation(op)) == -1) { | |
484 switch (op) { | |
485 case HistoryOperation::Older: | |
486 // Can't move earlier than the earliest entry. | |
487 return CC_ERROR; | |
488 case HistoryOperation::Newer: | |
489 // Moving to newer-than-the-newest entry yields the "live" entry. | |
490 new_input_lines = m_live_history_lines; | |
491 m_in_history = false; | |
492 break; | |
493 default: | |
494 llvm_unreachable("unsupported history direction"); | |
495 } | |
496 } | |
497 } | |
498 | |
499 // If we're pulling the lines from history, split them apart | |
500 if (m_in_history) | |
501 new_input_lines = SplitLines(history_event.str); | |
502 | |
503 // Erase the current edit session and replace it with a new one | |
504 MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart); | |
505 m_input_lines = new_input_lines; | |
506 DisplayInput(); | |
507 | |
508 // Prepare to edit the last line when moving to previous entry, or the first | |
509 // line when moving to next entry | |
510 switch (op) { | |
511 case HistoryOperation::Older: | |
512 m_current_line_index = (int)m_input_lines.size() - 1; | |
513 break; | |
514 case HistoryOperation::Newer: | |
515 m_current_line_index = 0; | |
516 break; | |
517 default: | |
518 llvm_unreachable("unsupported history direction"); | |
519 } | |
520 SetCurrentLine(m_current_line_index); | |
521 MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt); | |
522 return CC_NEWLINE; | |
523 } | |
524 | |
525 int Editline::GetCharacter(EditLineGetCharType *c) { | |
526 const LineInfoW *info = el_wline(m_editline); | |
527 | |
528 // Paint a faint version of the desired prompt over the version libedit draws | |
529 // (will only be requested if colors are supported) | |
530 if (m_needs_prompt_repaint) { | |
531 MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt); | |
532 fprintf(m_output_file, "%s" | |
533 "%s" | |
534 "%s", | |
535 ANSI_FAINT, Prompt(), ANSI_UNFAINT); | |
536 MoveCursor(CursorLocation::EditingPrompt, CursorLocation::EditingCursor); | |
537 m_needs_prompt_repaint = false; | |
538 } | |
539 | |
540 if (m_multiline_enabled) { | |
541 // Detect when the number of rows used for this input line changes due to | |
542 // an edit | |
543 int lineLength = (int)((info->lastchar - info->buffer) + GetPromptWidth()); | |
544 int new_line_rows = (lineLength / m_terminal_width) + 1; | |
545 if (m_current_line_rows != -1 && new_line_rows != m_current_line_rows) { | |
546 // Respond by repainting the current state from this line on | |
547 MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt); | |
548 SaveEditedLine(); | |
549 DisplayInput(m_current_line_index); | |
550 MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor); | |
551 } | |
552 m_current_line_rows = new_line_rows; | |
553 } | |
554 | |
555 // Read an actual character | |
556 while (true) { | |
557 lldb::ConnectionStatus status = lldb::eConnectionStatusSuccess; | |
558 char ch = 0; | |
559 | |
560 // This mutex is locked by our caller (GetLine). Unlock it while we read a | |
561 // character (blocking operation), so we do not hold the mutex | |
562 // indefinitely. This gives a chance for someone to interrupt us. After | |
563 // Read returns, immediately lock the mutex again and check if we were | |
564 // interrupted. | |
565 m_output_mutex.unlock(); | |
566 int read_count = | |
567 m_input_connection.Read(&ch, 1, llvm::None, status, nullptr); | |
568 m_output_mutex.lock(); | |
569 if (m_editor_status == EditorStatus::Interrupted) { | |
570 while (read_count > 0 && status == lldb::eConnectionStatusSuccess) | |
571 read_count = | |
572 m_input_connection.Read(&ch, 1, llvm::None, status, nullptr); | |
573 lldbassert(status == lldb::eConnectionStatusInterrupted); | |
574 return 0; | |
575 } | |
576 | |
577 if (read_count) { | |
578 if (CompleteCharacter(ch, *c)) | |
579 return 1; | |
580 } else { | |
581 switch (status) { | |
582 case lldb::eConnectionStatusSuccess: // Success | |
583 break; | |
584 | |
585 case lldb::eConnectionStatusInterrupted: | |
586 llvm_unreachable("Interrupts should have been handled above."); | |
587 | |
588 case lldb::eConnectionStatusError: // Check GetError() for details | |
589 case lldb::eConnectionStatusTimedOut: // Request timed out | |
590 case lldb::eConnectionStatusEndOfFile: // End-of-file encountered | |
591 case lldb::eConnectionStatusNoConnection: // No connection | |
592 case lldb::eConnectionStatusLostConnection: // Lost connection while | |
593 // connected to a valid | |
594 // connection | |
595 m_editor_status = EditorStatus::EndOfInput; | |
596 return 0; | |
597 } | |
598 } | |
599 } | |
600 } | |
601 | |
602 const char *Editline::Prompt() { | |
603 if (m_color_prompts) | |
604 m_needs_prompt_repaint = true; | |
605 return m_current_prompt.c_str(); | |
606 } | |
607 | |
608 unsigned char Editline::BreakLineCommand(int ch) { | |
609 // Preserve any content beyond the cursor, truncate and save the current line | |
610 const LineInfoW *info = el_wline(m_editline); | |
611 auto current_line = | |
612 EditLineStringType(info->buffer, info->cursor - info->buffer); | |
613 auto new_line_fragment = | |
614 EditLineStringType(info->cursor, info->lastchar - info->cursor); | |
615 m_input_lines[m_current_line_index] = current_line; | |
616 | |
617 // Ignore whitespace-only extra fragments when breaking a line | |
618 if (::IsOnlySpaces(new_line_fragment)) | |
619 new_line_fragment = EditLineConstString(""); | |
620 | |
621 // Establish the new cursor position at the start of a line when inserting a | |
622 // line break | |
623 m_revert_cursor_index = 0; | |
624 | |
625 // Don't perform automatic formatting when pasting | |
626 if (!IsInputPending(m_input_file)) { | |
627 // Apply smart indentation | |
628 if (m_fix_indentation_callback) { | |
629 StringList lines = GetInputAsStringList(m_current_line_index + 1); | |
630 #if LLDB_EDITLINE_USE_WCHAR | |
631 lines.AppendString(m_utf8conv.to_bytes(new_line_fragment)); | |
632 #else | |
633 lines.AppendString(new_line_fragment); | |
634 #endif | |
635 | |
636 int indent_correction = m_fix_indentation_callback( | |
637 this, lines, 0, m_fix_indentation_callback_baton); | |
638 new_line_fragment = FixIndentation(new_line_fragment, indent_correction); | |
639 m_revert_cursor_index = GetIndentation(new_line_fragment); | |
640 } | |
641 } | |
642 | |
643 // Insert the new line and repaint everything from the split line on down | |
644 m_input_lines.insert(m_input_lines.begin() + m_current_line_index + 1, | |
645 new_line_fragment); | |
646 MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt); | |
647 DisplayInput(m_current_line_index); | |
648 | |
649 // Reposition the cursor to the right line and prepare to edit the new line | |
650 SetCurrentLine(m_current_line_index + 1); | |
651 MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt); | |
652 return CC_NEWLINE; | |
653 } | |
654 | |
655 unsigned char Editline::EndOrAddLineCommand(int ch) { | |
656 // Don't perform end of input detection when pasting, always treat this as a | |
657 // line break | |
658 if (IsInputPending(m_input_file)) { | |
659 return BreakLineCommand(ch); | |
660 } | |
661 | |
662 // Save any edits to this line | |
663 SaveEditedLine(); | |
664 | |
665 // If this is the end of the last line, consider whether to add a line | |
666 // instead | |
667 const LineInfoW *info = el_wline(m_editline); | |
668 if (m_current_line_index == m_input_lines.size() - 1 && | |
669 info->cursor == info->lastchar) { | |
670 if (m_is_input_complete_callback) { | |
671 auto lines = GetInputAsStringList(); | |
672 if (!m_is_input_complete_callback(this, lines, | |
673 m_is_input_complete_callback_baton)) { | |
674 return BreakLineCommand(ch); | |
675 } | |
676 | |
677 // The completion test is allowed to change the input lines when complete | |
678 m_input_lines.clear(); | |
679 for (unsigned index = 0; index < lines.GetSize(); index++) { | |
680 #if LLDB_EDITLINE_USE_WCHAR | |
681 m_input_lines.insert(m_input_lines.end(), | |
682 m_utf8conv.from_bytes(lines[index])); | |
683 #else | |
684 m_input_lines.insert(m_input_lines.end(), lines[index]); | |
685 #endif | |
686 } | |
687 } | |
688 } | |
689 MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockEnd); | |
690 fprintf(m_output_file, "\n"); | |
691 m_editor_status = EditorStatus::Complete; | |
692 return CC_NEWLINE; | |
693 } | |
694 | |
695 unsigned char Editline::DeleteNextCharCommand(int ch) { | |
696 LineInfoW *info = const_cast<LineInfoW *>(el_wline(m_editline)); | |
697 | |
698 // Just delete the next character normally if possible | |
699 if (info->cursor < info->lastchar) { | |
700 info->cursor++; | |
701 el_deletestr(m_editline, 1); | |
702 return CC_REFRESH; | |
703 } | |
704 | |
705 // Fail when at the end of the last line, except when ^D is pressed on the | |
706 // line is empty, in which case it is treated as EOF | |
707 if (m_current_line_index == m_input_lines.size() - 1) { | |
708 if (ch == 4 && info->buffer == info->lastchar) { | |
709 fprintf(m_output_file, "^D\n"); | |
710 m_editor_status = EditorStatus::EndOfInput; | |
711 return CC_EOF; | |
712 } | |
713 return CC_ERROR; | |
714 } | |
715 | |
716 // Prepare to combine this line with the one below | |
717 MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt); | |
718 | |
719 // Insert the next line of text at the cursor and restore the cursor position | |
720 const EditLineCharType *cursor = info->cursor; | |
721 el_winsertstr(m_editline, m_input_lines[m_current_line_index + 1].c_str()); | |
722 info->cursor = cursor; | |
723 SaveEditedLine(); | |
724 | |
725 // Delete the extra line | |
726 m_input_lines.erase(m_input_lines.begin() + m_current_line_index + 1); | |
727 | |
728 // Clear and repaint from this line on down | |
729 DisplayInput(m_current_line_index); | |
730 MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor); | |
731 return CC_REFRESH; | |
732 } | |
733 | |
734 unsigned char Editline::DeletePreviousCharCommand(int ch) { | |
735 LineInfoW *info = const_cast<LineInfoW *>(el_wline(m_editline)); | |
736 | |
737 // Just delete the previous character normally when not at the start of a | |
738 // line | |
739 if (info->cursor > info->buffer) { | |
740 el_deletestr(m_editline, 1); | |
741 return CC_REFRESH; | |
742 } | |
743 | |
744 // No prior line and no prior character? Let the user know | |
745 if (m_current_line_index == 0) | |
746 return CC_ERROR; | |
747 | |
748 // No prior character, but prior line? Combine with the line above | |
749 SaveEditedLine(); | |
750 SetCurrentLine(m_current_line_index - 1); | |
751 auto priorLine = m_input_lines[m_current_line_index]; | |
752 m_input_lines.erase(m_input_lines.begin() + m_current_line_index); | |
753 m_input_lines[m_current_line_index] = | |
754 priorLine + m_input_lines[m_current_line_index]; | |
755 | |
756 // Repaint from the new line down | |
757 fprintf(m_output_file, ANSI_UP_N_ROWS ANSI_SET_COLUMN_N, | |
758 CountRowsForLine(priorLine), 1); | |
759 DisplayInput(m_current_line_index); | |
760 | |
761 // Put the cursor back where libedit expects it to be before returning to | |
762 // editing by telling libedit about the newly inserted text | |
763 MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt); | |
764 el_winsertstr(m_editline, priorLine.c_str()); | |
765 return CC_REDISPLAY; | |
766 } | |
767 | |
768 unsigned char Editline::PreviousLineCommand(int ch) { | |
769 SaveEditedLine(); | |
770 | |
771 if (m_current_line_index == 0) { | |
772 return RecallHistory(HistoryOperation::Older); | |
773 } | |
774 | |
775 // Start from a known location | |
776 MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt); | |
777 | |
778 // Treat moving up from a blank last line as a deletion of that line | |
779 if (m_current_line_index == m_input_lines.size() - 1 && IsOnlySpaces()) { | |
780 m_input_lines.erase(m_input_lines.begin() + m_current_line_index); | |
781 fprintf(m_output_file, ANSI_CLEAR_BELOW); | |
782 } | |
783 | |
784 SetCurrentLine(m_current_line_index - 1); | |
785 fprintf(m_output_file, ANSI_UP_N_ROWS ANSI_SET_COLUMN_N, | |
786 CountRowsForLine(m_input_lines[m_current_line_index]), 1); | |
787 return CC_NEWLINE; | |
788 } | |
789 | |
790 unsigned char Editline::NextLineCommand(int ch) { | |
791 SaveEditedLine(); | |
792 | |
793 // Handle attempts to move down from the last line | |
794 if (m_current_line_index == m_input_lines.size() - 1) { | |
795 // Don't add an extra line if the existing last line is blank, move through | |
796 // history instead | |
797 if (IsOnlySpaces()) { | |
798 return RecallHistory(HistoryOperation::Newer); | |
799 } | |
800 | |
801 // Determine indentation for the new line | |
802 int indentation = 0; | |
803 if (m_fix_indentation_callback) { | |
804 StringList lines = GetInputAsStringList(); | |
805 lines.AppendString(""); | |
806 indentation = m_fix_indentation_callback( | |
807 this, lines, 0, m_fix_indentation_callback_baton); | |
808 } | |
809 m_input_lines.insert( | |
810 m_input_lines.end(), | |
811 EditLineStringType(indentation, EditLineCharType(' '))); | |
812 } | |
813 | |
814 // Move down past the current line using newlines to force scrolling if | |
815 // needed | |
816 SetCurrentLine(m_current_line_index + 1); | |
817 const LineInfoW *info = el_wline(m_editline); | |
818 int cursor_position = (int)((info->cursor - info->buffer) + GetPromptWidth()); | |
819 int cursor_row = cursor_position / m_terminal_width; | |
820 for (int line_count = 0; line_count < m_current_line_rows - cursor_row; | |
821 line_count++) { | |
822 fprintf(m_output_file, "\n"); | |
823 } | |
824 return CC_NEWLINE; | |
825 } | |
826 | |
827 unsigned char Editline::PreviousHistoryCommand(int ch) { | |
828 SaveEditedLine(); | |
829 | |
830 return RecallHistory(HistoryOperation::Older); | |
831 } | |
832 | |
833 unsigned char Editline::NextHistoryCommand(int ch) { | |
834 SaveEditedLine(); | |
835 | |
836 return RecallHistory(HistoryOperation::Newer); | |
837 } | |
838 | |
839 unsigned char Editline::FixIndentationCommand(int ch) { | |
840 if (!m_fix_indentation_callback) | |
841 return CC_NORM; | |
842 | |
843 // Insert the character typed before proceeding | |
844 EditLineCharType inserted[] = {(EditLineCharType)ch, 0}; | |
845 el_winsertstr(m_editline, inserted); | |
846 LineInfoW *info = const_cast<LineInfoW *>(el_wline(m_editline)); | |
847 int cursor_position = info->cursor - info->buffer; | |
848 | |
849 // Save the edits and determine the correct indentation level | |
850 SaveEditedLine(); | |
851 StringList lines = GetInputAsStringList(m_current_line_index + 1); | |
852 int indent_correction = m_fix_indentation_callback( | |
853 this, lines, cursor_position, m_fix_indentation_callback_baton); | |
854 | |
855 // If it is already correct no special work is needed | |
856 if (indent_correction == 0) | |
857 return CC_REFRESH; | |
858 | |
859 // Change the indentation level of the line | |
860 std::string currentLine = lines.GetStringAtIndex(m_current_line_index); | |
861 if (indent_correction > 0) { | |
862 currentLine = currentLine.insert(0, indent_correction, ' '); | |
863 } else { | |
864 currentLine = currentLine.erase(0, -indent_correction); | |
865 } | |
866 #if LLDB_EDITLINE_USE_WCHAR | |
867 m_input_lines[m_current_line_index] = m_utf8conv.from_bytes(currentLine); | |
868 #else | |
869 m_input_lines[m_current_line_index] = currentLine; | |
870 #endif | |
871 | |
872 // Update the display to reflect the change | |
873 MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt); | |
874 DisplayInput(m_current_line_index); | |
875 | |
876 // Reposition the cursor back on the original line and prepare to restart | |
877 // editing with a new cursor position | |
878 SetCurrentLine(m_current_line_index); | |
879 MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt); | |
880 m_revert_cursor_index = cursor_position + indent_correction; | |
881 return CC_NEWLINE; | |
882 } | |
883 | |
884 unsigned char Editline::RevertLineCommand(int ch) { | |
885 el_winsertstr(m_editline, m_input_lines[m_current_line_index].c_str()); | |
886 if (m_revert_cursor_index >= 0) { | |
887 LineInfoW *info = const_cast<LineInfoW *>(el_wline(m_editline)); | |
888 info->cursor = info->buffer + m_revert_cursor_index; | |
889 if (info->cursor > info->lastchar) { | |
890 info->cursor = info->lastchar; | |
891 } | |
892 m_revert_cursor_index = -1; | |
893 } | |
894 return CC_REFRESH; | |
895 } | |
896 | |
897 unsigned char Editline::BufferStartCommand(int ch) { | |
898 SaveEditedLine(); | |
899 MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart); | |
900 SetCurrentLine(0); | |
901 m_revert_cursor_index = 0; | |
902 return CC_NEWLINE; | |
903 } | |
904 | |
905 unsigned char Editline::BufferEndCommand(int ch) { | |
906 SaveEditedLine(); | |
907 MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockEnd); | |
908 SetCurrentLine((int)m_input_lines.size() - 1); | |
909 MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt); | |
910 return CC_NEWLINE; | |
911 } | |
912 | |
913 /// Prints completions and their descriptions to the given file. Only the | |
914 /// completions in the interval [start, end) are printed. | |
915 static void | |
916 PrintCompletion(FILE *output_file, | |
917 llvm::ArrayRef<CompletionResult::Completion> results, | |
918 size_t max_len) { | |
919 for (const CompletionResult::Completion &c : results) { | |
920 fprintf(output_file, "\t%-*s", (int)max_len, c.GetCompletion().c_str()); | |
921 if (!c.GetDescription().empty()) | |
922 fprintf(output_file, " -- %s", c.GetDescription().c_str()); | |
923 fprintf(output_file, "\n"); | |
924 } | |
925 } | |
926 | |
927 static void | |
928 DisplayCompletions(::EditLine *editline, FILE *output_file, | |
929 llvm::ArrayRef<CompletionResult::Completion> results) { | |
930 assert(!results.empty()); | |
931 | |
932 fprintf(output_file, "\n" ANSI_CLEAR_BELOW "Available completions:\n"); | |
933 const size_t page_size = 40; | |
934 bool all = false; | |
935 | |
936 auto longest = | |
937 std::max_element(results.begin(), results.end(), [](auto &c1, auto &c2) { | |
938 return c1.GetCompletion().size() < c2.GetCompletion().size(); | |
939 }); | |
940 | |
941 const size_t max_len = longest->GetCompletion().size(); | |
942 | |
943 if (results.size() < page_size) { | |
944 PrintCompletion(output_file, results, max_len); | |
945 return; | |
946 } | |
947 | |
948 size_t cur_pos = 0; | |
949 while (cur_pos < results.size()) { | |
950 size_t remaining = results.size() - cur_pos; | |
951 size_t next_size = all ? remaining : std::min(page_size, remaining); | |
952 | |
953 PrintCompletion(output_file, results.slice(cur_pos, next_size), max_len); | |
954 | |
955 cur_pos += next_size; | |
956 | |
957 if (cur_pos >= results.size()) | |
958 break; | |
959 | |
960 fprintf(output_file, "More (Y/n/a): "); | |
961 char reply = 'n'; | |
962 int got_char = el_getc(editline, &reply); | |
963 fprintf(output_file, "\n"); | |
964 if (got_char == -1 || reply == 'n') | |
965 break; | |
966 if (reply == 'a') | |
967 all = true; | |
968 } | |
969 } | |
970 | |
971 unsigned char Editline::TabCommand(int ch) { | |
972 if (m_completion_callback == nullptr) | |
973 return CC_ERROR; | |
974 | |
975 const LineInfo *line_info = el_line(m_editline); | |
976 | |
977 llvm::StringRef line(line_info->buffer, | |
978 line_info->lastchar - line_info->buffer); | |
979 unsigned cursor_index = line_info->cursor - line_info->buffer; | |
980 CompletionResult result; | |
981 CompletionRequest request(line, cursor_index, result); | |
982 | |
983 m_completion_callback(request, m_completion_callback_baton); | |
984 | |
985 llvm::ArrayRef<CompletionResult::Completion> results = result.GetResults(); | |
986 | |
987 StringList completions; | |
988 result.GetMatches(completions); | |
989 | |
990 if (results.size() == 0) | |
991 return CC_ERROR; | |
992 | |
993 if (results.size() == 1) { | |
994 CompletionResult::Completion completion = results.front(); | |
995 switch (completion.GetMode()) { | |
996 case CompletionMode::Normal: { | |
997 std::string to_add = completion.GetCompletion(); | |
998 to_add = to_add.substr(request.GetCursorArgumentPrefix().size()); | |
999 if (request.GetParsedArg().IsQuoted()) | |
1000 to_add.push_back(request.GetParsedArg().GetQuoteChar()); | |
1001 to_add.push_back(' '); | |
1002 el_insertstr(m_editline, to_add.c_str()); | |
1003 break; | |
1004 } | |
1005 case CompletionMode::Partial: { | |
1006 std::string to_add = completion.GetCompletion(); | |
1007 to_add = to_add.substr(request.GetCursorArgumentPrefix().size()); | |
1008 el_insertstr(m_editline, to_add.c_str()); | |
1009 break; | |
1010 } | |
1011 case CompletionMode::RewriteLine: { | |
1012 el_deletestr(m_editline, line_info->cursor - line_info->buffer); | |
1013 el_insertstr(m_editline, completion.GetCompletion().c_str()); | |
1014 break; | |
1015 } | |
1016 } | |
1017 return CC_REDISPLAY; | |
1018 } | |
1019 | |
1020 // If we get a longer match display that first. | |
1021 std::string longest_prefix = completions.LongestCommonPrefix(); | |
1022 if (!longest_prefix.empty()) | |
1023 longest_prefix = | |
1024 longest_prefix.substr(request.GetCursorArgumentPrefix().size()); | |
1025 if (!longest_prefix.empty()) { | |
1026 el_insertstr(m_editline, longest_prefix.c_str()); | |
1027 return CC_REDISPLAY; | |
1028 } | |
1029 | |
1030 DisplayCompletions(m_editline, m_output_file, results); | |
1031 | |
1032 DisplayInput(); | |
1033 MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor); | |
1034 return CC_REDISPLAY; | |
1035 } | |
1036 | |
1037 void Editline::ConfigureEditor(bool multiline) { | |
1038 if (m_editline && m_multiline_enabled == multiline) | |
1039 return; | |
1040 m_multiline_enabled = multiline; | |
1041 | |
1042 if (m_editline) { | |
1043 // Disable edit mode to stop the terminal from flushing all input during | |
1044 // the call to el_end() since we expect to have multiple editline instances | |
1045 // in this program. | |
1046 el_set(m_editline, EL_EDITMODE, 0); | |
1047 el_end(m_editline); | |
1048 } | |
1049 | |
1050 m_editline = | |
1051 el_init(m_editor_name.c_str(), m_input_file, m_output_file, m_error_file); | |
1052 TerminalSizeChanged(); | |
1053 | |
1054 if (m_history_sp && m_history_sp->IsValid()) { | |
1055 if (!m_history_sp->Load()) { | |
1056 fputs("Could not load history file\n.", m_output_file); | |
1057 } | |
1058 el_wset(m_editline, EL_HIST, history, m_history_sp->GetHistoryPtr()); | |
1059 } | |
1060 el_set(m_editline, EL_CLIENTDATA, this); | |
1061 el_set(m_editline, EL_SIGNAL, 0); | |
1062 el_set(m_editline, EL_EDITOR, "emacs"); | |
1063 el_set(m_editline, EL_PROMPT, | |
1064 (EditlinePromptCallbackType)([](EditLine *editline) { | |
1065 return Editline::InstanceFor(editline)->Prompt(); | |
1066 })); | |
1067 | |
1068 el_wset(m_editline, EL_GETCFN, (EditlineGetCharCallbackType)([]( | |
1069 EditLine *editline, EditLineGetCharType *c) { | |
1070 return Editline::InstanceFor(editline)->GetCharacter(c); | |
1071 })); | |
1072 | |
1073 // Commands used for multiline support, registered whether or not they're | |
1074 // used | |
1075 el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-break-line"), | |
1076 EditLineConstString("Insert a line break"), | |
1077 (EditlineCommandCallbackType)([](EditLine *editline, int ch) { | |
1078 return Editline::InstanceFor(editline)->BreakLineCommand(ch); | |
1079 })); | |
1080 el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-end-or-add-line"), | |
1081 EditLineConstString("End editing or continue when incomplete"), | |
1082 (EditlineCommandCallbackType)([](EditLine *editline, int ch) { | |
1083 return Editline::InstanceFor(editline)->EndOrAddLineCommand(ch); | |
1084 })); | |
1085 el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-delete-next-char"), | |
1086 EditLineConstString("Delete next character"), | |
1087 (EditlineCommandCallbackType)([](EditLine *editline, int ch) { | |
1088 return Editline::InstanceFor(editline)->DeleteNextCharCommand(ch); | |
1089 })); | |
1090 el_wset( | |
1091 m_editline, EL_ADDFN, EditLineConstString("lldb-delete-previous-char"), | |
1092 EditLineConstString("Delete previous character"), | |
1093 (EditlineCommandCallbackType)([](EditLine *editline, int ch) { | |
1094 return Editline::InstanceFor(editline)->DeletePreviousCharCommand(ch); | |
1095 })); | |
1096 el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-previous-line"), | |
1097 EditLineConstString("Move to previous line"), | |
1098 (EditlineCommandCallbackType)([](EditLine *editline, int ch) { | |
1099 return Editline::InstanceFor(editline)->PreviousLineCommand(ch); | |
1100 })); | |
1101 el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-next-line"), | |
1102 EditLineConstString("Move to next line"), | |
1103 (EditlineCommandCallbackType)([](EditLine *editline, int ch) { | |
1104 return Editline::InstanceFor(editline)->NextLineCommand(ch); | |
1105 })); | |
1106 el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-previous-history"), | |
1107 EditLineConstString("Move to previous history"), | |
1108 (EditlineCommandCallbackType)([](EditLine *editline, int ch) { | |
1109 return Editline::InstanceFor(editline)->PreviousHistoryCommand(ch); | |
1110 })); | |
1111 el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-next-history"), | |
1112 EditLineConstString("Move to next history"), | |
1113 (EditlineCommandCallbackType)([](EditLine *editline, int ch) { | |
1114 return Editline::InstanceFor(editline)->NextHistoryCommand(ch); | |
1115 })); | |
1116 el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-buffer-start"), | |
1117 EditLineConstString("Move to start of buffer"), | |
1118 (EditlineCommandCallbackType)([](EditLine *editline, int ch) { | |
1119 return Editline::InstanceFor(editline)->BufferStartCommand(ch); | |
1120 })); | |
1121 el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-buffer-end"), | |
1122 EditLineConstString("Move to end of buffer"), | |
1123 (EditlineCommandCallbackType)([](EditLine *editline, int ch) { | |
1124 return Editline::InstanceFor(editline)->BufferEndCommand(ch); | |
1125 })); | |
1126 el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-fix-indentation"), | |
1127 EditLineConstString("Fix line indentation"), | |
1128 (EditlineCommandCallbackType)([](EditLine *editline, int ch) { | |
1129 return Editline::InstanceFor(editline)->FixIndentationCommand(ch); | |
1130 })); | |
1131 | |
1132 // Register the complete callback under two names for compatibility with | |
1133 // older clients using custom .editrc files (largely because libedit has a | |
1134 // bad bug where if you have a bind command that tries to bind to a function | |
1135 // name that doesn't exist, it can corrupt the heap and crash your process | |
1136 // later.) | |
1137 EditlineCommandCallbackType complete_callback = [](EditLine *editline, | |
1138 int ch) { | |
1139 return Editline::InstanceFor(editline)->TabCommand(ch); | |
1140 }; | |
1141 el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-complete"), | |
1142 EditLineConstString("Invoke completion"), complete_callback); | |
1143 el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb_complete"), | |
1144 EditLineConstString("Invoke completion"), complete_callback); | |
1145 | |
1146 // General bindings we don't mind being overridden | |
1147 if (!multiline) { | |
1148 el_set(m_editline, EL_BIND, "^r", "em-inc-search-prev", | |
1149 NULL); // Cycle through backwards search, entering string | |
1150 } | |
1151 el_set(m_editline, EL_BIND, "^w", "ed-delete-prev-word", | |
1152 NULL); // Delete previous word, behave like bash in emacs mode | |
1153 el_set(m_editline, EL_BIND, "\t", "lldb-complete", | |
1154 NULL); // Bind TAB to auto complete | |
1155 | |
1156 // Allow ctrl-left-arrow and ctrl-right-arrow for navigation, behave like | |
1157 // bash in emacs mode. | |
1158 el_set(m_editline, EL_BIND, ESCAPE "[1;5C", "em-next-word", NULL); | |
1159 el_set(m_editline, EL_BIND, ESCAPE "[1;5D", "ed-prev-word", NULL); | |
1160 el_set(m_editline, EL_BIND, ESCAPE "[5C", "em-next-word", NULL); | |
1161 el_set(m_editline, EL_BIND, ESCAPE "[5D", "ed-prev-word", NULL); | |
1162 el_set(m_editline, EL_BIND, ESCAPE ESCAPE "[C", "em-next-word", NULL); | |
1163 el_set(m_editline, EL_BIND, ESCAPE ESCAPE "[D", "ed-prev-word", NULL); | |
1164 | |
1165 // Allow user-specific customization prior to registering bindings we | |
1166 // absolutely require | |
1167 el_source(m_editline, nullptr); | |
1168 | |
1169 // Register an internal binding that external developers shouldn't use | |
1170 el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-revert-line"), | |
1171 EditLineConstString("Revert line to saved state"), | |
1172 (EditlineCommandCallbackType)([](EditLine *editline, int ch) { | |
1173 return Editline::InstanceFor(editline)->RevertLineCommand(ch); | |
1174 })); | |
1175 | |
1176 // Register keys that perform auto-indent correction | |
1177 if (m_fix_indentation_callback && m_fix_indentation_callback_chars) { | |
1178 char bind_key[2] = {0, 0}; | |
1179 const char *indent_chars = m_fix_indentation_callback_chars; | |
1180 while (*indent_chars) { | |
1181 bind_key[0] = *indent_chars; | |
1182 el_set(m_editline, EL_BIND, bind_key, "lldb-fix-indentation", NULL); | |
1183 ++indent_chars; | |
1184 } | |
1185 } | |
1186 | |
1187 // Multi-line editor bindings | |
1188 if (multiline) { | |
1189 el_set(m_editline, EL_BIND, "\n", "lldb-end-or-add-line", NULL); | |
1190 el_set(m_editline, EL_BIND, "\r", "lldb-end-or-add-line", NULL); | |
1191 el_set(m_editline, EL_BIND, ESCAPE "\n", "lldb-break-line", NULL); | |
1192 el_set(m_editline, EL_BIND, ESCAPE "\r", "lldb-break-line", NULL); | |
1193 el_set(m_editline, EL_BIND, "^p", "lldb-previous-line", NULL); | |
1194 el_set(m_editline, EL_BIND, "^n", "lldb-next-line", NULL); | |
1195 el_set(m_editline, EL_BIND, "^?", "lldb-delete-previous-char", NULL); | |
1196 el_set(m_editline, EL_BIND, "^d", "lldb-delete-next-char", NULL); | |
1197 el_set(m_editline, EL_BIND, ESCAPE "[3~", "lldb-delete-next-char", NULL); | |
1198 el_set(m_editline, EL_BIND, ESCAPE "[\\^", "lldb-revert-line", NULL); | |
1199 | |
1200 // Editor-specific bindings | |
1201 if (IsEmacs()) { | |
1202 el_set(m_editline, EL_BIND, ESCAPE "<", "lldb-buffer-start", NULL); | |
1203 el_set(m_editline, EL_BIND, ESCAPE ">", "lldb-buffer-end", NULL); | |
1204 el_set(m_editline, EL_BIND, ESCAPE "[A", "lldb-previous-line", NULL); | |
1205 el_set(m_editline, EL_BIND, ESCAPE "[B", "lldb-next-line", NULL); | |
1206 el_set(m_editline, EL_BIND, ESCAPE ESCAPE "[A", "lldb-previous-history", | |
1207 NULL); | |
1208 el_set(m_editline, EL_BIND, ESCAPE ESCAPE "[B", "lldb-next-history", | |
1209 NULL); | |
1210 el_set(m_editline, EL_BIND, ESCAPE "[1;3A", "lldb-previous-history", | |
1211 NULL); | |
1212 el_set(m_editline, EL_BIND, ESCAPE "[1;3B", "lldb-next-history", NULL); | |
1213 } else { | |
1214 el_set(m_editline, EL_BIND, "^H", "lldb-delete-previous-char", NULL); | |
1215 | |
1216 el_set(m_editline, EL_BIND, "-a", ESCAPE "[A", "lldb-previous-line", | |
1217 NULL); | |
1218 el_set(m_editline, EL_BIND, "-a", ESCAPE "[B", "lldb-next-line", NULL); | |
1219 el_set(m_editline, EL_BIND, "-a", "x", "lldb-delete-next-char", NULL); | |
1220 el_set(m_editline, EL_BIND, "-a", "^H", "lldb-delete-previous-char", | |
1221 NULL); | |
1222 el_set(m_editline, EL_BIND, "-a", "^?", "lldb-delete-previous-char", | |
1223 NULL); | |
1224 | |
1225 // Escape is absorbed exiting edit mode, so re-register important | |
1226 // sequences without the prefix | |
1227 el_set(m_editline, EL_BIND, "-a", "[A", "lldb-previous-line", NULL); | |
1228 el_set(m_editline, EL_BIND, "-a", "[B", "lldb-next-line", NULL); | |
1229 el_set(m_editline, EL_BIND, "-a", "[\\^", "lldb-revert-line", NULL); | |
1230 } | |
1231 } | |
1232 } | |
1233 | |
1234 // Editline public methods | |
1235 | |
1236 Editline *Editline::InstanceFor(EditLine *editline) { | |
1237 Editline *editor; | |
1238 el_get(editline, EL_CLIENTDATA, &editor); | |
1239 return editor; | |
1240 } | |
1241 | |
1242 Editline::Editline(const char *editline_name, FILE *input_file, | |
1243 FILE *output_file, FILE *error_file, bool color_prompts) | |
1244 : m_editor_status(EditorStatus::Complete), m_color_prompts(color_prompts), | |
1245 m_input_file(input_file), m_output_file(output_file), | |
1246 m_error_file(error_file), m_input_connection(fileno(input_file), false) { | |
1247 // Get a shared history instance | |
1248 m_editor_name = (editline_name == nullptr) ? "lldb-tmp" : editline_name; | |
1249 m_history_sp = EditlineHistory::GetHistory(m_editor_name); | |
1250 | |
1251 #ifdef USE_SETUPTERM_WORKAROUND | |
1252 if (m_output_file) { | |
1253 const int term_fd = fileno(m_output_file); | |
1254 if (term_fd != -1) { | |
1255 static std::mutex *g_init_terminal_fds_mutex_ptr = nullptr; | |
1256 static std::set<int> *g_init_terminal_fds_ptr = nullptr; | |
1257 static llvm::once_flag g_once_flag; | |
1258 llvm::call_once(g_once_flag, [&]() { | |
1259 g_init_terminal_fds_mutex_ptr = | |
1260 new std::mutex(); // NOTE: Leak to avoid C++ destructor chain issues | |
1261 g_init_terminal_fds_ptr = new std::set<int>(); // NOTE: Leak to avoid | |
1262 // C++ destructor chain | |
1263 // issues | |
1264 }); | |
1265 | |
1266 // We must make sure to initialize the terminal a given file descriptor | |
1267 // only once. If we do this multiple times, we start leaking memory. | |
1268 std::lock_guard<std::mutex> guard(*g_init_terminal_fds_mutex_ptr); | |
1269 if (g_init_terminal_fds_ptr->find(term_fd) == | |
1270 g_init_terminal_fds_ptr->end()) { | |
1271 g_init_terminal_fds_ptr->insert(term_fd); | |
1272 setupterm((char *)0, term_fd, (int *)0); | |
1273 } | |
1274 } | |
1275 } | |
1276 #endif | |
1277 } | |
1278 | |
1279 Editline::~Editline() { | |
1280 if (m_editline) { | |
1281 // Disable edit mode to stop the terminal from flushing all input during | |
1282 // the call to el_end() since we expect to have multiple editline instances | |
1283 // in this program. | |
1284 el_set(m_editline, EL_EDITMODE, 0); | |
1285 el_end(m_editline); | |
1286 m_editline = nullptr; | |
1287 } | |
1288 | |
1289 // EditlineHistory objects are sometimes shared between multiple Editline | |
1290 // instances with the same program name. So just release our shared pointer | |
1291 // and if we are the last owner, it will save the history to the history save | |
1292 // file automatically. | |
1293 m_history_sp.reset(); | |
1294 } | |
1295 | |
1296 void Editline::SetPrompt(const char *prompt) { | |
1297 m_set_prompt = prompt == nullptr ? "" : prompt; | |
1298 } | |
1299 | |
1300 void Editline::SetContinuationPrompt(const char *continuation_prompt) { | |
1301 m_set_continuation_prompt = | |
1302 continuation_prompt == nullptr ? "" : continuation_prompt; | |
1303 } | |
1304 | |
1305 void Editline::TerminalSizeChanged() { | |
1306 if (m_editline != nullptr) { | |
1307 el_resize(m_editline); | |
1308 int columns; | |
1309 // This function is documenting as taking (const char *, void *) for the | |
1310 // vararg part, but in reality in was consuming arguments until the first | |
1311 // null pointer. This was fixed in libedit in April 2019 | |
1312 // <http://mail-index.netbsd.org/source-changes/2019/04/26/msg105454.html>, | |
1313 // but we're keeping the workaround until a version with that fix is more | |
1314 // widely available. | |
1315 if (el_get(m_editline, EL_GETTC, "co", &columns, nullptr) == 0) { | |
1316 m_terminal_width = columns; | |
1317 if (m_current_line_rows != -1) { | |
1318 const LineInfoW *info = el_wline(m_editline); | |
1319 int lineLength = | |
1320 (int)((info->lastchar - info->buffer) + GetPromptWidth()); | |
1321 m_current_line_rows = (lineLength / columns) + 1; | |
1322 } | |
1323 } else { | |
1324 m_terminal_width = INT_MAX; | |
1325 m_current_line_rows = 1; | |
1326 } | |
1327 } | |
1328 } | |
1329 | |
1330 const char *Editline::GetPrompt() { return m_set_prompt.c_str(); } | |
1331 | |
1332 uint32_t Editline::GetCurrentLine() { return m_current_line_index; } | |
1333 | |
1334 bool Editline::Interrupt() { | |
1335 bool result = true; | |
1336 std::lock_guard<std::mutex> guard(m_output_mutex); | |
1337 if (m_editor_status == EditorStatus::Editing) { | |
1338 fprintf(m_output_file, "^C\n"); | |
1339 result = m_input_connection.InterruptRead(); | |
1340 } | |
1341 m_editor_status = EditorStatus::Interrupted; | |
1342 return result; | |
1343 } | |
1344 | |
1345 bool Editline::Cancel() { | |
1346 bool result = true; | |
1347 std::lock_guard<std::mutex> guard(m_output_mutex); | |
1348 if (m_editor_status == EditorStatus::Editing) { | |
1349 MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart); | |
1350 fprintf(m_output_file, ANSI_CLEAR_BELOW); | |
1351 result = m_input_connection.InterruptRead(); | |
1352 } | |
1353 m_editor_status = EditorStatus::Interrupted; | |
1354 return result; | |
1355 } | |
1356 | |
1357 void Editline::SetAutoCompleteCallback(CompleteCallbackType callback, | |
1358 void *baton) { | |
1359 m_completion_callback = callback; | |
1360 m_completion_callback_baton = baton; | |
1361 } | |
1362 | |
1363 void Editline::SetIsInputCompleteCallback(IsInputCompleteCallbackType callback, | |
1364 void *baton) { | |
1365 m_is_input_complete_callback = callback; | |
1366 m_is_input_complete_callback_baton = baton; | |
1367 } | |
1368 | |
1369 bool Editline::SetFixIndentationCallback(FixIndentationCallbackType callback, | |
1370 void *baton, | |
1371 const char *indent_chars) { | |
1372 m_fix_indentation_callback = callback; | |
1373 m_fix_indentation_callback_baton = baton; | |
1374 m_fix_indentation_callback_chars = indent_chars; | |
1375 return false; | |
1376 } | |
1377 | |
1378 bool Editline::GetLine(std::string &line, bool &interrupted) { | |
1379 ConfigureEditor(false); | |
1380 m_input_lines = std::vector<EditLineStringType>(); | |
1381 m_input_lines.insert(m_input_lines.begin(), EditLineConstString("")); | |
1382 | |
1383 std::lock_guard<std::mutex> guard(m_output_mutex); | |
1384 | |
1385 lldbassert(m_editor_status != EditorStatus::Editing); | |
1386 if (m_editor_status == EditorStatus::Interrupted) { | |
1387 m_editor_status = EditorStatus::Complete; | |
1388 interrupted = true; | |
1389 return true; | |
1390 } | |
1391 | |
1392 SetCurrentLine(0); | |
1393 m_in_history = false; | |
1394 m_editor_status = EditorStatus::Editing; | |
1395 m_revert_cursor_index = -1; | |
1396 | |
1397 int count; | |
1398 auto input = el_wgets(m_editline, &count); | |
1399 | |
1400 interrupted = m_editor_status == EditorStatus::Interrupted; | |
1401 if (!interrupted) { | |
1402 if (input == nullptr) { | |
1403 fprintf(m_output_file, "\n"); | |
1404 m_editor_status = EditorStatus::EndOfInput; | |
1405 } else { | |
1406 m_history_sp->Enter(input); | |
1407 #if LLDB_EDITLINE_USE_WCHAR | |
1408 line = m_utf8conv.to_bytes(SplitLines(input)[0]); | |
1409 #else | |
1410 line = SplitLines(input)[0]; | |
1411 #endif | |
1412 m_editor_status = EditorStatus::Complete; | |
1413 } | |
1414 } | |
1415 return m_editor_status != EditorStatus::EndOfInput; | |
1416 } | |
1417 | |
1418 bool Editline::GetLines(int first_line_number, StringList &lines, | |
1419 bool &interrupted) { | |
1420 ConfigureEditor(true); | |
1421 | |
1422 // Print the initial input lines, then move the cursor back up to the start | |
1423 // of input | |
1424 SetBaseLineNumber(first_line_number); | |
1425 m_input_lines = std::vector<EditLineStringType>(); | |
1426 m_input_lines.insert(m_input_lines.begin(), EditLineConstString("")); | |
1427 | |
1428 std::lock_guard<std::mutex> guard(m_output_mutex); | |
1429 // Begin the line editing loop | |
1430 DisplayInput(); | |
1431 SetCurrentLine(0); | |
1432 MoveCursor(CursorLocation::BlockEnd, CursorLocation::BlockStart); | |
1433 m_editor_status = EditorStatus::Editing; | |
1434 m_in_history = false; | |
1435 | |
1436 m_revert_cursor_index = -1; | |
1437 while (m_editor_status == EditorStatus::Editing) { | |
1438 int count; | |
1439 m_current_line_rows = -1; | |
1440 el_wpush(m_editline, EditLineConstString( | |
1441 "\x1b[^")); // Revert to the existing line content | |
1442 el_wgets(m_editline, &count); | |
1443 } | |
1444 | |
1445 interrupted = m_editor_status == EditorStatus::Interrupted; | |
1446 if (!interrupted) { | |
1447 // Save the completed entry in history before returning | |
1448 m_history_sp->Enter(CombineLines(m_input_lines).c_str()); | |
1449 | |
1450 lines = GetInputAsStringList(); | |
1451 } | |
1452 return m_editor_status != EditorStatus::EndOfInput; | |
1453 } | |
1454 | |
1455 void Editline::PrintAsync(Stream *stream, const char *s, size_t len) { | |
1456 std::lock_guard<std::mutex> guard(m_output_mutex); | |
1457 if (m_editor_status == EditorStatus::Editing) { | |
1458 MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart); | |
1459 fprintf(m_output_file, ANSI_CLEAR_BELOW); | |
1460 } | |
1461 stream->Write(s, len); | |
1462 stream->Flush(); | |
1463 if (m_editor_status == EditorStatus::Editing) { | |
1464 DisplayInput(); | |
1465 MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor); | |
1466 } | |
1467 } | |
1468 | |
1469 bool Editline::CompleteCharacter(char ch, EditLineGetCharType &out) { | |
1470 #if !LLDB_EDITLINE_USE_WCHAR | |
1471 if (ch == (char)EOF) | |
1472 return false; | |
1473 | |
1474 out = (unsigned char)ch; | |
1475 return true; | |
1476 #else | |
1477 std::codecvt_utf8<wchar_t> cvt; | |
1478 llvm::SmallString<4> input; | |
1479 for (;;) { | |
1480 const char *from_next; | |
1481 wchar_t *to_next; | |
1482 std::mbstate_t state = std::mbstate_t(); | |
1483 input.push_back(ch); | |
1484 switch (cvt.in(state, input.begin(), input.end(), from_next, &out, &out + 1, | |
1485 to_next)) { | |
1486 case std::codecvt_base::ok: | |
1487 return out != (int)WEOF; | |
1488 | |
1489 case std::codecvt_base::error: | |
1490 case std::codecvt_base::noconv: | |
1491 return false; | |
1492 | |
1493 case std::codecvt_base::partial: | |
1494 lldb::ConnectionStatus status; | |
1495 size_t read_count = m_input_connection.Read( | |
1496 &ch, 1, std::chrono::seconds(0), status, nullptr); | |
1497 if (read_count == 0) | |
1498 return false; | |
1499 break; | |
1500 } | |
1501 } | |
1502 #endif | |
1503 } |