diff lldb/source/Host/common/Editline.cpp @ 221:79ff65ed7e25

LLVM12 Original
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Tue, 15 Jun 2021 19:15:29 +0900
parents 0572611fdcc8
children c4bab56944e8
line wrap: on
line diff
--- a/lldb/source/Host/common/Editline.cpp	Tue Jun 15 19:13:43 2021 +0900
+++ b/lldb/source/Host/common/Editline.cpp	Tue Jun 15 19:15:29 2021 +0900
@@ -6,12 +6,12 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include <climits>
 #include <iomanip>
-#include <iostream>
-#include <limits.h>
+
+#include "lldb/Host/Editline.h"
 
 #include "lldb/Host/ConnectionFileDescriptor.h"
-#include "lldb/Host/Editline.h"
 #include "lldb/Host/FileSystem.h"
 #include "lldb/Host/Host.h"
 #include "lldb/Utility/CompletionRequest.h"
@@ -49,9 +49,12 @@
 // understand the relationship between DisplayInput(), MoveCursor(),
 // SetCurrentLine(), and SaveEditedLine() before making changes.
 
+/// https://www.ecma-international.org/publications/files/ECMA-ST/Ecma-048.pdf
 #define ESCAPE "\x1b"
+/// Faint, decreased intensity or second colour.
 #define ANSI_FAINT ESCAPE "[2m"
-#define ANSI_UNFAINT ESCAPE "[22m"
+/// Normal colour or normal intensity (neither bold nor faint).
+#define ANSI_UNFAINT ESCAPE "[0m"
 #define ANSI_CLEAR_BELOW ESCAPE "[J"
 #define ANSI_CLEAR_RIGHT ESCAPE "[K"
 #define ANSI_SET_COLUMN_N ESCAPE "[%dG"
@@ -150,6 +153,11 @@
     result.push_back(input.substr(start, end - start));
     start = end + 1;
   }
+  // Treat an empty history session as a single command of zero-length instead
+  // of returning an empty vector.
+  if (result.empty()) {
+    result.emplace_back();
+  }
   return result;
 }
 
@@ -208,7 +216,7 @@
     // Compute the history path lazily.
     if (m_path.empty() && m_history && !m_prefix.empty()) {
       llvm::SmallString<128> lldb_history_file;
-      llvm::sys::path::home_directory(lldb_history_file);
+      FileSystem::Instance().GetHomeDirectory(lldb_history_file);
       llvm::sys::path::append(lldb_history_file, ".lldb");
 
       // LLDB stores its history in ~/.lldb/. If for some reason this directory
@@ -639,8 +647,7 @@
       lines.AppendString(new_line_fragment);
 #endif
 
-      int indent_correction = m_fix_indentation_callback(
-          this, lines, 0, m_fix_indentation_callback_baton);
+      int indent_correction = m_fix_indentation_callback(this, lines, 0);
       new_line_fragment = FixIndentation(new_line_fragment, indent_correction);
       m_revert_cursor_index = GetIndentation(new_line_fragment);
     }
