int SwiftREPL::CompleteCode(const std::string ¤t_code, lldb_private::StringList &matches) { //----------------------------------------------------------------------g // If we use the target's SwiftASTContext for completion, it reaaallly // slows down subsequent expressions. The compiler team doesn't have time // to fix this issue currently, so we need to work around it by making // our own copy of the AST and using this separate AST for completion. //---------------------------------------------------------------------- Error error; #define USE_SEPARATE_AST_FOR_COMPLETION #if defined(USE_SEPARATE_AST_FOR_COMPLETION) if (!m_swift_ast_sp) { SwiftASTContext *target_swift_ast = m_target.GetScratchSwiftASTContext(error); if (target_swift_ast) m_swift_ast_sp.reset (new SwiftASTContext(*target_swift_ast)); } SwiftASTContext *swift_ast = m_swift_ast_sp.get(); #else SwiftASTContext *swift_ast = m_target.GetScratchSwiftASTContext(error); #endif if (swift_ast) { swift::ASTContext *ast = swift_ast->GetASTContext(); swift::REPLCompletions completions; static ConstString g_repl_module_name("repl"); swift::ModuleDecl *repl_module = swift_ast->GetModule (g_repl_module_name, error); if (repl_module == NULL) { repl_module = swift_ast->CreateModule(g_repl_module_name, error); const swift::SourceFile::ImplicitModuleImportKind implicit_import_kind = swift::SourceFile::ImplicitModuleImportKind::Stdlib; llvm::Optional<unsigned> bufferID; swift::SourceFile *repl_source_file = new (*ast) swift::SourceFile(*repl_module, swift::SourceFileKind::REPL, bufferID, implicit_import_kind); repl_module->addFile(*repl_source_file); } if (repl_module) { swift::SourceFile &repl_source_file = repl_module->getMainSourceFile(swift::SourceFileKind::REPL); llvm::StringRef current_code_ref(current_code); completions.populate(repl_source_file, current_code_ref); llvm::StringRef root = completions.getRoot(); if (!root.empty()) { matches.AppendString(root.data(), root.size()); return 1; } // llvm::StringRef prev_stem = completions.getPreviousStem(); // llvm::StringRef next_stem = completions.getNextStem(); // printf ("\nroot: '%*s'", (int)root.size(), root.data()); // printf ("\nprev_stem: '%*s'", (int)prev_stem.size(), prev_stem.data()); // printf ("\nnext_stem: '%*s'", (int)next_stem.size(), next_stem.data()); // printf ("\nvalid: %i", completions.isValid()); // printf ("\nempty: %i", completions.isEmpty()); // printf ("\nunique: %i", completions.isUnique()); // Otherwise, advance through the completion state machine. const swift::CompletionState completion_state = completions.getState(); switch (completion_state) { case swift::CompletionState::CompletedRoot: { // We completed the root. Next step is to display the completion list. matches.AppendString(""); // Empty string to indicate no completion, // just display other strings that come after it llvm::ArrayRef<llvm::StringRef> llvm_matches = completions.getCompletionList(); for (const auto &llvm_match : llvm_matches) { if (!llvm_match.empty()) matches.AppendString(llvm_match.data(), llvm_match.size()); } // Don't include the empty string we appended above or we will display one // too many we need to return the magical value of one less than our actual matches. // TODO: modify all IOHandlerDelegate::IOHandlerComplete() to use a CompletionMatches // class that wraps up the "StringList matches;" along with other smarts so we don't // have to return magic values and incorrect sizes. return matches.GetSize() - 1; } break; case swift::CompletionState::DisplayedCompletionList: { // Complete the next completion stem in the cycle. llvm::StringRef stem = completions.getPreviousStem().InsertableString; matches.AppendString(stem.data(), stem.size()); } break; case swift::CompletionState::Empty: case swift::CompletionState::Unique: // We already provided a definitive completion--nothing else to do. break; case swift::CompletionState::Invalid: llvm_unreachable("got an invalid completion set?!"); } } } return matches.GetSize(); }
bool Options::HandleOptionCompletion( Args &input, OptionElementVector &opt_element_vector, int cursor_index, int char_pos, int match_start_point, int max_return_elements, CommandInterpreter &interpreter, bool &word_complete, lldb_private::StringList &matches) { word_complete = true; // For now we just scan the completions to see if the cursor position is in // an option or its argument. Otherwise we'll call HandleArgumentCompletion. // In the future we can use completion to validate options as well if we want. auto opt_defs = GetDefinitions(); std::string cur_opt_std_str(input.GetArgumentAtIndex(cursor_index)); cur_opt_std_str.erase(char_pos); const char *cur_opt_str = cur_opt_std_str.c_str(); for (size_t i = 0; i < opt_element_vector.size(); i++) { int opt_pos = opt_element_vector[i].opt_pos; int opt_arg_pos = opt_element_vector[i].opt_arg_pos; int opt_defs_index = opt_element_vector[i].opt_defs_index; if (opt_pos == cursor_index) { // We're completing the option itself. if (opt_defs_index == OptionArgElement::eBareDash) { // We're completing a bare dash. That means all options are open. // FIXME: We should scan the other options provided and only complete // options // within the option group they belong to. char opt_str[3] = {'-', 'a', '\0'}; for (auto &def : opt_defs) { if (!def.short_option) continue; opt_str[1] = def.short_option; matches.AppendString(opt_str); } return true; } else if (opt_defs_index == OptionArgElement::eBareDoubleDash) { std::string full_name("--"); for (auto &def : opt_defs) { if (!def.short_option) continue; full_name.erase(full_name.begin() + 2, full_name.end()); full_name.append(def.long_option); matches.AppendString(full_name.c_str()); } return true; } else if (opt_defs_index != OptionArgElement::eUnrecognizedArg) { // We recognized it, if it an incomplete long option, complete it anyway // (getopt_long_only is // happy with shortest unique string, but it's still a nice thing to // do.) Otherwise return // The string so the upper level code will know this is a full match and // add the " ". if (cur_opt_str && strlen(cur_opt_str) > 2 && cur_opt_str[0] == '-' && cur_opt_str[1] == '-' && strcmp(opt_defs[opt_defs_index].long_option, cur_opt_str) != 0) { std::string full_name("--"); full_name.append(opt_defs[opt_defs_index].long_option); matches.AppendString(full_name.c_str()); return true; } else { matches.AppendString(input.GetArgumentAtIndex(cursor_index)); return true; } } else { // FIXME - not handling wrong options yet: // Check to see if they are writing a long option & complete it. // I think we will only get in here if the long option table has two // elements // that are not unique up to this point. getopt_long_only does shortest // unique match // for long options already. if (cur_opt_str && strlen(cur_opt_str) > 2 && cur_opt_str[0] == '-' && cur_opt_str[1] == '-') { for (auto &def : opt_defs) { if (!def.long_option) continue; if (strstr(def.long_option, cur_opt_str + 2) == def.long_option) { std::string full_name("--"); full_name.append(def.long_option); // The options definitions table has duplicates because of the // way the grouping information is stored, so only add once. bool duplicate = false; for (size_t k = 0; k < matches.GetSize(); k++) { if (matches.GetStringAtIndex(k) == full_name) { duplicate = true; break; } } if (!duplicate) matches.AppendString(full_name.c_str()); } } } return true; } } else if (opt_arg_pos == cursor_index) { // Okay the cursor is on the completion of an argument. // See if it has a completion, otherwise return no matches. if (opt_defs_index != -1) { HandleOptionArgumentCompletion( input, cursor_index, strlen(input.GetArgumentAtIndex(cursor_index)), opt_element_vector, i, match_start_point, max_return_elements, interpreter, word_complete, matches); return true; } else { // No completion callback means no completions... return true; } } else { // Not the last element, keep going. continue; } } return false; }