[Lldb-commits] [lldb] 3c11e57 - [LLDB][GUI] Add initial searcher support

Greg Clayton via lldb-commits lldb-commits at lists.llvm.org
Wed Aug 25 13:55:18 PDT 2021


Author: Omar Emara
Date: 2021-08-25T13:55:11-07:00
New Revision: 3c11e5722c302a9dcf01b97ace868e156e376bc6

URL: https://github.com/llvm/llvm-project/commit/3c11e5722c302a9dcf01b97ace868e156e376bc6
DIFF: https://github.com/llvm/llvm-project/commit/3c11e5722c302a9dcf01b97ace868e156e376bc6.diff

LOG: [LLDB][GUI] Add initial searcher support

This patch adds a new type of reusable UI components. Searcher Windows
contain a text field to enter a search keyword and a list of scrollable
matches are presented. The target match can be selected and executed
which invokes a user callback to do something with the match.

This patch also adds one searcher delegate, which wraps the common
command completion searchers for simple use cases.

Reviewed By: clayborg

Differential Revision: https://reviews.llvm.org/D108545

Added: 
    

Modified: 
    lldb/source/Core/IOHandlerCursesGUI.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/source/Core/IOHandlerCursesGUI.cpp b/lldb/source/Core/IOHandlerCursesGUI.cpp
index ef4f5c3d35f6..71e8365e2511 100644
--- a/lldb/source/Core/IOHandlerCursesGUI.cpp
+++ b/lldb/source/Core/IOHandlerCursesGUI.cpp
@@ -3641,6 +3641,232 @@ class ProcessLaunchFormDelegate : public FormDelegate {
   EnvironmentVariableListFieldDelegate *m_inherited_environment_field;
 };
 
