xref: /freebsd/contrib/llvm-project/lldb/include/lldb/Utility/CompletionRequest.h (revision 700637cbb5e582861067a11aaca4d053546871d2)
1 //===-- CompletionRequest.h -------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #ifndef LLDB_UTILITY_COMPLETIONREQUEST_H
10 #define LLDB_UTILITY_COMPLETIONREQUEST_H
11 
12 #include "lldb/Utility/Args.h"
13 #include "lldb/Utility/LLDBAssert.h"
14 #include "lldb/Utility/StringList.h"
15 #include "llvm/ADT/StringRef.h"
16 #include "llvm/ADT/StringSet.h"
17 
18 namespace lldb_private {
19 enum class CompletionMode {
20   /// The current token has been completed. The client should indicate this
21   /// to the user (usually this is done by adding a trailing space behind the
22   /// token).
23   /// Example: "command sub" -> "command subcommand " (note the trailing space).
24   Normal,
25   /// The current token has been partially completed. This means that we found
26   /// a completion, but that the token is still incomplete. Examples
27   /// for this are file paths, where we want to complete "/bi" to "/bin/", but
28   /// the file path token is still incomplete after the completion. Clients
29   /// should not indicate to the user that this is a full completion (e.g. by
30   /// not inserting the usual trailing space after a successful completion).
31   /// Example: "file /us" -> "file /usr/" (note the missing trailing space).
32   Partial,
33   /// The full line has been rewritten by the completion.
34   /// Example: "alias name" -> "other_command full_name".
35   RewriteLine,
36 };
37 
38 class CompletionResult {
39 public:
40   /// A single completion and all associated data.
41   class Completion {
42 
43     /// The actual text that should be completed. The meaning of this text
44     /// is defined by the CompletionMode.
45     /// \see m_mode
46     std::string m_completion;
47     /// The description that should be displayed to the user alongside the
48     /// completion text.
49     std::string m_descripton;
50     CompletionMode m_mode;
51 
52   public:
Completion(llvm::StringRef completion,llvm::StringRef description,CompletionMode mode)53     Completion(llvm::StringRef completion, llvm::StringRef description,
54                CompletionMode mode)
55         : m_completion(completion.rtrim().str()),
56           m_descripton(description.rtrim().str()), m_mode(mode) {}
GetCompletion()57     const std::string &GetCompletion() const { return m_completion; }
GetDescription()58     const std::string &GetDescription() const { return m_descripton; }
GetMode()59     CompletionMode GetMode() const { return m_mode; }
60 
61     /// Generates a string that uniquely identifies this completion result.
62     std::string GetUniqueKey() const;
63   };
64 
65 private:
66   /// List of found completions.
67   std::vector<Completion> m_results;
68 
69   /// A set of the unique keys of all found completions so far. Used to filter
70   /// out duplicates.
71   /// \see CompletionResult::Completion::GetUniqueKey
72   llvm::StringSet<> m_added_values;
73 
74 public:
75   void AddResult(llvm::StringRef completion, llvm::StringRef description,
76                  CompletionMode mode);
77 
GetResults()78   llvm::ArrayRef<Completion> GetResults() const { return m_results; }
79 
80   /// Adds all collected completion matches to the given list.
81   /// The list will be cleared before the results are added. The number of
82   /// results here is guaranteed to be equal to GetNumberOfResults().
83   void GetMatches(StringList &matches) const;
84 
85   /// Adds all collected completion descriptions to the given list.
86   /// The list will be cleared before the results are added. The number of
87   /// results here is guaranteed to be equal to GetNumberOfResults().
88   void GetDescriptions(StringList &descriptions) const;
89 
GetNumberOfResults()90   std::size_t GetNumberOfResults() const { return m_results.size(); }
91 };
92 
93 /// \class CompletionRequest CompletionRequest.h
94 ///   "lldb/Utility/ArgCompletionRequest.h"
95 ///
96 /// Contains all information necessary to complete an incomplete command
97 /// for the user. Will be filled with the generated completions by the different
98 /// completions functions.
99 ///
100 class CompletionRequest {
101 public:
102   /// Constructs a completion request.
103   ///
104   /// \param [in] command_line
105   ///     The command line the user has typed at this point.
106   ///
107   /// \param [in] raw_cursor_pos
108   ///     The position of the cursor in the command line string. Index 0 means
109   ///     the cursor is at the start of the line. The completion starts from
110   ///     this cursor position.
111   ///
112   /// \param [out] result
113   ///     The CompletionResult that will be filled with the results after this
114   ///     request has been handled.
115   CompletionRequest(llvm::StringRef command_line, unsigned raw_cursor_pos,
116                     CompletionResult &result);
117 
118   /// Sets the maximum number of completions that should be returned.
SetMaxReturnElements(size_t max_return_elements)119   void SetMaxReturnElements(size_t max_return_elements) {
120     m_max_return_elements = max_return_elements;
121   }
122 
123   /// Returns the raw user input used to create this CompletionRequest cut off
124   /// at the cursor position. The cursor will be at the end of the raw line.
GetRawLine()125   llvm::StringRef GetRawLine() const {
126     return m_command.substr(0, GetRawCursorPos());
127   }
128 
129   /// Returns the full raw user input used to create this CompletionRequest.
130   /// This string is not cut off at the cursor position and will include
131   /// characters behind the cursor position.
132   ///
133   /// You should most likely *not* use this function unless the characters
134   /// behind the cursor position influence the completion.
GetRawLineWithUnusedSuffix()135   llvm::StringRef GetRawLineWithUnusedSuffix() const { return m_command; }
136 
GetRawCursorPos()137   unsigned GetRawCursorPos() const { return m_raw_cursor_pos; }
138 
GetParsedLine()139   const Args &GetParsedLine() const { return m_parsed_line; }
140 
GetParsedLine()141   Args &GetParsedLine() { return m_parsed_line; }
142 
GetParsedArg()143   const Args::ArgEntry &GetParsedArg() {
144     return GetParsedLine()[GetCursorIndex()];
145   }
146 
GetCursorCharPos()147   size_t GetCursorCharPos() const { return m_cursor_char_position; }
148 
149   /// Drops the first argument from the argument list.
ShiftArguments()150   void ShiftArguments() {
151     m_cursor_index--;
152     m_parsed_line.Shift();
153   }
154 
155   /// Adds an empty argument at the end of the argument list and moves
156   /// the cursor to this new argument.
AppendEmptyArgument()157   void AppendEmptyArgument() {
158     m_parsed_line.AppendArgument(llvm::StringRef());
159     m_cursor_index++;
160     m_cursor_char_position = 0;
161   }
162 
GetCursorIndex()163   size_t GetCursorIndex() const { return m_cursor_index; }
164 
GetMaxReturnElements()165   size_t GetMaxReturnElements() const { return m_max_return_elements; }
166 
167   /// Returns true if the maximum number of completions has not been reached
168   /// yet, hence we should keep adding completions.
ShouldAddCompletions()169   bool ShouldAddCompletions() const {
170     return GetMaxNumberOfCompletionsToAdd() > 0;
171   }
172 
173   /// Returns the maximum number of completions that need to be added
174   /// until reaching the maximum
GetMaxNumberOfCompletionsToAdd()175   size_t GetMaxNumberOfCompletionsToAdd() const {
176     const size_t number_of_results = m_result.GetNumberOfResults();
177     if (number_of_results >= m_max_return_elements)
178       return 0;
179     return m_max_return_elements - number_of_results;
180   }
181 
182   /// Adds a possible completion string. If the completion was already
183   /// suggested before, it will not be added to the list of results. A copy of
184   /// the suggested completion is stored, so the given string can be free'd
185   /// afterwards.
186   ///
187   /// \param completion The suggested completion.
188   /// \param description An optional description of the completion string. The
189   ///     description will be displayed to the user alongside the completion.
190   /// \param mode The CompletionMode for this completion.
191   void AddCompletion(llvm::StringRef completion,
192                      llvm::StringRef description = "",
193                      CompletionMode mode = CompletionMode::Normal) {
194     m_result.AddResult(completion, description, mode);
195   }
196 
197   /// Adds a possible completion string if the completion would complete the
198   /// current argument.
199   ///
200   /// \param completion The suggested completion.
201   /// \param description An optional description of the completion string. The
202   ///     description will be displayed to the user alongside the completion.
203   template <CompletionMode M = CompletionMode::Normal>
204   void TryCompleteCurrentArg(llvm::StringRef completion,
205                              llvm::StringRef description = "") {
206     // Trying to rewrite the whole line while checking for the current
207     // argument never makes sense. Completion modes are always hardcoded, so
208     // this can be a static_assert.
209     static_assert(M != CompletionMode::RewriteLine,
210                   "Shouldn't rewrite line with this function");
211     if (completion.starts_with(GetCursorArgumentPrefix()))
212       AddCompletion(completion, description, M);
213   }
214 
215   /// Adds multiple possible completion strings.
216   ///
217   /// \param completions The list of completions.
218   ///
219   /// \see AddCompletion
AddCompletions(const StringList & completions)220   void AddCompletions(const StringList &completions) {
221     for (const std::string &completion : completions)
222       AddCompletion(completion);
223   }
224 
225   /// Adds multiple possible completion strings alongside their descriptions.
226   ///
227   /// The number of completions and descriptions must be identical.
228   ///
229   /// \param completions The list of completions.
230   /// \param descriptions The list of descriptions.
231   ///
232   /// \see AddCompletion
AddCompletions(const StringList & completions,const StringList & descriptions)233   void AddCompletions(const StringList &completions,
234                       const StringList &descriptions) {
235     lldbassert(completions.GetSize() == descriptions.GetSize());
236     for (std::size_t i = 0; i < completions.GetSize(); ++i)
237       AddCompletion(completions.GetStringAtIndex(i),
238                     descriptions.GetStringAtIndex(i));
239   }
240 
GetCursorArgumentPrefix()241   llvm::StringRef GetCursorArgumentPrefix() const {
242     return GetParsedLine().GetArgumentAtIndex(GetCursorIndex());
243   }
244 
245 private:
246   /// The raw command line we are supposed to complete.
247   llvm::StringRef m_command;
248   /// The cursor position in m_command.
249   unsigned m_raw_cursor_pos;
250   /// The command line parsed as arguments.
251   Args m_parsed_line;
252   /// The index of the argument in which the completion cursor is.
253   size_t m_cursor_index;
254   /// The cursor position in the argument indexed by m_cursor_index.
255   size_t m_cursor_char_position;
256   /// The maximum number of completions that should be returned.
257   size_t m_max_return_elements = std::numeric_limits<size_t>::max();
258 
259   /// The result this request is supposed to fill out.
260   /// We keep this object private to ensure that no backend can in any way
261   /// depend on already calculated completions (which would make debugging and
262   /// testing them much more complicated).
263   CompletionResult &m_result;
264 };
265 
266 } // namespace lldb_private
267 
268 #endif // LLDB_UTILITY_COMPLETIONREQUEST_H
269