A first pass at auto completion for variables and their children. This is currently hooked up for "frame variable" only. With a little work we can also enable it for the "expression" command and also for other things.

llvm-svn: 181850
This commit is contained in:
Greg Clayton 2013-05-14 23:43:18 +00:00
parent c750907218
commit f21feadcd9
7 changed files with 476 additions and 5 deletions

View File

@ -49,10 +49,11 @@ public:
eSettingsNameCompletion = (1u << 5),
ePlatformPluginCompletion = (1u << 6),
eArchitectureCompletion = (1u << 7),
eVariablePathCompletion = (1u << 8),
// This item serves two purposes. It is the last element in the enum,
// so you can add custom enums starting from here in your Option class.
// Also if you & in this bit the base code will not process the option.
eCustomCompletion = (1u << 8)
eCustomCompletion = (1u << 9)
} CommonCompletionTypes;
@ -145,7 +146,16 @@ public:
SearchFilter *searcher,
bool &word_complete,
lldb_private::StringList &matches);
static int
VariablePath (CommandInterpreter &interpreter,
const char *partial_file_name,
int match_start_point,
int max_return_elements,
SearchFilter *searcher,
bool &word_complete,
lldb_private::StringList &matches);
//----------------------------------------------------------------------
// The Completer class is a convenient base class for building searchers
// that go along with the SearchFilter passed to the standard Completer

View File

@ -76,6 +76,15 @@ public:
return m_ast;
}
static ClangASTType
GetCanonicalType (clang::ASTContext *ast, lldb::clang_type_t clang_type);
ClangASTType
GetCanonicalType ()
{
return GetCanonicalType (GetASTContext(), GetOpaqueQualType());
}
ConstString
GetConstTypeName ();
@ -137,6 +146,12 @@ public:
GetTypeClass (clang::ASTContext *ast_context,
lldb::clang_type_t clang_type);
lldb::TypeClass
GetTypeClass () const
{
return GetTypeClass (GetASTContext(), GetOpaqueQualType());
}
void
DumpValue (ExecutionContext *exe_ctx,
Stream *s,
@ -310,7 +325,7 @@ public:
StreamString &new_value);
lldb::clang_type_t
GetPointeeType ();
GetPointeeType () const;
static lldb::clang_type_t
GetPointeeType (lldb::clang_type_t opaque_clang_qual_type);

View File

@ -157,6 +157,12 @@ public:
VariableList &variable_list,
ValueObjectList &valobj_list);
static size_t
AutoComplete (const ExecutionContext &exe_ctx,
const char *name,
StringList &matches,
bool &word_complete);
protected:
ConstString m_name; // The basename of the variable (no namespaces)
Mangled m_mangled; // The mangled name of the variable

View File

@ -27,6 +27,7 @@
#include "lldb/Interpreter/CommandCompletions.h"
#include "lldb/Interpreter/CommandInterpreter.h"
#include "lldb/Symbol/CompileUnit.h"
#include "lldb/Symbol/Variable.h"
#include "lldb/Target/Target.h"
#include "lldb/Utility/CleanUp.h"
@ -44,6 +45,7 @@ CommandCompletions::g_common_completions[] =
{eSettingsNameCompletion, CommandCompletions::SettingsNames},
{ePlatformPluginCompletion, CommandCompletions::PlatformPluginNames},
{eArchitectureCompletion, CommandCompletions::ArchitectureNames},
{eVariablePathCompletion, CommandCompletions::VariablePath},
{eNoCompletion, NULL} // This one has to be last in the list.
};
@ -459,6 +461,19 @@ CommandCompletions::ArchitectureNames (CommandInterpreter &interpreter,
}
int
CommandCompletions::VariablePath (CommandInterpreter &interpreter,
const char *partial_name,
int match_start_point,
int max_return_elements,
SearchFilter *searcher,
bool &word_complete,
lldb_private::StringList &matches)
{
return Variable::AutoComplete (interpreter.GetExecutionContext(), partial_name, matches, word_complete);
}
CommandCompletions::Completer::Completer
(
CommandInterpreter &interpreter,

View File

@ -345,6 +345,32 @@ public:
{
return &m_option_group;
}
virtual int
HandleArgumentCompletion (Args &input,
int &cursor_index,
int &cursor_char_position,
OptionElementVector &opt_element_vector,
int match_start_point,
int max_return_elements,
bool &word_complete,
StringList &matches)
{
// Arguments are the standard source file completer.
std::string completion_str (input.GetArgumentAtIndex(cursor_index));
completion_str.erase (cursor_char_position);
CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter,
CommandCompletions::eVariablePathCompletion,
completion_str.c_str(),
match_start_point,
max_return_elements,
NULL,
word_complete,
matches);
return matches.GetSize();
}
protected:
virtual bool

View File

