diff options
author | Pavel Labath <pavel@labath.sk> | 2024-05-30 09:57:13 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-05-30 09:57:13 +0200 |
commit | d554f2379b427505907ba52b8b9270c0b436224f (patch) | |
tree | 5de1b24a1bf67ae6f14ae7b18a7653564498c5b2 /lldb/source/Commands/CommandCompletions.cpp | |
parent | f38ebec7106fd541046d502be0f79a4dda1a89b0 (diff) | |
download | llvm-d554f2379b427505907ba52b8b9270c0b436224f.zip llvm-d554f2379b427505907ba52b8b9270c0b436224f.tar.gz llvm-d554f2379b427505907ba52b8b9270c0b436224f.tar.bz2 |
[lldb] Fix module name tab completion (#93458)
Module names can be matched either by a full path or just their
basename. The completion machinery tried to do both, but had several
bugs:
- it always inserted the basename as a completion candidate, even if the
string being completed was a full path
- due to FileSpec canonicalization, it lost information about trailing
slashes (it treated "lib/<TAB>" as "lib<TAB>", even though it's clear
the former was trying to complete a directory name)
- due to both of the previous issues, the completion candidates could
end up being shorter than the string being completed, which caused
crashes (string out of range errors) when attempting to substitute the
results.
This patch rewrites to logic to remove these kinds of issues:
- basename and full path completion are handled separately
- full path completion is attempted always, basename only if the input
string does not contain a slash
- the code remembers both the canonical and original spelling or the
completed argument. The canonical arg is used for matching, while the
original spelling is used for completion. This way "/foo///.//b<TAB>"
can still match "/foo/bar", but it will complete to "/foo///.//bar".
Diffstat (limited to 'lldb/source/Commands/CommandCompletions.cpp')
-rw-r--r-- | lldb/source/Commands/CommandCompletions.cpp | 57 |
1 files changed, 36 insertions, 21 deletions
diff --git a/lldb/source/Commands/CommandCompletions.cpp b/lldb/source/Commands/CommandCompletions.cpp index 16078a9..54f4b36 100644 --- a/lldb/source/Commands/CommandCompletions.cpp +++ b/lldb/source/Commands/CommandCompletions.cpp @@ -6,7 +6,9 @@ // //===----------------------------------------------------------------------===// +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSet.h" #include "lldb/Breakpoint/Watchpoint.h" @@ -262,9 +264,25 @@ class ModuleCompleter : public Completer { public: ModuleCompleter(CommandInterpreter &interpreter, CompletionRequest &request) : Completer(interpreter, request) { - FileSpec partial_spec(m_request.GetCursorArgumentPrefix()); - m_file_name = partial_spec.GetFilename().GetCString(); - m_dir_name = partial_spec.GetDirectory().GetCString(); + llvm::StringRef request_str = m_request.GetCursorArgumentPrefix(); + // We can match the full path, or the file name only. The full match will be + // attempted always, the file name match only if the request does not + // contain a path separator. + + // Preserve both the path as spelled by the user (used for completion) and + // the canonical version (used for matching). + m_spelled_path = request_str; + m_canonical_path = FileSpec(m_spelled_path).GetPath(); + if (!m_spelled_path.empty() && + llvm::sys::path::is_separator(m_spelled_path.back()) && + !llvm::StringRef(m_canonical_path).ends_with(m_spelled_path.back())) { + m_canonical_path += m_spelled_path.back(); + } + + if (llvm::find_if(request_str, [](char c) { + return llvm::sys::path::is_separator(c); + }) == request_str.end()) + m_file_name = request_str; } lldb::SearchDepth GetDepth() override { return lldb::eSearchDepthModule; } @@ -273,22 +291,18 @@ public: SymbolContext &context, Address *addr) override { if (context.module_sp) { - const char *cur_file_name = - context.module_sp->GetFileSpec().GetFilename().GetCString(); - const char *cur_dir_name = - context.module_sp->GetFileSpec().GetDirectory().GetCString(); - - bool match = false; - if (m_file_name && cur_file_name && - strstr(cur_file_name, m_file_name) == cur_file_name) - match = true; - - if (match && m_dir_name && cur_dir_name && - strstr(cur_dir_name, m_dir_name) != cur_dir_name) - match = false; - - if (match) { - m_request.AddCompletion(cur_file_name); + // Attempt a full path match. + std::string cur_path = context.module_sp->GetFileSpec().GetPath(); + llvm::StringRef cur_path_view = cur_path; + if (cur_path_view.consume_front(m_canonical_path)) + m_request.AddCompletion((m_spelled_path + cur_path_view).str()); + + // And a file name match. + if (m_file_name) { + llvm::StringRef cur_file_name = + context.module_sp->GetFileSpec().GetFilename().GetStringRef(); + if (cur_file_name.starts_with(*m_file_name)) + m_request.AddCompletion(cur_file_name); } } return Searcher::eCallbackReturnContinue; @@ -297,8 +311,9 @@ public: void DoCompletion(SearchFilter *filter) override { filter->Search(*this); } private: - const char *m_file_name; - const char *m_dir_name; + std::optional<llvm::StringRef> m_file_name; + llvm::StringRef m_spelled_path; + std::string m_canonical_path; ModuleCompleter(const ModuleCompleter &) = delete; const ModuleCompleter &operator=(const ModuleCompleter &) = delete; |