+////////////
+// Searchers
+////////////
+
+class SearcherDelegate {
+public:
+  SearcherDelegate() {}
+
+  virtual ~SearcherDelegate() = default;
+
+  virtual int GetNumberOfMatches() = 0;
+
+  // Get the string that will be displayed for the match at the input index.
+  virtual const std::string &GetMatchTextAtIndex(int index) = 0;
+
+  // Update the matches of the search. This is executed every time the text
+  // field handles an event.
+  virtual void UpdateMatches(const std::string &text) = 0;
+
+  // Execute the user callback given the index of some match. This is executed
+  // once the user selects a match.
+  virtual void ExecuteCallback(int match_index) = 0;
+};
+
+typedef std::shared_ptr<SearcherDelegate> SearcherDelegateSP;
+
+class SearcherWindowDelegate : public WindowDelegate {
+public:
+  SearcherWindowDelegate(SearcherDelegateSP &delegate_sp)
+      : m_delegate_sp(delegate_sp), m_text_field("Search", "", false),
+        m_selected_match(0), m_first_visible_match(0) {
+    ;
+  }
+
+  // A completion window is padded by one character from all sides. A text field
+  // is first drawn for inputting the searcher request, then a list of matches
+  // are displayed in a scrollable list.
+  //
+  // ___<Searcher Window Name>____________________________
+  // |                                                   |
+  // | __[Search]_______________________________________ |
+  // | |                                               | |
+  // | |_______________________________________________| |
+  // | - Match 1.                                        |
+  // | - Match 2.                                        |
+  // | - ...                                             |
+  // |                                                   |
+  // |____________________________[Press Esc to Cancel]__|
+  //
+
+  // Get the index of the last visible match. Assuming at least one match
+  // exists.
+  int GetLastVisibleMatch(int height) {
+    int index = m_first_visible_match + height;
+    return std::min(index, m_delegate_sp->GetNumberOfMatches()) - 1;
+  }
+
+  int GetNumberOfVisibleMatches(int height) {
+    return GetLastVisibleMatch(height) - m_first_visible_match + 1;
+  }
+
+  void UpdateScrolling(Surface &surface) {
+    if (m_selected_match < m_first_visible_match) {
+      m_first_visible_match = m_selected_match;
+      return;
+    }
+
+    int height = surface.GetHeight();
+    int last_visible_match = GetLastVisibleMatch(height);
+    if (m_selected_match > last_visible_match) {
+      m_first_visible_match = m_selected_match - height + 1;
+    }
+  }
+
+  void DrawMatches(Surface &surface) {
+    if (m_delegate_sp->GetNumberOfMatches() == 0)
+      return;
+
+    UpdateScrolling(surface);
+
+    int count = GetNumberOfVisibleMatches(surface.GetHeight());
+    for (int i = 0; i < count; i++) {
+      surface.MoveCursor(1, i);
+      int current_match = m_first_visible_match + i;
+      if (current_match == m_selected_match)
+        surface.AttributeOn(A_REVERSE);
+      surface.PutCString(
+          m_delegate_sp->GetMatchTextAtIndex(current_match).c_str());
+      if (current_match == m_selected_match)
+        surface.AttributeOff(A_REVERSE);
+    }
+  }
+
+  void DrawContent(Surface &surface) {
+    Rect content_bounds = surface.GetFrame();
+    Rect text_field_bounds, matchs_bounds;
+    content_bounds.HorizontalSplit(m_text_field.FieldDelegateGetHeight(),
+                                   text_field_bounds, matchs_bounds);
+    Surface text_field_surface = surface.SubSurface(text_field_bounds);
+    Surface matches_surface = surface.SubSurface(matchs_bounds);
+
+    m_text_field.FieldDelegateDraw(text_field_surface, true);
+    DrawMatches(matches_surface);
+  }
+
+  bool WindowDelegateDraw(Window &window, bool force) override {
+    window.Erase();
+
+    window.DrawTitleBox(window.GetName(), "Press Esc to Cancel");
+
+    Rect content_bounds = window.GetFrame();
+    content_bounds.Inset(2, 2);
+    Surface content_surface = window.SubSurface(content_bounds);
+
+    DrawContent(content_surface);
+    return true;
+  }
+
+  void SelectNext() {
+    if (m_selected_match != m_delegate_sp->GetNumberOfMatches() - 1)
+      m_selected_match++;
+    return;
+  }
+
+  void SelectPrevious() {
+    if (m_selected_match != 0)
+      m_selected_match--;
+    return;
+  }
+
+  void ExecuteCallback(Window &window) {
+    m_delegate_sp->ExecuteCallback(m_selected_match);
+    window.GetParent()->RemoveSubWindow(&window);
+  }
+
+  void UpdateMatches() {
+    m_delegate_sp->UpdateMatches(m_text_field.GetText());
+    m_selected_match = 0;
+  }
+
+  HandleCharResult WindowDelegateHandleChar(Window &window, int key) override {
+    switch (key) {
+    case '\r':
+    case '\n':
+    case KEY_ENTER:
+      ExecuteCallback(window);
+      return eKeyHandled;
+    case '\t':
+    case KEY_DOWN:
+      SelectNext();
+      return eKeyHandled;
+    case KEY_SHIFT_TAB:
+    case KEY_UP:
+      SelectPrevious();
+      return eKeyHandled;
+    case KEY_ESCAPE:
+      window.GetParent()->RemoveSubWindow(&window);
+      return eKeyHandled;
+    default:
+      break;
+    }
+
+    if (m_text_field.FieldDelegateHandleChar(key) == eKeyHandled)
+      UpdateMatches();
+
+    return eKeyHandled;
+  }
+
+protected:
+  SearcherDelegateSP m_delegate_sp;
+  TextFieldDelegate m_text_field;
+  // The index of the currently selected match.
+  int m_selected_match;
+  // The index of the first visible match.
+  int m_first_visible_match;
+};
+
+//////////////////////////////
+// Searcher Delegate Instances
+//////////////////////////////
+
+// This is a searcher delegate wrapper around CommandCompletions common
+// callbacks. The callbacks are only given the match string. The completion_mask
+// can be a combination of CommonCompletionTypes.
+class CommonCompletionSearcherDelegate : public SearcherDelegate {
+public:
+  typedef std::function<void(const std::string &)> CallbackType;
+
+  CommonCompletionSearcherDelegate(Debugger &debugger, uint32_t completion_mask,
+                                   CallbackType callback)
+      : m_debugger(debugger), m_completion_mask(completion_mask),
+        m_callback(callback) {}
+
+  int GetNumberOfMatches() override { return m_matches.GetSize(); }
+
+  const std::string &GetMatchTextAtIndex(int index) override {
+    return m_matches[index];
+  }
+
+  void UpdateMatches(const std::string &text) override {
+    CompletionResult result;
+    CompletionRequest request(text.c_str(), text.size(), result);
+    CommandCompletions::InvokeCommonCompletionCallbacks(
+        m_debugger.GetCommandInterpreter(), m_completion_mask, request,
+        nullptr);
+    result.GetMatches(m_matches);
+  }
+
+  void ExecuteCallback(int match_index) override {
+    m_callback(m_matches[match_index]);
+  }
+
+protected:
+  Debugger &m_debugger;
+  // A compound mask from CommonCompletionTypes.
+  uint32_t m_completion_mask;
+  // A callback to execute once the user selects a match. The match is passed to
+  // the callback as a string.
+  CallbackType m_callback;
+  StringList m_matches;
+};
+
+////////
+// Menus
+////////
+
 class MenuDelegate {
 public:
   virtual ~MenuDelegate() = default;


        


More information about the lldb-commits mailing list