@ -79,6 +79,14 @@ ClangASTType::GetTypeNameForOpaqueQualType (clang::ASTContext *ast, clang_type_t
return GetTypeNameForQualType (ast, clang::QualType::getFromOpaquePtr(opaque_qual_type));
}
ClangASTType
ClangASTType::GetCanonicalType (clang::ASTContext *ast, lldb::clang_type_t opaque_qual_type)
{
if (ast && opaque_qual_type)
return ClangASTType (ast,
clang::QualType::getFromOpaquePtr(opaque_qual_type).getCanonicalType().getAsOpaquePtr());
return ClangASTType();
}
ConstString
ClangASTType::GetConstTypeName ()
@ -124,7 +132,7 @@ ClangASTType::GetConstTypeName (clang::ASTContext *ast, clang_type_t clang_type)
}
clang_type_t
ClangASTType::GetPointeeType ()
ClangASTType::GetPointeeType () const
{
return GetPointeeType (m_type);
}

View File

@ -516,6 +516,397 @@ Variable::DumpLocationForAddress (Stream *s, const Address &address)
}
}
return false;
}
static void
PrivateAutoComplete (StackFrame *frame,
const std::string &partial_path,
const std::string &prefix_path, // Anything that has been resolved already will be in here
const ClangASTType& clang_type,
StringList &matches,
bool &word_complete);
static void
PrivateAutoCompleteMembers (StackFrame *frame,
const std::string &partial_member_name,
const std::string &partial_path,
const std::string &prefix_path, // Anything that has been resolved already will be in here
const ClangASTType& clang_type,
StringList &matches,
bool &word_complete);
static void
PrivateAutoCompleteMembers (StackFrame *frame,
const std::string &partial_member_name,
const std::string &partial_path,
const std::string &prefix_path, // Anything that has been resolved already will be in here
const ClangASTType& clang_type,
StringList &matches,
bool &word_complete)
{
// We are in a type parsing child members
const uint32_t num_bases = ClangASTContext::GetNumDirectBaseClasses(clang_type.GetASTContext(),
clang_type.GetOpaqueQualType());
if (num_bases > 0)
{
for (uint32_t i = 0; i < num_bases; ++i)
{
ClangASTType base_class_type (clang_type.GetASTContext(),
ClangASTContext::GetDirectBaseClassAtIndex (clang_type.GetASTContext(),
clang_type.GetOpaqueQualType(),
i,
NULL));
PrivateAutoCompleteMembers (frame,
partial_member_name,
partial_path,
prefix_path,
base_class_type.GetCanonicalType(),
matches,
word_complete);
}
}
const uint32_t num_vbases = ClangASTContext::GetNumVirtualBaseClasses(clang_type.GetASTContext(),
clang_type.GetOpaqueQualType());
if (num_vbases > 0)
{
for (uint32_t i = 0; i < num_vbases; ++i)
{
ClangASTType vbase_class_type (clang_type.GetASTContext(),
ClangASTContext::GetVirtualBaseClassAtIndex(clang_type.GetASTContext(),
clang_type.GetOpaqueQualType(),
i,
NULL));
PrivateAutoCompleteMembers (frame,
partial_member_name,
partial_path,
prefix_path,
vbase_class_type.GetCanonicalType(),
matches,
word_complete);
}
}
// We are in a type parsing child members
const uint32_t num_fields = ClangASTContext::GetNumFields(clang_type.GetASTContext(),
clang_type.GetOpaqueQualType());
if (num_fields > 0)
{
for (uint32_t i = 0; i < num_fields; ++i)
{
std::string member_name;
lldb::clang_type_t member_type = ClangASTContext::GetFieldAtIndex (clang_type.GetASTContext(),
clang_type.GetOpaqueQualType(),
i,
member_name,
NULL,
NULL,
NULL);
if (partial_member_name.empty() ||
member_name.find(partial_member_name) == 0)
{
if (member_name == partial_member_name)
{
ClangASTType member_clang_type (clang_type.GetASTContext(), member_type);
PrivateAutoComplete (frame,
partial_path,
prefix_path + member_name, // Anything that has been resolved already will be in here
member_clang_type.GetCanonicalType(),
matches,
word_complete);
}
else
{
matches.AppendString (prefix_path + member_name);
}
}
}
}
}
static void
PrivateAutoComplete (StackFrame *frame,
const std::string &partial_path,
const std::string &prefix_path, // Anything that has been resolved already will be in here
const ClangASTType& clang_type,
StringList &matches,
bool &word_complete)
{
// printf ("\nPrivateAutoComplete()\n\tprefix_path = '%s'\n\tpartial_path = '%s'\n", prefix_path.c_str(), partial_path.c_str());
std::string remaining_partial_path;
const lldb::TypeClass type_class = clang_type.GetTypeClass();
if (partial_path.empty())
{
if (clang_type.IsValid())
{
switch (type_class)
{
default:
case eTypeClassArray:
case eTypeClassBlockPointer:
case eTypeClassBuiltin:
case eTypeClassComplexFloat:
case eTypeClassComplexInteger:
case eTypeClassEnumeration:
case eTypeClassFunction:
case eTypeClassMemberPointer:
case eTypeClassReference:
case eTypeClassTypedef:
case eTypeClassVector:
{
matches.AppendString (prefix_path);
word_complete = matches.GetSize() == 1;
}
break;
case eTypeClassClass:
case eTypeClassStruct:
case eTypeClassUnion:
if (prefix_path.back() != '.')
matches.AppendString (prefix_path + '.');
break;
case eTypeClassObjCObject:
case eTypeClassObjCInterface:
break;
case eTypeClassObjCObjectPointer:
case eTypeClassPointer:
{
bool omit_empty_base_classes = true;
if (ClangASTContext::GetNumChildren (clang_type.GetASTContext(), clang_type.GetPointeeType(), omit_empty_base_classes) > 0)
matches.AppendString (prefix_path + "->");
else
{
matches.AppendString (prefix_path);
word_complete = true;
}
}
break;
}
}
else
{
if (frame)
{
const bool get_file_globals = true;
VariableList *variable_list = frame->GetVariableList(get_file_globals);
const size_t num_variables = variable_list->GetSize();
for (size_t i=0; i<num_variables; ++i)
{
Variable *variable = variable_list->GetVariableAtIndex(i).get();
matches.AppendString (variable->GetName().AsCString());
}
}
}
}
else
{
const char ch = partial_path[0];
switch (ch)
{
case '*':
if (prefix_path.empty())
{
PrivateAutoComplete (frame,
partial_path.substr(1),
std::string("*"),
clang_type,
matches,
word_complete);
}
break;
case '&':
if (prefix_path.empty())
{
PrivateAutoComplete (frame,
partial_path.substr(1),
std::string("&"),
clang_type,
matches,
word_complete);
}
break;
case '-':
if (partial_path[1] == '>' && !prefix_path.empty())
{
switch (type_class)
{
case lldb::eTypeClassPointer:
{
ClangASTType pointee_type(clang_type.GetASTContext(), clang_type.GetPointeeType());
if (partial_path[2])
{
// If there is more after the "->", then search deeper
PrivateAutoComplete (frame,
partial_path.substr(2),
prefix_path + "->",
pointee_type.GetCanonicalType(),
matches,
word_complete);
}
else
{
// Nothing after the "->", so list all members
PrivateAutoCompleteMembers (frame,
std::string(),
std::string(),
prefix_path + "->",
pointee_type.GetCanonicalType(),
matches,
word_complete);
}
}
default:
break;
}
}
break;
case '.':
if (clang_type.IsValid())
{
switch (type_class)
{
case lldb::eTypeClassUnion:
case lldb::eTypeClassStruct:
case lldb::eTypeClassClass:
if (partial_path[1])
{
// If there is more after the ".", then search deeper
PrivateAutoComplete (frame,
partial_path.substr(1),
prefix_path + ".",
clang_type,
matches,
word_complete);
}
else
{
// Nothing after the ".", so list all members
PrivateAutoCompleteMembers (frame,
std::string(),
partial_path,
prefix_path + ".",
clang_type,
matches,
word_complete);
}
default:
break;
}
}
break;
default:
if (isalpha(ch) || ch == '_' || ch == '$')
{
const size_t partial_path_len = partial_path.size();
size_t pos = 1;
while (pos < partial_path_len)
{
const char curr_ch = partial_path[pos];
if (isalnum(curr_ch) || curr_ch == '_' || curr_ch == '$')
{
++pos;
continue;
}
break;
}
std::string token(partial_path, 0, pos);
remaining_partial_path = partial_path.substr(pos);
if (clang_type.IsValid())
{
PrivateAutoCompleteMembers (frame,
token,
remaining_partial_path,
prefix_path,
clang_type,
matches,
word_complete);
}
else if (frame)
{
// We haven't found our variable yet
const bool get_file_globals = true;
VariableList *variable_list = frame->GetVariableList(get_file_globals);
const size_t num_variables = variable_list->GetSize();
for (size_t i=0; i<num_variables; ++i)
{
Variable *variable = variable_list->GetVariableAtIndex(i).get();
const char *variable_name = variable->GetName().AsCString();
if (strstr(variable_name, token.c_str()) == variable_name)
{
if (strcmp (variable_name, token.c_str()) == 0)
{
Type *variable_type = variable->GetType();
if (variable_type)
{
ClangASTType variable_clang_type (variable_type->GetClangAST(), variable_type->GetClangForwardType());
PrivateAutoComplete (frame,
remaining_partial_path,
prefix_path + token, // Anything that has been resolved already will be in here
variable_clang_type.GetCanonicalType(),
matches,
word_complete);
}
else
{
matches.AppendString (prefix_path + variable_name);
}
}
else if (remaining_partial_path.empty())
{
matches.AppendString (prefix_path + variable_name);
}
}
}
}
}
break;
}
}
}
size_t
Variable::AutoComplete (const ExecutionContext &exe_ctx,
const char *partial_path_cstr,
StringList &matches,
bool &word_complete)
{
word_complete = false;
std::string partial_path;
std::string prefix_path;
ClangASTType clang_type;
if (partial_path_cstr && partial_path_cstr[0])
partial_path = partial_path_cstr;
PrivateAutoComplete (exe_ctx.GetFramePtr(),
partial_path,
prefix_path,
clang_type,
matches,
word_complete);
return matches.GetSize();
}