@@ -675,8 +682,7 @@
       info->cursor == info->lastchar) {
     if (m_is_input_complete_callback) {
       auto lines = GetInputAsStringList();
-      if (!m_is_input_complete_callback(this, lines,
-                                        m_is_input_complete_callback_baton)) {
+      if (!m_is_input_complete_callback(this, lines)) {
         return BreakLineCommand(ch);
       }
 
@@ -809,8 +815,7 @@
     if (m_fix_indentation_callback) {
       StringList lines = GetInputAsStringList();
       lines.AppendString("");
-      indentation = m_fix_indentation_callback(
-          this, lines, 0, m_fix_indentation_callback_baton);
+      indentation = m_fix_indentation_callback(this, lines, 0);
     }
     m_input_lines.insert(
         m_input_lines.end(),
@@ -855,8 +860,8 @@
   // Save the edits and determine the correct indentation level
   SaveEditedLine();
   StringList lines = GetInputAsStringList(m_current_line_index + 1);
-  int indent_correction = m_fix_indentation_callback(
-      this, lines, cursor_position, m_fix_indentation_callback_baton);
+  int indent_correction =
+      m_fix_indentation_callback(this, lines, cursor_position);
 
   // If it is already correct no special work is needed
   if (indent_correction == 0)
@@ -975,7 +980,7 @@
 }
 
 unsigned char Editline::TabCommand(int ch) {
-  if (m_completion_callback == nullptr)
+  if (!m_completion_callback)
     return CC_ERROR;
 
   const LineInfo *line_info = el_line(m_editline);
@@ -986,7 +991,7 @@
   CompletionResult result;
   CompletionRequest request(line, cursor_index, result);
 
-  m_completion_callback(request, m_completion_callback_baton);
+  m_completion_callback(request);
 
   llvm::ArrayRef<CompletionResult::Completion> results = result.GetResults();
 
@@ -1002,11 +1007,15 @@
     case CompletionMode::Normal: {
       std::string to_add = completion.GetCompletion();
       to_add = to_add.substr(request.GetCursorArgumentPrefix().size());
-      if (request.GetParsedArg().IsQuoted())
+      // Terminate the current argument with a quote if it started with a quote.
+      if (!request.GetParsedLine().empty() && request.GetParsedArg().IsQuoted())
         to_add.push_back(request.GetParsedArg().GetQuoteChar());
       to_add.push_back(' ');
       el_insertstr(m_editline, to_add.c_str());
-      break;
+      // Clear all the autosuggestion parts if the only single space can be completed.
+      if (to_add == " ")
+        return CC_REDISPLAY;
+      return CC_REFRESH;
     }
     case CompletionMode::Partial: {
       std::string to_add = completion.GetCompletion();
@@ -1040,6 +1049,74 @@
   return CC_REDISPLAY;
 }
 
+unsigned char Editline::ApplyAutosuggestCommand(int ch) {
+  if (!m_suggestion_callback) {
+    return CC_REDISPLAY;
+  }
+
+  const LineInfo *line_info = el_line(m_editline);
+  llvm::StringRef line(line_info->buffer,
+                       line_info->lastchar - line_info->buffer);
+
+  if (llvm::Optional<std::string> to_add = m_suggestion_callback(line))
+    el_insertstr(m_editline, to_add->c_str());
+
+  return CC_REDISPLAY;
+}
+
+unsigned char Editline::TypedCharacter(int ch) {
+  std::string typed = std::string(1, ch);
+  el_insertstr(m_editline, typed.c_str());
+
+  if (!m_suggestion_callback) {
+    return CC_REDISPLAY;
+  }
+
+  const LineInfo *line_info = el_line(m_editline);
+  llvm::StringRef line(line_info->buffer,
+                       line_info->lastchar - line_info->buffer);
+
+  if (llvm::Optional<std::string> to_add = m_suggestion_callback(line)) {
+    std::string to_add_color = ANSI_FAINT + to_add.getValue() + ANSI_UNFAINT;
+    fputs(typed.c_str(), m_output_file);
+    fputs(to_add_color.c_str(), m_output_file);
+    size_t new_autosuggestion_size = line.size() + to_add->length();
+    // Print spaces to hide any remains of a previous longer autosuggestion.
+    if (new_autosuggestion_size < m_previous_autosuggestion_size) {
+      size_t spaces_to_print =
+          m_previous_autosuggestion_size - new_autosuggestion_size;
+      std::string spaces = std::string(spaces_to_print, ' ');
+      fputs(spaces.c_str(), m_output_file);
+    }
+    m_previous_autosuggestion_size = new_autosuggestion_size;
+
+    int editline_cursor_position =
+        (int)((line_info->cursor - line_info->buffer) + GetPromptWidth());
+    int editline_cursor_row = editline_cursor_position / m_terminal_width;
+    int toColumn =
+        editline_cursor_position - (editline_cursor_row * m_terminal_width);
+    fprintf(m_output_file, ANSI_SET_COLUMN_N, toColumn);
+    return CC_REFRESH;
+  }
+
+  return CC_REDISPLAY;
+}
+
+void Editline::AddFunctionToEditLine(const EditLineCharType *command,
+                                     const EditLineCharType *helptext,
+                                     EditlineCommandCallbackType callbackFn) {
+  el_wset(m_editline, EL_ADDFN, command, helptext, callbackFn);
+}
+
+void Editline::SetEditLinePromptCallback(
+    EditlinePromptCallbackType callbackFn) {
+  el_set(m_editline, EL_PROMPT, callbackFn);
+}
+
+void Editline::SetGetCharacterFunction(EditlineGetCharCallbackType callbackFn) {
+  el_wset(m_editline, EL_GETCFN, callbackFn);
+}
+
 void Editline::ConfigureEditor(bool multiline) {
   if (m_editline && m_multiline_enabled == multiline)
     return;
@@ -1066,74 +1143,83 @@
   el_set(m_editline, EL_CLIENTDATA, this);
   el_set(m_editline, EL_SIGNAL, 0);
   el_set(m_editline, EL_EDITOR, "emacs");
-  el_set(m_editline, EL_PROMPT,
-         (EditlinePromptCallbackType)([](EditLine *editline) {
-           return Editline::InstanceFor(editline)->Prompt();
-         }));
 
-  el_wset(m_editline, EL_GETCFN, (EditlineGetCharCallbackType)([](
-                                     EditLine *editline, EditLineGetCharType *c) {
-            return Editline::InstanceFor(editline)->GetCharacter(c);
-          }));
+  SetGetCharacterFunction([](EditLine *editline, EditLineGetCharType *c) {
+    return Editline::InstanceFor(editline)->GetCharacter(c);
+  });
+
+  SetEditLinePromptCallback([](EditLine *editline) {
+    return Editline::InstanceFor(editline)->Prompt();
+  });
 
   // Commands used for multiline support, registered whether or not they're
   // used
-  el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-break-line"),
-          EditLineConstString("Insert a line break"),
-          (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
-            return Editline::InstanceFor(editline)->BreakLineCommand(ch);
-          }));
-  el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-end-or-add-line"),
-          EditLineConstString("End editing or continue when incomplete"),
-          (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
-            return Editline::InstanceFor(editline)->EndOrAddLineCommand(ch);
-          }));
-  el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-delete-next-char"),
-          EditLineConstString("Delete next character"),
-          (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
-            return Editline::InstanceFor(editline)->DeleteNextCharCommand(ch);
-          }));
-  el_wset(
-      m_editline, EL_ADDFN, EditLineConstString("lldb-delete-previous-char"),
+  AddFunctionToEditLine(
+      EditLineConstString("lldb-break-line"),
+      EditLineConstString("Insert a line break"),
+      [](EditLine *editline, int ch) {
+        return Editline::InstanceFor(editline)->BreakLineCommand(ch);
+      });
+
+  AddFunctionToEditLine(
+      EditLineConstString("lldb-end-or-add-line"),
+      EditLineConstString("End editing or continue when incomplete"),
+      [](EditLine *editline, int ch) {
+        return Editline::InstanceFor(editline)->EndOrAddLineCommand(ch);
+      });
+  AddFunctionToEditLine(
+      EditLineConstString("lldb-delete-next-char"),
+      EditLineConstString("Delete next character"),
+      [](EditLine *editline, int ch) {
+        return Editline::InstanceFor(editline)->DeleteNextCharCommand(ch);
+      });
+  AddFunctionToEditLine(
+      EditLineConstString("lldb-delete-previous-char"),
       EditLineConstString("Delete previous character"),
-      (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
+      [](EditLine *editline, int ch) {
         return Editline::InstanceFor(editline)->DeletePreviousCharCommand(ch);
-      }));
-  el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-previous-line"),
-          EditLineConstString("Move to previous line"),
-          (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
-            return Editline::InstanceFor(editline)->PreviousLineCommand(ch);
-          }));
-  el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-next-line"),
-          EditLineConstString("Move to next line"),
-          (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
-            return Editline::InstanceFor(editline)->NextLineCommand(ch);
-          }));
-  el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-previous-history"),
-          EditLineConstString("Move to previous history"),
-          (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
-            return Editline::InstanceFor(editline)->PreviousHistoryCommand(ch);
-          }));
-  el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-next-history"),
-          EditLineConstString("Move to next history"),
-          (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
-            return Editline::InstanceFor(editline)->NextHistoryCommand(ch);
-          }));
-  el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-buffer-start"),
-          EditLineConstString("Move to start of buffer"),
-          (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
-            return Editline::InstanceFor(editline)->BufferStartCommand(ch);
-          }));
-  el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-buffer-end"),
-          EditLineConstString("Move to end of buffer"),
-          (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
-            return Editline::InstanceFor(editline)->BufferEndCommand(ch);
-          }));
-  el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-fix-indentation"),
-          EditLineConstString("Fix line indentation"),
-          (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
-            return Editline::InstanceFor(editline)->FixIndentationCommand(ch);
-          }));
+      });
+  AddFunctionToEditLine(
+      EditLineConstString("lldb-previous-line"),
+      EditLineConstString("Move to previous line"),
+      [](EditLine *editline, int ch) {
+        return Editline::InstanceFor(editline)->PreviousLineCommand(ch);
+      });
+  AddFunctionToEditLine(
+      EditLineConstString("lldb-next-line"),
+      EditLineConstString("Move to next line"), [](EditLine *editline, int ch) {
+        return Editline::InstanceFor(editline)->NextLineCommand(ch);
+      });
+  AddFunctionToEditLine(
+      EditLineConstString("lldb-previous-history"),
+      EditLineConstString("Move to previous history"),
+      [](EditLine *editline, int ch) {
+        return Editline::InstanceFor(editline)->PreviousHistoryCommand(ch);
+      });
+  AddFunctionToEditLine(
+      EditLineConstString("lldb-next-history"),
+      EditLineConstString("Move to next history"),
+      [](EditLine *editline, int ch) {
+        return Editline::InstanceFor(editline)->NextHistoryCommand(ch);
+      });
+  AddFunctionToEditLine(
+      EditLineConstString("lldb-buffer-start"),
+      EditLineConstString("Move to start of buffer"),
+      [](EditLine *editline, int ch) {
+        return Editline::InstanceFor(editline)->BufferStartCommand(ch);
+      });
+  AddFunctionToEditLine(
+      EditLineConstString("lldb-buffer-end"),
+      EditLineConstString("Move to end of buffer"),
+      [](EditLine *editline, int ch) {
+        return Editline::InstanceFor(editline)->BufferEndCommand(ch);
+      });
+  AddFunctionToEditLine(
+      EditLineConstString("lldb-fix-indentation"),
+      EditLineConstString("Fix line indentation"),
+      [](EditLine *editline, int ch) {
+        return Editline::InstanceFor(editline)->FixIndentationCommand(ch);
+      });
 
   // Register the complete callback under two names for compatibility with
   // older clients using custom .editrc files (largely because libedit has a
