int CommandObjectMultiword::HandleCompletion(Args &input, int &cursor_index,
                                             int &cursor_char_position,
                                             int match_start_point,
                                             int max_return_elements,
                                             bool &word_complete,
                                             StringList &matches) {
  // Any of the command matches will provide a complete word, otherwise the
  // individual
  // completers will override this.
  word_complete = true;

  const char *arg0 = input.GetArgumentAtIndex(0);
  if (cursor_index == 0) {
    AddNamesMatchingPartialString(m_subcommand_dict, arg0, matches);

    if (matches.GetSize() == 1 && matches.GetStringAtIndex(0) != nullptr &&
        strcmp(arg0, matches.GetStringAtIndex(0)) == 0) {
      StringList temp_matches;
      CommandObject *cmd_obj = GetSubcommandObject(arg0, &temp_matches);
      if (cmd_obj != nullptr) {
        if (input.GetArgumentCount() == 1) {
          word_complete = true;
        } else {
          matches.DeleteStringAtIndex(0);
          input.Shift();
          cursor_char_position = 0;
          input.AppendArgument("");
          return cmd_obj->HandleCompletion(
              input, cursor_index, cursor_char_position, match_start_point,
              max_return_elements, word_complete, matches);
        }
      }
    }
    return matches.GetSize();
  } else {
    CommandObject *sub_command_object = GetSubcommandObject(arg0, &matches);
    if (sub_command_object == nullptr) {
      return matches.GetSize();
    } else {
      // Remove the one match that we got from calling GetSubcommandObject.
      matches.DeleteStringAtIndex(0);
      input.Shift();
      cursor_index--;
      return sub_command_object->HandleCompletion(
          input, cursor_index, cursor_char_position, match_start_point,
          max_return_elements, word_complete, matches);
    }
  }
}
bool CommandObjectMultiword::Execute(const char *args_string,
                                     CommandReturnObject &result) {
  Args args(args_string);
  const size_t argc = args.GetArgumentCount();
  if (argc == 0) {
    this->CommandObject::GenerateHelpText(result);
  } else {
    const char *sub_command = args.GetArgumentAtIndex(0);

    if (sub_command) {
      if (::strcasecmp(sub_command, "help") == 0) {
        this->CommandObject::GenerateHelpText(result);
      } else if (!m_subcommand_dict.empty()) {
        StringList matches;
        CommandObject *sub_cmd_obj = GetSubcommandObject(sub_command, &matches);
        if (sub_cmd_obj != nullptr) {
          // Now call CommandObject::Execute to process and options in
          // 'rest_of_line'.  From there
          // the command-specific version of Execute will be called, with the
          // processed arguments.

          args.Shift();

          sub_cmd_obj->Execute(args_string, result);
        } else {
          std::string error_msg;
          const size_t num_subcmd_matches = matches.GetSize();
          if (num_subcmd_matches > 0)
            error_msg.assign("ambiguous command ");
          else
            error_msg.assign("invalid command ");

          error_msg.append("'");
          error_msg.append(GetCommandName());
          error_msg.append(" ");
          error_msg.append(sub_command);
          error_msg.append("'.");

          if (num_subcmd_matches > 0) {
            error_msg.append(" Possible completions:");
            for (size_t i = 0; i < num_subcmd_matches; i++) {
              error_msg.append("\n\t");
              error_msg.append(matches.GetStringAtIndex(i));
            }
          }
          error_msg.append("\n");
          result.AppendRawError(error_msg.c_str());
          result.SetStatus(eReturnStatusFailed);
        }
      } else {
        result.AppendErrorWithFormat("'%s' does not have any subcommands.\n",
                                     GetCommandName());
        result.SetStatus(eReturnStatusFailed);
      }
    }
  }

  return result.Succeeded();
}
Example #3
0
void
StringList::AppendList (StringList strings)
{
    size_t len = strings.GetSize();

    for (size_t i = 0; i < len; ++i)
        m_strings.push_back (strings.GetStringAtIndex(i));
}
Example #4
0
File: REPL.cpp Project: kraj/lldb
bool REPL::IOHandlerIsInputComplete(IOHandler &io_handler, StringList &lines) {
  // Check for meta command
  const size_t num_lines = lines.GetSize();
  if (num_lines == 1) {
    const char *first_line = lines.GetStringAtIndex(0);
    if (first_line[0] == ':')
      return true; // Meta command is a single line where that starts with ':'
  }

  // Check if REPL input is done
  std::string source_string(lines.CopyList());
  return SourceIsComplete(source_string);
}
TEST(CompletionRequest, TestCompletionOwnership) {
  std::string command = "a bad c";
  const unsigned cursor_pos = 3;
  StringList matches;

  CompletionResult result;
  CompletionRequest request(command, cursor_pos, 0, 0, result);

  std::string Temporary = "bar";
  request.AddCompletion(Temporary);
  // Manipulate our completion. The request should have taken a copy, so that
  // shouldn't influence anything.
  Temporary[0] = 'f';

  result.GetMatches(matches);
  EXPECT_EQ(1U, request.GetNumberOfMatches());
  EXPECT_STREQ("bar", matches.GetStringAtIndex(0));
}
Example #6
0
SBStringList
SBDebugger::GetInternalVariableValue (const char *var_name, const char *debugger_instance_name)
{
    SBStringList ret_value;
    SettableVariableType var_type;
    Error err;

    UserSettingsControllerSP root_settings_controller = Debugger::GetSettingsController();

    StringList value = root_settings_controller->GetVariable (var_name, var_type, debugger_instance_name, err);
    
    if (err.Success())
    {
        for (unsigned i = 0; i != value.GetSize(); ++i)
            ret_value.AppendString (value.GetStringAtIndex(i));
    }
    else
    {
        ret_value.AppendString (err.AsCString());
    }


    return ret_value;
}
bool
CommandObjectHelp::DoExecute (Args& command, CommandReturnObject &result)
{
    CommandObject::CommandMap::iterator pos;
    CommandObject *cmd_obj;
    const size_t argc = command.GetArgumentCount ();
    
    // 'help' doesn't take any arguments, other than command names.  If argc is 0, we show the user
    // all commands (aliases and user commands if asked for).  Otherwise every argument must be the name of a command or a sub-command.
    if (argc == 0)
    {
        uint32_t cmd_types = CommandInterpreter::eCommandTypesBuiltin;
        if (m_options.m_show_aliases)
            cmd_types |= CommandInterpreter::eCommandTypesAliases;
        if (m_options.m_show_user_defined)
            cmd_types |= CommandInterpreter::eCommandTypesUserDef;
        if (m_options.m_show_hidden)
            cmd_types |= CommandInterpreter::eCommandTypesHidden;

        result.SetStatus (eReturnStatusSuccessFinishNoResult);
        m_interpreter.GetHelp (result, cmd_types);  // General help
    }
    else
    {
        // Get command object for the first command argument. Only search built-in command dictionary.
        StringList matches;
        cmd_obj = m_interpreter.GetCommandObject (command.GetArgumentAtIndex (0), &matches);
        bool is_alias_command = m_interpreter.AliasExists (command.GetArgumentAtIndex (0));
        std::string alias_name = command.GetArgumentAtIndex(0);
        
        if (cmd_obj != nullptr)
        {
            StringList matches;
            bool all_okay = true;
            CommandObject *sub_cmd_obj = cmd_obj;
            // Loop down through sub_command dictionaries until we find the command object that corresponds
            // to the help command entered.
            std::string sub_command;
            for (size_t i = 1; i < argc && all_okay; ++i)
            {
                sub_command = command.GetArgumentAtIndex(i);
                matches.Clear();
                if (sub_cmd_obj->IsAlias())
                    sub_cmd_obj = ((CommandAlias*)sub_cmd_obj)->GetUnderlyingCommand().get();
                if (! sub_cmd_obj->IsMultiwordObject ())
                {
                    all_okay = false;
                }
                else
                {
                    CommandObject *found_cmd;
                    found_cmd = sub_cmd_obj->GetSubcommandObject(sub_command.c_str(), &matches);
                    if (found_cmd == nullptr)
                        all_okay = false;
                    else if (matches.GetSize() > 1)
                        all_okay = false;
                    else
                        sub_cmd_obj = found_cmd;
                }
            }
            
            if (!all_okay || (sub_cmd_obj == nullptr))
            {
                std::string cmd_string;
                command.GetCommandString (cmd_string);
                if (matches.GetSize() >= 2)
                {
                    StreamString s;
                    s.Printf ("ambiguous command %s", cmd_string.c_str());
                    size_t num_matches = matches.GetSize();
                    for (size_t match_idx = 0; match_idx < num_matches; match_idx++)
                    {
                        s.Printf ("\n\t%s", matches.GetStringAtIndex(match_idx));
                    }
                    s.Printf ("\n");
                    result.AppendError(s.GetData());
                    result.SetStatus (eReturnStatusFailed);
                    return false;
                }
                else if (!sub_cmd_obj)
                {
                    StreamString error_msg_stream;
                    GenerateAdditionalHelpAvenuesMessage(&error_msg_stream,
                                                         cmd_string.c_str(),
                                                         m_interpreter.GetCommandPrefix(),
                                                         sub_command.c_str());
                    result.AppendErrorWithFormat("%s",error_msg_stream.GetData());
                    result.SetStatus (eReturnStatusFailed);
                    return false;
                }
                else
                {
                    GenerateAdditionalHelpAvenuesMessage(&result.GetOutputStream(),
                                                         cmd_string.c_str(),
                                                         m_interpreter.GetCommandPrefix(),
                                                         sub_command.c_str());
                    result.GetOutputStream().Printf("\nThe closest match is '%s'. Help on it follows.\n\n", sub_cmd_obj->GetCommandName());
                }
            }
            
            sub_cmd_obj->GenerateHelpText(result);
            
            if (is_alias_command)
            {
                StreamString sstr;
                m_interpreter.GetAlias(alias_name.c_str())->GetAliasExpansion(sstr);
                result.GetOutputStream().Printf ("\n'%s' is an abbreviation for %s\n", alias_name.c_str(), sstr.GetData());
            }
        }
        else if (matches.GetSize() > 0)
        {
            Stream &output_strm = result.GetOutputStream();
            output_strm.Printf("Help requested with ambiguous command name, possible completions:\n");
            const size_t match_count = matches.GetSize();
            for (size_t i = 0; i < match_count; i++)
            {
                output_strm.Printf("\t%s\n", matches.GetStringAtIndex(i));
            }
        }
        else
        {
            // Maybe the user is asking for help about a command argument rather than a command.
            const CommandArgumentType arg_type = CommandObject::LookupArgumentName (command.GetArgumentAtIndex (0));
            if (arg_type != eArgTypeLastArg)
            {
                Stream &output_strm = result.GetOutputStream ();
                CommandObject::GetArgumentHelp (output_strm, arg_type, m_interpreter);
                result.SetStatus (eReturnStatusSuccessFinishNoResult);
            }
            else
            {
                StreamString error_msg_stream;
                GenerateAdditionalHelpAvenuesMessage(&error_msg_stream, command.GetArgumentAtIndex(0), m_interpreter.GetCommandPrefix());
                result.AppendErrorWithFormat("%s",error_msg_stream.GetData());
                result.SetStatus (eReturnStatusFailed);
            }
        }
    }
    
    return result.Succeeded();
}
Example #8
0
int
CommandInterpreter::HandleCompletionMatches (Args &parsed_line,
                                             int &cursor_index,
                                             int &cursor_char_position,
                                             int match_start_point,
                                             int max_return_elements,
                                             bool &word_complete,
                                             StringList &matches)
{
    int num_command_matches = 0;
    bool look_for_subcommand = false;
    
    // For any of the command completions a unique match will be a complete word.
    word_complete = true;

    if (cursor_index == -1)
    {
        // We got nothing on the command line, so return the list of commands
        bool include_aliases = true;
        num_command_matches = GetCommandNamesMatchingPartialString ("", include_aliases, matches);
    }
    else if (cursor_index == 0)
    {
        // The cursor is in the first argument, so just do a lookup in the dictionary.
        CommandObject *cmd_obj = GetCommandObject (parsed_line.GetArgumentAtIndex(0), &matches);
        num_command_matches = matches.GetSize();

        if (num_command_matches == 1
            && cmd_obj && cmd_obj->IsMultiwordObject()
            && matches.GetStringAtIndex(0) != NULL
            && strcmp (parsed_line.GetArgumentAtIndex(0), matches.GetStringAtIndex(0)) == 0)
        {
            look_for_subcommand = true;
            num_command_matches = 0;
            matches.DeleteStringAtIndex(0);
            parsed_line.AppendArgument ("");
            cursor_index++;
            cursor_char_position = 0;
        }
    }

    if (cursor_index > 0 || look_for_subcommand)
    {
        // We are completing further on into a commands arguments, so find the command and tell it
        // to complete the command.
        // First see if there is a matching initial command:
        CommandObject *command_object = GetCommandObject (parsed_line.GetArgumentAtIndex(0));
        if (command_object == NULL)
        {
            return 0;
        }
        else
        {
            parsed_line.Shift();
            cursor_index--;
            num_command_matches = command_object->HandleCompletion (*this,
                                                                    parsed_line, 
                                                                    cursor_index, 
                                                                    cursor_char_position,
                                                                    match_start_point, 
                                                                    max_return_elements,
                                                                    word_complete, 
                                                                    matches);
        }
    }

    return num_command_matches;

}
Example #9
0
bool
CommandInterpreter::HandleCommand 
(
    const char *command_line, 
    bool add_to_history,
    CommandReturnObject &result,
    ExecutionContext *override_context
)
{
    // FIXME: there should probably be a mutex to make sure only one thread can
    // run the interpreter at a time.

    // TODO: this should be a logging channel in lldb.
//    if (DebugSelf())
//    {
//        result.AppendMessageWithFormat ("Processing command: %s\n", command_line);
//    }

    m_debugger.UpdateExecutionContext (override_context);

    if (command_line == NULL || command_line[0] == '\0')
    {
        if (m_command_history.empty())
        {
            result.AppendError ("empty command");
            result.SetStatus(eReturnStatusFailed);
            return false;
        }
        else
        {
            command_line = m_repeat_command.c_str();
            if (m_repeat_command.empty())
            {
                result.AppendErrorWithFormat("No auto repeat.\n");
                result.SetStatus (eReturnStatusFailed);
                return false;
            }
        }
        add_to_history = false;
    }

    Args command_args(command_line);

    if (command_args.GetArgumentCount() > 0)
    {
        const char *command_cstr = command_args.GetArgumentAtIndex(0);
        if (command_cstr)
        {

            // We're looking up the command object here.  So first find an exact match to the
            // command in the commands.
            CommandObject *command_obj = GetCommandObject(command_cstr);
                
            if (command_obj != NULL)
            {
                if (command_obj->IsAlias())
                {
                    BuildAliasCommandArgs (command_obj, command_cstr, command_args, result);
                    if (!result.Succeeded())
                        return false;
                }

                if (add_to_history)
                {
                    const char *repeat_command = command_obj->GetRepeatCommand(command_args, 0);
                    if (repeat_command != NULL)
                        m_repeat_command.assign(repeat_command);
                    else
                        m_repeat_command.assign(command_line);
                        
                    m_command_history.push_back (command_line);
                }


                if (command_obj->WantsRawCommandString())
                {
                    const char *stripped_command = ::strstr (command_line, command_cstr);
                    if (stripped_command)
                    {
                        stripped_command += strlen(command_cstr);
                        while (isspace(*stripped_command))
                            ++stripped_command;
                        command_obj->ExecuteRawCommandString (*this, stripped_command, result);
                    }
                }
                else
                {
                    // Remove the command from the args.
                    command_args.Shift();
                    command_obj->ExecuteWithOptions (*this, command_args, result);
                }
            }
            else
            {
                // We didn't find the first command object, so complete the first argument.
                StringList matches;
                int num_matches;
                int cursor_index = 0;
                int cursor_char_position = strlen (command_args.GetArgumentAtIndex(0));
                bool word_complete;
                num_matches = HandleCompletionMatches (command_args, 
                                                       cursor_index,
                                                       cursor_char_position,
                                                       0, 
                                                       -1, 
                                                       word_complete,
                                                       matches);

                if (num_matches > 0)
                {
                    std::string error_msg;
                    error_msg.assign ("ambiguous command '");
                    error_msg.append(command_cstr);
                    error_msg.append ("'.");

                    error_msg.append (" Possible completions:");
                    for (int i = 0; i < num_matches; i++)
                    {
                        error_msg.append ("\n\t");
                        error_msg.append (matches.GetStringAtIndex (i));
                    }
                    error_msg.append ("\n");
                    result.AppendRawError (error_msg.c_str(), error_msg.size());
                }
                else
                    result.AppendErrorWithFormat ("Unrecognized command '%s'.\n", command_cstr);

                result.SetStatus (eReturnStatusFailed);
            }
        }
    }
    return result.Succeeded();
}
Example #10
0
bool
CommandObjectHelp::Execute (Args& command, CommandReturnObject &result)
{
    CommandObject::CommandMap::iterator pos;
    CommandObject *cmd_obj;
    const int argc = command.GetArgumentCount ();
    
    // 'help' doesn't take any options or arguments, other than command names.  If argc is 0, we show the user
    // all commands and aliases.  Otherwise every argument must be the name of a command or a sub-command.
    if (argc == 0)
    {
        result.SetStatus (eReturnStatusSuccessFinishNoResult);
        m_interpreter.GetHelp (result);  // General help, for ALL commands.
    }
    else
    {
        // Get command object for the first command argument. Only search built-in command dictionary.
        StringList matches;
        cmd_obj = m_interpreter.GetCommandObject (command.GetArgumentAtIndex (0), &matches);
        bool is_alias_command = m_interpreter.AliasExists (command.GetArgumentAtIndex (0));
        std::string alias_name = command.GetArgumentAtIndex(0);
        
        if (cmd_obj != NULL)
        {
            StringList matches;
            bool all_okay = true;
            CommandObject *sub_cmd_obj = cmd_obj;
            // Loop down through sub_command dictionaries until we find the command object that corresponds
            // to the help command entered.
            for (int i = 1; i < argc && all_okay; ++i)
            {
                std::string sub_command = command.GetArgumentAtIndex(i);
                matches.Clear();
                if (! sub_cmd_obj->IsMultiwordObject ())
                {
                    all_okay = false;
                }
                else
                {
                    CommandObject *found_cmd;
                    found_cmd = ((CommandObjectMultiword *) sub_cmd_obj)->GetSubcommandObject(sub_command.c_str(), 
                                                                                              &matches);
                    if (found_cmd == NULL)
                        all_okay = false;
                    else if (matches.GetSize() > 1)
                        all_okay = false;
                    else
                        sub_cmd_obj = found_cmd;
                }
            }
            
            if (!all_okay || (sub_cmd_obj == NULL))
            {
                std::string cmd_string;
                command.GetCommandString (cmd_string);
                if (matches.GetSize() < 2)
                {
                    result.AppendErrorWithFormat("'%s' is not a known command.\n"
                                                 "Try 'help' to see a current list of commands.\n",
                                                 cmd_string.c_str());
                }
                else 
                {
                    StreamString s;
                    s.Printf ("ambiguous command %s", cmd_string.c_str());
                    size_t num_matches = matches.GetSize();
                    for (size_t match_idx = 0; match_idx < num_matches; match_idx++)
                    {
                        s.Printf ("\n\t%s", matches.GetStringAtIndex(match_idx));
                    }
                    s.Printf ("\n");
                    result.AppendError(s.GetData());
                }

                result.SetStatus (eReturnStatusFailed);
            }
            else
            {
                Stream &output_strm = result.GetOutputStream();
                if (sub_cmd_obj->GetOptions() != NULL)
                {
                    if (sub_cmd_obj->WantsRawCommandString())
                    {
                        std::string help_text (sub_cmd_obj->GetHelp());
                        help_text.append ("  This command takes 'raw' input (no need to quote stuff).");
                        m_interpreter.OutputFormattedHelpText (output_strm, "", "", help_text.c_str(), 1);
                    }
                    else
                        m_interpreter.OutputFormattedHelpText (output_strm, "", "", sub_cmd_obj->GetHelp(), 1);
                    output_strm.Printf ("\nSyntax: %s\n", sub_cmd_obj->GetSyntax());
                    sub_cmd_obj->GetOptions()->GenerateOptionUsage (m_interpreter, output_strm, sub_cmd_obj);
                    const char *long_help = sub_cmd_obj->GetHelpLong();
                    if ((long_help != NULL)
                        && (strlen (long_help) > 0))
                        output_strm.Printf ("\n%s", long_help);
                    // Mark this help command with a success status.
                    if (sub_cmd_obj->WantsRawCommandString())
                    {
                        m_interpreter.OutputFormattedHelpText (output_strm, "", "", "\nIMPORTANT NOTE:  Because this command takes 'raw' input, if you use any command options you must use ' -- ' between the end of the command options and the beginning of the raw input.", 1);
                    }
                    result.SetStatus (eReturnStatusSuccessFinishNoResult);
                }
                else if (sub_cmd_obj->IsMultiwordObject())
                {
                    if (sub_cmd_obj->WantsRawCommandString())
                    {
                        std::string help_text (sub_cmd_obj->GetHelp());
                        help_text.append ("  This command takes 'raw' input (no need to quote stuff).");
                        m_interpreter.OutputFormattedHelpText (output_strm, "", "", help_text.c_str(), 1);
                    }
                    else
                        m_interpreter.OutputFormattedHelpText (output_strm, "", "", sub_cmd_obj->GetHelp(), 1);
                    ((CommandObjectMultiword *) sub_cmd_obj)->GenerateHelpText (result);
                }
                else
                {
                    const char *long_help = sub_cmd_obj->GetHelpLong();
                    if ((long_help != NULL)
                        && (strlen (long_help) > 0))
                        output_strm.Printf ("\n%s", long_help);
                    else if (sub_cmd_obj->WantsRawCommandString())
                    {
                        std::string help_text (sub_cmd_obj->GetHelp());
                        help_text.append ("  This command takes 'raw' input (no need to quote stuff).");
                        m_interpreter.OutputFormattedHelpText (output_strm, "", "", help_text.c_str(), 1);
                    }
                    else
                        m_interpreter.OutputFormattedHelpText (output_strm, "", "", sub_cmd_obj->GetHelp(), 1);
                    output_strm.Printf ("\nSyntax: %s\n", sub_cmd_obj->GetSyntax());
                    // Mark this help command with a success status.
                    result.SetStatus (eReturnStatusSuccessFinishNoResult);
                }
            }
            
            if (is_alias_command)
            {
                StreamString sstr;
                m_interpreter.GetAliasHelp (alias_name.c_str(), cmd_obj->GetCommandName(), sstr);
                result.GetOutputStream().Printf ("\n'%s' is an abbreviation for %s\n", alias_name.c_str(), sstr.GetData());
            }
        }
        else if (matches.GetSize() > 0)
        {
            Stream &output_strm = result.GetOutputStream();
            output_strm.Printf("Help requested with ambiguous command name, possible completions:\n");
            const uint32_t match_count = matches.GetSize();
            for (uint32_t i = 0; i < match_count; i++)
            {
                output_strm.Printf("\t%s\n", matches.GetStringAtIndex(i));
            }
        }
        else
        {
            // Maybe the user is asking for help about a command argument rather than a command.
            const CommandArgumentType arg_type = CommandObject::LookupArgumentName (command.GetArgumentAtIndex (0));
            if (arg_type != eArgTypeLastArg)
            {
                Stream &output_strm = result.GetOutputStream ();
                CommandObject::GetArgumentHelp (output_strm, arg_type, m_interpreter);
                result.SetStatus (eReturnStatusSuccessFinishNoResult);
            }
            else
            {
                result.AppendErrorWithFormat 
                    ("'%s' is not a known command.\nTry 'help' to see a current list of commands.\n",
                     command.GetArgumentAtIndex(0));
                result.SetStatus (eReturnStatusFailed);
            }
        }
    }
    
    return result.Succeeded();
}
TEST(CompletionRequest, DuplicateFiltering) {
  std::string command = "a bad c";
  const unsigned cursor_pos = 3;
  StringList matches;

  CompletionResult result;
  CompletionRequest request(command, cursor_pos, 0, 0, result);
  result.GetMatches(matches);

  EXPECT_EQ(0U, request.GetNumberOfMatches());

  // Add foo twice
  request.AddCompletion("foo");
  result.GetMatches(matches);

  EXPECT_EQ(1U, request.GetNumberOfMatches());
  EXPECT_EQ(1U, matches.GetSize());
  EXPECT_STREQ("foo", matches.GetStringAtIndex(0));

  request.AddCompletion("foo");
  result.GetMatches(matches);

  EXPECT_EQ(1U, request.GetNumberOfMatches());
  EXPECT_EQ(1U, matches.GetSize());
  EXPECT_STREQ("foo", matches.GetStringAtIndex(0));

  // Add bar twice
  request.AddCompletion("bar");
  result.GetMatches(matches);

  EXPECT_EQ(2U, request.GetNumberOfMatches());
  EXPECT_EQ(2U, matches.GetSize());
  EXPECT_STREQ("foo", matches.GetStringAtIndex(0));
  EXPECT_STREQ("bar", matches.GetStringAtIndex(1));

  request.AddCompletion("bar");
  result.GetMatches(matches);

  EXPECT_EQ(2U, request.GetNumberOfMatches());
  EXPECT_EQ(2U, matches.GetSize());
  EXPECT_STREQ("foo", matches.GetStringAtIndex(0));
  EXPECT_STREQ("bar", matches.GetStringAtIndex(1));

  // Add foo again.
  request.AddCompletion("foo");
  result.GetMatches(matches);

  EXPECT_EQ(2U, request.GetNumberOfMatches());
  EXPECT_EQ(2U, matches.GetSize());
  EXPECT_STREQ("foo", matches.GetStringAtIndex(0));
  EXPECT_STREQ("bar", matches.GetStringAtIndex(1));

  // Add something with an existing prefix
  request.AddCompletion("foobar");
  result.GetMatches(matches);

  EXPECT_EQ(3U, request.GetNumberOfMatches());
  EXPECT_EQ(3U, matches.GetSize());
  EXPECT_STREQ("foo", matches.GetStringAtIndex(0));
  EXPECT_STREQ("bar", matches.GetStringAtIndex(1));
  EXPECT_STREQ("foobar", matches.GetStringAtIndex(2));
}