Skip to content

Commit 63f1008

Browse files
ashgtida-viper
andauthored
[lldb-dap] Migrating 'completions' to structured types. (llvm#153317)
This migrates the CompletionHandler to structured types and adds a new CompletionItem and CompletionItemType to the general types. --------- Co-authored-by: Ebuka Ezike <yerimyah1@gmail.com>
1 parent 729701b commit 63f1008

File tree

9 files changed

+350
-153
lines changed

9 files changed

+350
-153
lines changed

lldb/tools/lldb-dap/DAP.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,9 @@ lldb::SBThread DAP::GetLLDBThread(const llvm::json::Object &arguments) {
566566
}
567567

568568
lldb::SBFrame DAP::GetLLDBFrame(uint64_t frame_id) {
569+
if (frame_id == LLDB_DAP_INVALID_FRAME_ID)
570+
return lldb::SBFrame();
571+
569572
lldb::SBProcess process = target.GetProcess();
570573
// Upper 32 bits is the thread index ID
571574
lldb::SBThread thread =
@@ -575,8 +578,8 @@ lldb::SBFrame DAP::GetLLDBFrame(uint64_t frame_id) {
575578
}
576579

577580
lldb::SBFrame DAP::GetLLDBFrame(const llvm::json::Object &arguments) {
578-
const auto frame_id =
579-
GetInteger<uint64_t>(arguments, "frameId").value_or(UINT64_MAX);
581+
const auto frame_id = GetInteger<uint64_t>(arguments, "frameId")
582+
.value_or(LLDB_DAP_INVALID_FRAME_ID);
580583
return GetLLDBFrame(frame_id);
581584
}
582585

lldb/tools/lldb-dap/Handler/CompletionsHandler.cpp

Lines changed: 29 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -8,167 +8,54 @@
88

99
#include "DAP.h"
1010
#include "JSONUtils.h"
11+
#include "Protocol/ProtocolRequests.h"
12+
#include "Protocol/ProtocolTypes.h"
1113
#include "RequestHandler.h"
1214
#include "lldb/API/SBStringList.h"
1315

14-
namespace lldb_dap {
16+
using namespace llvm;
17+
using namespace lldb_dap;
18+
using namespace lldb_dap::protocol;
1519

16-
// "CompletionsRequest": {
17-
// "allOf": [ { "$ref": "#/definitions/Request" }, {
18-
// "type": "object",
19-
// "description": "Returns a list of possible completions for a given caret
20-
// position and text.\nThe CompletionsRequest may only be called if the
21-
// 'supportsCompletionsRequest' capability exists and is true.",
22-
// "properties": {
23-
// "command": {
24-
// "type": "string",
25-
// "enum": [ "completions" ]
26-
// },
27-
// "arguments": {
28-
// "$ref": "#/definitions/CompletionsArguments"
29-
// }
30-
// },
31-
// "required": [ "command", "arguments" ]
32-
// }]
33-
// },
34-
// "CompletionsArguments": {
35-
// "type": "object",
36-
// "description": "Arguments for 'completions' request.",
37-
// "properties": {
38-
// "frameId": {
39-
// "type": "integer",
40-
// "description": "Returns completions in the scope of this stack frame.
41-
// If not specified, the completions are returned for the global scope."
42-
// },
43-
// "text": {
44-
// "type": "string",
45-
// "description": "One or more source lines. Typically this is the text a
46-
// user has typed into the debug console before he asked for completion."
47-
// },
48-
// "column": {
49-
// "type": "integer",
50-
// "description": "The character position for which to determine the
51-
// completion proposals."
52-
// },
53-
// "line": {
54-
// "type": "integer",
55-
// "description": "An optional line for which to determine the completion
56-
// proposals. If missing the first line of the text is assumed."
57-
// }
58-
// },
59-
// "required": [ "text", "column" ]
60-
// },
61-
// "CompletionsResponse": {
62-
// "allOf": [ { "$ref": "#/definitions/Response" }, {
63-
// "type": "object",
64-
// "description": "Response to 'completions' request.",
65-
// "properties": {
66-
// "body": {
67-
// "type": "object",
68-
// "properties": {
69-
// "targets": {
70-
// "type": "array",
71-
// "items": {
72-
// "$ref": "#/definitions/CompletionItem"
73-
// },
74-
// "description": "The possible completions for ."
75-
// }
76-
// },
77-
// "required": [ "targets" ]
78-
// }
79-
// },
80-
// "required": [ "body" ]
81-
// }]
82-
// },
83-
// "CompletionItem": {
84-
// "type": "object",
85-
// "description": "CompletionItems are the suggestions returned from the
86-
// CompletionsRequest.", "properties": {
87-
// "label": {
88-
// "type": "string",
89-
// "description": "The label of this completion item. By default this is
90-
// also the text that is inserted when selecting this completion."
91-
// },
92-
// "text": {
93-
// "type": "string",
94-
// "description": "If text is not falsy then it is inserted instead of the
95-
// label."
96-
// },
97-
// "sortText": {
98-
// "type": "string",
99-
// "description": "A string that should be used when comparing this item
100-
// with other items. When `falsy` the label is used."
101-
// },
102-
// "type": {
103-
// "$ref": "#/definitions/CompletionItemType",
104-
// "description": "The item's type. Typically the client uses this
105-
// information to render the item in the UI with an icon."
106-
// },
107-
// "start": {
108-
// "type": "integer",
109-
// "description": "This value determines the location (in the
110-
// CompletionsRequest's 'text' attribute) where the completion text is
111-
// added.\nIf missing the text is added at the location specified by the
112-
// CompletionsRequest's 'column' attribute."
113-
// },
114-
// "length": {
115-
// "type": "integer",
116-
// "description": "This value determines how many characters are
117-
// overwritten by the completion text.\nIf missing the value 0 is assumed
118-
// which results in the completion text being inserted."
119-
// }
120-
// },
121-
// "required": [ "label" ]
122-
// },
123-
// "CompletionItemType": {
124-
// "type": "string",
125-
// "description": "Some predefined types for the CompletionItem. Please note
126-
// that not all clients have specific icons for all of them.", "enum": [
127-
// "method", "function", "constructor", "field", "variable", "class",
128-
// "interface", "module", "property", "unit", "value", "enum", "keyword",
129-
// "snippet", "text", "color", "file", "reference", "customcolor" ]
130-
// }
131-
void CompletionsRequestHandler::operator()(
132-
const llvm::json::Object &request) const {
133-
llvm::json::Object response;
134-
FillResponse(request, response);
135-
llvm::json::Object body;
136-
const auto *arguments = request.getObject("arguments");
20+
namespace lldb_dap {
13721

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 capability
25+
/// `supportsCompletionsRequest` is true.
26+
Expected<CompletionsResponseBody>
27+
CompletionsRequestHandler::Run(const CompletionsArguments &args) const {
13828
// If we have a frame, try to set the context for variable completions.
139-
lldb::SBFrame frame = dap.GetLLDBFrame(*arguments);
29+
lldb::SBFrame frame = dap.GetLLDBFrame(args.frameId);
14030
if (frame.IsValid()) {
14131
frame.GetThread().GetProcess().SetSelectedThread(frame.GetThread());
14232
frame.GetThread().SetSelectedFrame(frame.GetFrameID());
14333
}
14434

145-
std::string text = GetString(arguments, "text").value_or("").str();
146-
auto original_column =
147-
GetInteger<int64_t>(arguments, "column").value_or(text.size());
148-
auto original_line = GetInteger<int64_t>(arguments, "line").value_or(1);
35+
std::string text = args.text;
36+
auto original_column = args.column;
37+
auto original_line = args.line;
14938
auto offset = original_column - 1;
15039
if (original_line > 1) {
151-
llvm::SmallVector<::llvm::StringRef, 2> lines;
152-
llvm::StringRef(text).split(lines, '\n');
40+
SmallVector<StringRef, 2> lines;
41+
StringRef(text).split(lines, '\n');
15342
for (int i = 0; i < original_line - 1; i++) {
15443
offset += lines[i].size();
15544
}
15645
}
157-
llvm::json::Array targets;
46+
47+
std::vector<CompletionItem> targets;
15848

15949
bool had_escape_prefix =
160-
llvm::StringRef(text).starts_with(dap.configuration.commandEscapePrefix);
50+
StringRef(text).starts_with(dap.configuration.commandEscapePrefix);
16151
ReplMode completion_mode = dap.DetectReplMode(frame, text, true);
16252

16353
// Handle the offset change introduced by stripping out the
16454
// `command_escape_prefix`.
16555
if (had_escape_prefix) {
16656
if (offset <
16757
static_cast<int64_t>(dap.configuration.commandEscapePrefix.size())) {
168-
body.try_emplace("targets", std::move(targets));
169-
response.try_emplace("body", std::move(body));
170-
dap.SendJSON(llvm::json::Value(std::move(response)));
171-
return;
58+
return CompletionsResponseBody{std::move(targets)};
17259
}
17360
offset -= dap.configuration.commandEscapePrefix.size();
17461
}
@@ -198,27 +85,25 @@ void CompletionsRequestHandler::operator()(
19885
std::string match = matches.GetStringAtIndex(i);
19986
std::string description = descriptions.GetStringAtIndex(i);
20087

201-
llvm::json::Object item;
202-
llvm::StringRef match_ref = match;
203-
for (llvm::StringRef commit_point : {".", "->"}) {
88+
CompletionItem item;
89+
StringRef match_ref = match;
90+
for (StringRef commit_point : {".", "->"}) {
20491
if (match_ref.contains(commit_point)) {
20592
match_ref = match_ref.rsplit(commit_point).second;
20693
}
20794
}
208-
EmplaceSafeString(item, "text", match_ref);
95+
item.text = match_ref;
20996

21097
if (description.empty())
211-
EmplaceSafeString(item, "label", match);
98+
item.label = match;
21299
else
213-
EmplaceSafeString(item, "label", match + " -- " + description);
100+
item.label = match + " -- " + description;
214101

215102
targets.emplace_back(std::move(item));
216103
}
217104
}
218105

219-
body.try_emplace("targets", std::move(targets));
220-
response.try_emplace("body", std::move(body));
221-
dap.SendJSON(llvm::json::Value(std::move(response)));
106+
return CompletionsResponseBody{std::move(targets)};
222107
}
223108

224109
} // namespace lldb_dap

lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ llvm::Expected<protocol::DataBreakpointInfoResponseBody>
2323
DataBreakpointInfoRequestHandler::Run(
2424
const protocol::DataBreakpointInfoArguments &args) const {
2525
protocol::DataBreakpointInfoResponseBody response;
26-
lldb::SBFrame frame = dap.GetLLDBFrame(args.frameId.value_or(UINT64_MAX));
26+
lldb::SBFrame frame = dap.GetLLDBFrame(args.frameId);
2727
lldb::SBValue variable = dap.variables.FindVariable(
2828
args.variablesReference.value_or(0), args.name);
2929
std::string addr, size;

lldb/tools/lldb-dap/Handler/RequestHandler.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -243,14 +243,17 @@ class BreakpointLocationsRequestHandler
243243
uint32_t end_line) const;
244244
};
245245

246-
class CompletionsRequestHandler : public LegacyRequestHandler {
246+
class CompletionsRequestHandler
247+
: public RequestHandler<protocol::CompletionsArguments,
248+
llvm::Expected<protocol::CompletionsResponseBody>> {
247249
public:
248-
using LegacyRequestHandler::LegacyRequestHandler;
250+
using RequestHandler::RequestHandler;
249251
static llvm::StringLiteral GetCommand() { return "completions"; }
250252
FeatureSet GetSupportedFeatures() const override {
251253
return {protocol::eAdapterFeatureCompletionsRequest};
252254
}
253-
void operator()(const llvm::json::Object &request) const override;
255+
llvm::Expected<protocol::CompletionsResponseBody>
256+
Run(const protocol::CompletionsArguments &args) const override;
254257
};
255258

256259
class ContinueRequestHandler

lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,17 @@ json::Value toJSON(const ContinueResponseBody &CRB) {
329329
return std::move(Body);
330330
}
331331

332+
bool fromJSON(const json::Value &Params, CompletionsArguments &CA,
333+
json::Path P) {
334+
json::ObjectMapper O(Params, P);
335+
return O && O.map("text", CA.text) && O.map("column", CA.column) &&
336+
O.mapOptional("frameId", CA.frameId) && O.mapOptional("line", CA.line);
337+
}
338+
339+
json::Value toJSON(const CompletionsResponseBody &CRB) {
340+
return json::Object{{"targets", CRB.targets}};
341+
}
342+
332343
bool fromJSON(const json::Value &Params, SetVariableArguments &SVA,
333344
json::Path P) {
334345
json::ObjectMapper O(Params, P);

lldb/tools/lldb-dap/Protocol/ProtocolRequests.h

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,8 @@ bool fromJSON(const llvm::json::Value &, LaunchRequestArguments &,
310310
using LaunchResponse = VoidResponse;
311311

312312
#define LLDB_DAP_INVALID_PORT -1
313+
/// An invalid 'frameId' default value.
314+
#define LLDB_DAP_INVALID_FRAME_ID UINT64_MAX
313315

314316
/// lldb-dap specific attach arguments.
315317
struct AttachRequestArguments {
@@ -376,6 +378,35 @@ struct ContinueResponseBody {
376378
};
377379
llvm::json::Value toJSON(const ContinueResponseBody &);
378380

381+
/// Arguments for `completions` request.
382+
struct CompletionsArguments {
383+
/// Returns completions in the scope of this stack frame. If not specified,
384+
/// the completions are returned for the global scope.
385+
uint64_t frameId = LLDB_DAP_INVALID_FRAME_ID;
386+
387+
/// One or more source lines. Typically this is the text users have typed into
388+
/// the debug console before they asked for completion.
389+
std::string text;
390+
391+
/// The position within `text` for which to determine the completion
392+
/// proposals. It is measured in UTF-16 code units and the client capability
393+
/// `columnsStartAt1` determines whether it is 0- or 1-based.
394+
int64_t column = 0;
395+
396+
/// A line for which to determine the completion proposals. If missing the
397+
/// first line of the text is assumed.
398+
int64_t line = 0;
399+
};
400+
bool fromJSON(const llvm::json::Value &, CompletionsArguments &,
401+
llvm::json::Path);
402+
403+
/// Response to `completions` request.
404+
struct CompletionsResponseBody {
405+
/// The possible completions for a given caret position and text.
406+
std::vector<CompletionItem> targets;
407+
};
408+
llvm::json::Value toJSON(const CompletionsResponseBody &);
409+
379410
/// Arguments for `configurationDone` request.
380411
using ConfigurationDoneArguments = EmptyArguments;
381412

@@ -455,7 +486,7 @@ struct ScopesArguments {
455486
/// Retrieve the scopes for the stack frame identified by `frameId`. The
456487
/// `frameId` must have been obtained in the current suspended state. See
457488
/// 'Lifetime of Object References' in the Overview section for details.
458-
uint64_t frameId = LLDB_INVALID_FRAME_ID;
489+
uint64_t frameId = LLDB_DAP_INVALID_FRAME_ID;
459490
};
460491
bool fromJSON(const llvm::json::Value &, ScopesArguments &, llvm::json::Path);
461492

@@ -541,7 +572,7 @@ using StepInResponse = VoidResponse;
541572
/// Arguments for `stepInTargets` request.
542573
struct StepInTargetsArguments {
543574
/// The stack frame for which to retrieve the possible step-in targets.
544-
uint64_t frameId = LLDB_INVALID_FRAME_ID;
575+
uint64_t frameId = LLDB_DAP_INVALID_FRAME_ID;
545576
};
546577
bool fromJSON(const llvm::json::Value &, StepInTargetsArguments &,
547578
llvm::json::Path);
@@ -690,7 +721,7 @@ struct DataBreakpointInfoArguments {
690721
/// When `name` is an expression, evaluate it in the scope of this stack
691722
/// frame. If not specified, the expression is evaluated in the global scope.
692723
/// When `asAddress` is true, the `frameId` is ignored.
693-
std::optional<uint64_t> frameId;
724+
uint64_t frameId = LLDB_DAP_INVALID_FRAME_ID;
694725

695726
/// If specified, a debug adapter should return information for the range of
696727
/// memory extending `bytes` number of bytes from the address or variable

0 commit comments

Comments
 (0)