@@ -1144,16 +1230,50 @@
                                                      int ch) {
     return Editline::InstanceFor(editline)->TabCommand(ch);
   };
-  el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-complete"),
-          EditLineConstString("Invoke completion"), complete_callback);
-  el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb_complete"),
-          EditLineConstString("Invoke completion"), complete_callback);
+  AddFunctionToEditLine(EditLineConstString("lldb-complete"),
+                        EditLineConstString("Invoke completion"),
+                        complete_callback);
+  AddFunctionToEditLine(EditLineConstString("lldb_complete"),
+                        EditLineConstString("Invoke completion"),
+                        complete_callback);
 
   // General bindings we don't mind being overridden
   if (!multiline) {
     el_set(m_editline, EL_BIND, "^r", "em-inc-search-prev",
            NULL); // Cycle through backwards search, entering string
+
+    if (m_suggestion_callback) {
+      AddFunctionToEditLine(
+          EditLineConstString("lldb-apply-complete"),
+          EditLineConstString("Adopt autocompletion"),
+          [](EditLine *editline, int ch) {
+            return Editline::InstanceFor(editline)->ApplyAutosuggestCommand(ch);
+          });
+
+      el_set(m_editline, EL_BIND, "^f", "lldb-apply-complete",
+             NULL); // Apply a part that is suggested automatically
+
+      AddFunctionToEditLine(
+          EditLineConstString("lldb-typed-character"),
+          EditLineConstString("Typed character"),
+          [](EditLine *editline, int ch) {
+            return Editline::InstanceFor(editline)->TypedCharacter(ch);
+          });
+
+      char bind_key[2] = {0, 0};
+      llvm::StringRef ascii_chars =
+          "abcdefghijklmnopqrstuvwxzyABCDEFGHIJKLMNOPQRSTUVWXZY1234567890!\"#$%"
+          "&'()*+,./:;<=>?@[]_`{|}~ ";
+      for (char c : ascii_chars) {
+        bind_key[0] = c;
+        el_set(m_editline, EL_BIND, bind_key, "lldb-typed-character", NULL);
+      }
+      el_set(m_editline, EL_BIND, "\\-", "lldb-typed-character", NULL);
+      el_set(m_editline, EL_BIND, "\\^", "lldb-typed-character", NULL);
+      el_set(m_editline, EL_BIND, "\\\\", "lldb-typed-character", NULL);
+    }
   }
