[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