110 lines · cpp
1//===-- CompletionsHandler.cpp --------------------------------------------===//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "DAP.h"10#include "JSONUtils.h"11#include "Protocol/ProtocolRequests.h"12#include "Protocol/ProtocolTypes.h"13#include "RequestHandler.h"14#include "lldb/API/SBStringList.h"15 16using namespace llvm;17using namespace lldb_dap;18using namespace lldb_dap::protocol;19 20namespace lldb_dap {21 22/// Returns a list of possible completions for a given caret position and text.23///24/// Clients should only call this request if the corresponding capability25/// `supportsCompletionsRequest` is true.26Expected<CompletionsResponseBody>27CompletionsRequestHandler::Run(const CompletionsArguments &args) const {28 // If we have a frame, try to set the context for variable completions.29 lldb::SBFrame frame = dap.GetLLDBFrame(args.frameId);30 if (frame.IsValid()) {31 frame.GetThread().GetProcess().SetSelectedThread(frame.GetThread());32 frame.GetThread().SetSelectedFrame(frame.GetFrameID());33 }34 35 std::string text = args.text;36 auto original_column = args.column;37 auto original_line = args.line;38 auto offset = original_column - 1;39 if (original_line > 1) {40 SmallVector<StringRef, 2> lines;41 StringRef(text).split(lines, '\n');42 for (int i = 0; i < original_line - 1; i++) {43 offset += lines[i].size();44 }45 }46 47 std::vector<CompletionItem> targets;48 49 bool had_escape_prefix =50 StringRef(text).starts_with(dap.configuration.commandEscapePrefix);51 ReplMode completion_mode = dap.DetectReplMode(frame, text, true);52 53 // Handle the offset change introduced by stripping out the54 // `command_escape_prefix`.55 if (had_escape_prefix) {56 if (offset <57 static_cast<int64_t>(dap.configuration.commandEscapePrefix.size())) {58 return CompletionsResponseBody{std::move(targets)};59 }60 offset -= dap.configuration.commandEscapePrefix.size();61 }62 63 // While the user is typing then we likely have an incomplete input and cannot64 // reliably determine the precise intent (command vs variable), try completing65 // the text as both a command and variable expression, if applicable.66 const std::string expr_prefix = "expression -- ";67 std::array<std::tuple<ReplMode, std::string, uint64_t>, 2> exprs = {68 {std::make_tuple(ReplMode::Command, text, offset),69 std::make_tuple(ReplMode::Variable, expr_prefix + text,70 offset + expr_prefix.size())}};71 for (const auto &[mode, line, cursor] : exprs) {72 if (completion_mode != ReplMode::Auto && completion_mode != mode)73 continue;74 75 lldb::SBStringList matches;76 lldb::SBStringList descriptions;77 if (!dap.debugger.GetCommandInterpreter().HandleCompletionWithDescriptions(78 line.c_str(), cursor, 0, 100, matches, descriptions))79 continue;80 81 // The first element is the common substring after the cursor position for82 // all the matches. The rest of the elements are the matches so ignore the83 // first result.84 for (size_t i = 1; i < matches.GetSize(); i++) {85 std::string match = matches.GetStringAtIndex(i);86 std::string description = descriptions.GetStringAtIndex(i);87 88 CompletionItem item;89 StringRef match_ref = match;90 for (StringRef commit_point : {".", "->"}) {91 if (match_ref.contains(commit_point)) {92 match_ref = match_ref.rsplit(commit_point).second;93 }94 }95 item.text = match_ref;96 97 if (description.empty())98 item.label = match;99 else100 item.label = match + " -- " + description;101 102 targets.emplace_back(std::move(item));103 }104 }105 106 return CompletionsResponseBody{std::move(targets)};107}108 109} // namespace lldb_dap110