+
   el_set(m_editline, EL_BIND, "^w", "ed-delete-prev-word",
          NULL); // Delete previous word, behave like bash in emacs mode
   el_set(m_editline, EL_BIND, "\t", "lldb-complete",
@@ -1173,11 +1293,12 @@
   el_source(m_editline, nullptr);
 
   // Register an internal binding that external developers shouldn't use
-  el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-revert-line"),
-          EditLineConstString("Revert line to saved state"),
-          (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
-            return Editline::InstanceFor(editline)->RevertLineCommand(ch);
-          }));
+  AddFunctionToEditLine(
+      EditLineConstString("lldb-revert-line"),
+      EditLineConstString("Revert line to saved state"),
+      [](EditLine *editline, int ch) {
+        return Editline::InstanceFor(editline)->RevertLineCommand(ch);
+      });
 
   // Register keys that perform auto-indent correction
   if (m_fix_indentation_callback && m_fix_indentation_callback_chars) {
@@ -1364,27 +1485,6 @@
   return result;
 }
 
-void Editline::SetAutoCompleteCallback(CompleteCallbackType callback,
-                                       void *baton) {
-  m_completion_callback = callback;
-  m_completion_callback_baton = baton;
-}
-
-void Editline::SetIsInputCompleteCallback(IsInputCompleteCallbackType callback,
-                                          void *baton) {
-  m_is_input_complete_callback = callback;
-  m_is_input_complete_callback_baton = baton;
-}
-
-bool Editline::SetFixIndentationCallback(FixIndentationCallbackType callback,
-                                         void *baton,
-                                         const char *indent_chars) {
-  m_fix_indentation_callback = callback;
-  m_fix_indentation_callback_baton = baton;
-  m_fix_indentation_callback_chars = indent_chars;
-  return false;
-}
-
 bool Editline::GetLine(std::string &line, bool &interrupted) {
   ConfigureEditor(false);
   m_input_lines = std::vector<EditLineStringType>();