@@ -20,35 +20,46 @@ namespace {
20
20
std::vector<TextEdit>
21
21
replacementsToEdits (StringRef Code,
22
22
const std::vector<tooling::Replacement> &Replacements) {
23
- std::vector<TextEdit> Edits;
24
23
// Turn the replacements into the format specified by the Language Server
25
- // Protocol.
24
+ // Protocol. Fuse them into one big JSON array.
25
+ std::vector<TextEdit> Edits;
26
26
for (auto &R : Replacements) {
27
27
Range ReplacementRange = {
28
28
offsetToPosition (Code, R.getOffset ()),
29
29
offsetToPosition (Code, R.getOffset () + R.getLength ())};
30
30
Edits.push_back ({ReplacementRange, R.getReplacementText ()});
31
31
}
32
-
33
32
return Edits;
34
33
}
35
34
36
35
} // namespace
37
36
38
37
void ClangdLSPServer::onInitialize (Ctx C, InitializeParams &Params) {
39
- C.reply (
40
- R"( {"capabilities":{
41
- "textDocumentSync": 1,
42
- "documentFormattingProvider": true,
43
- "documentRangeFormattingProvider": true,
44
- "documentOnTypeFormattingProvider": {"firstTriggerCharacter":"}","moreTriggerCharacter":[]},
45
- "codeActionProvider": true,
46
- "completionProvider": {"resolveProvider": false, "triggerCharacters": [".",">",":"]},
47
- "signatureHelpProvider": {"triggerCharacters": ["(",","]},
48
- "definitionProvider": true,
49
- "executeCommandProvider": {"commands": [")" +
50
- ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND + R"( "]}
51
- }})" );
38
+ C.reply (json::obj{
39
+ {" textDocumentSync" , 1 },
40
+ {" documentFormattingProvider" , true },
41
+ {" documentRangeFormattingProvider" , true },
42
+ {" documentOnTypeFormattingProvider" ,
43
+ json::obj{
44
+ {" firstTriggerCharacter" , " }" },
45
+ {" moreTriggerCharacter" , {}},
46
+ }},
47
+ {" codeActionProvider" , true },
48
+ {" completionProvider" ,
49
+ json::obj{
50
+ {" resolveProvider" , false },
51
+ {" triggerCharacters" , {" ." , " >" , " :" }},
52
+ }},
53
+ {" signatureHelpProvider" ,
54
+ json::obj{
55
+ {" triggerCharacters" , {" (" , " ," }},
56
+ }},
57
+ {" definitionProvider" , true },
58
+ {" executeCommandProvider" ,
59
+ json::obj{
60
+ {" commands" , {ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND}},
61
+ }},
62
+ });
52
63
if (Params.rootUri && !Params.rootUri ->file .empty ())
53
64
Server.setRootPath (Params.rootUri ->file );
54
65
else if (Params.rootPath && !Params.rootPath ->empty ())
@@ -58,7 +69,7 @@ void ClangdLSPServer::onInitialize(Ctx C, InitializeParams &Params) {
58
69
void ClangdLSPServer::onShutdown (Ctx C, ShutdownParams &Params) {
59
70
// Do essentially nothing, just say we're ready to exit.
60
71
ShutdownRequestReceived = true ;
61
- C.reply (" null " );
72
+ C.reply (nullptr );
62
73
}
63
74
64
75
void ClangdLSPServer::onExit (Ctx C, ExitParams &Params) { IsDone = true ; }
@@ -98,7 +109,7 @@ void ClangdLSPServer::onCommand(Ctx C, ExecuteCommandParams &Params) {
98
109
99
110
ApplyWorkspaceEditParams ApplyEdit;
100
111
ApplyEdit.edit = *Params.workspaceEdit ;
101
- C.reply (" \" Fix applied.\" " );
112
+ C.reply (" Fix applied." );
102
113
// We don't need the response so id == 1 is OK.
103
114
// Ideally, we would wait for the response and if there is no error, we
104
115
// would reply success/failure to the original RPC.
@@ -121,51 +132,45 @@ void ClangdLSPServer::onDocumentOnTypeFormatting(
121
132
Ctx C, DocumentOnTypeFormattingParams &Params) {
122
133
auto File = Params.textDocument .uri .file ;
123
134
std::string Code = Server.getDocument (File);
124
- std::string Edits = TextEdit::unparse (
125
- replacementsToEdits (Code, Server.formatOnType (File, Params.position )));
126
- C.reply (Edits);
135
+ C.reply (json::ary (
136
+ replacementsToEdits (Code, Server.formatOnType (File, Params.position ))));
127
137
}
128
138
129
139
void ClangdLSPServer::onDocumentRangeFormatting (
130
140
Ctx C, DocumentRangeFormattingParams &Params) {
131
141
auto File = Params.textDocument .uri .file ;
132
142
std::string Code = Server.getDocument (File);
133
- std::string Edits = TextEdit::unparse (
134
- replacementsToEdits (Code, Server.formatRange (File, Params.range )));
135
- C.reply (Edits);
143
+ C.reply (json::ary (
144
+ replacementsToEdits (Code, Server.formatRange (File, Params.range ))));
136
145
}
137
146
138
147
void ClangdLSPServer::onDocumentFormatting (Ctx C,
139
148
DocumentFormattingParams &Params) {
140
149
auto File = Params.textDocument .uri .file ;
141
150
std::string Code = Server.getDocument (File);
142
- std::string Edits =
143
- TextEdit::unparse (replacementsToEdits (Code, Server.formatFile (File)));
144
- C.reply (Edits);
151
+ C.reply (json::ary (replacementsToEdits (Code, Server.formatFile (File))));
145
152
}
146
153
147
154
void ClangdLSPServer::onCodeAction (Ctx C, CodeActionParams &Params) {
148
155
// We provide a code action for each diagnostic at the requested location
149
156
// which has FixIts available.
150
157
std::string Code = Server.getDocument (Params.textDocument .uri .file );
151
- std::string Commands;
158
+ json::ary Commands;
152
159
for (Diagnostic &D : Params.context .diagnostics ) {
153
160
std::vector<clang::tooling::Replacement> Fixes =
154
161
getFixIts (Params.textDocument .uri .file , D);
155
162
auto Edits = replacementsToEdits (Code, Fixes);
156
- WorkspaceEdit WE;
157
- WE. changes = {{ llvm::yaml::escape (Params. textDocument . uri . uri ), Edits}} ;
158
-
159
- if (!Edits. empty ())
160
- Commands +=
161
- R"( {"title":"Apply FixIt ' )" + llvm::yaml::escape (D. message ) +
162
- R"( ' ", "command": " )" +
163
- ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND +
164
- R"( ", "arguments": [ )" + WorkspaceEdit::unparse (WE) + R"( ]}, )" ;
163
+ if (!Edits. empty ()) {
164
+ WorkspaceEdit WE ;
165
+ WE. changes = {{Params. textDocument . uri . uri , std::move (Edits)}};
166
+ Commands. push_back (json::obj{
167
+ { " title " , llvm::formatv ( " Apply FixIt {0} " , D. message )},
168
+ { " command " , ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND},
169
+ { " arguments " , {WE}},
170
+ });
171
+ }
165
172
}
166
- if (!Commands.empty ())
167
- Commands.pop_back ();
168
- C.reply (" [" + Commands + " ]" );
173
+ C.reply (std::move (Commands));
169
174
}
170
175
171
176
void ClangdLSPServer::onCompletion (Ctx C, TextDocumentPositionParams &Params) {
@@ -177,15 +182,7 @@ void ClangdLSPServer::onCompletion(Ctx C, TextDocumentPositionParams &Params) {
177
182
// had an API that would allow to attach callbacks to
178
183
// futures returned by ClangdServer.
179
184
.Value ;
180
-
181
- std::string Completions;
182
- for (const auto &Item : Items) {
183
- Completions += CompletionItem::unparse (Item);
184
- Completions += " ," ;
185
- }
186
- if (!Completions.empty ())
187
- Completions.pop_back ();
188
- C.reply (" [" + Completions + " ]" );
185
+ C.reply (json::ary (Items));
189
186
}
190
187
191
188
void ClangdLSPServer::onSignatureHelp (Ctx C,
@@ -195,7 +192,7 @@ void ClangdLSPServer::onSignatureHelp(Ctx C,
195
192
Position{Params.position .line , Params.position .character });
196
193
if (!SignatureHelp)
197
194
return C.replyError (-32602 , llvm::toString (SignatureHelp.takeError ()));
198
- C.reply (SignatureHelp::unparse (SignatureHelp ->Value ) );
195
+ C.reply (SignatureHelp->Value );
199
196
}
200
197
201
198
void ClangdLSPServer::onGoToDefinition (Ctx C,
@@ -205,22 +202,14 @@ void ClangdLSPServer::onGoToDefinition(Ctx C,
205
202
Position{Params.position .line , Params.position .character });
206
203
if (!Items)
207
204
return C.replyError (-32602 , llvm::toString (Items.takeError ()));
208
-
209
- std::string Locations;
210
- for (const auto &Item : Items->Value ) {
211
- Locations += Location::unparse (Item);
212
- Locations += " ," ;
213
- }
214
- if (!Locations.empty ())
215
- Locations.pop_back ();
216
- C.reply (" [" + Locations + " ]" );
205
+ C.reply (json::ary (Items->Value ));
217
206
}
218
207
219
208
void ClangdLSPServer::onSwitchSourceHeader (Ctx C,
220
209
TextDocumentIdentifier &Params) {
221
210
llvm::Optional<Path> Result = Server.switchSourceHeader (Params.uri .file );
222
211
std::string ResultUri;
223
- C.reply (Result ? URI::unparse ( URI:: fromFile (*Result)) : R"( "" ) " );
212
+ C.reply (Result ? URI::fromFile (*Result). uri : " " );
224
213
}
225
214
226
215
ClangdLSPServer::ClangdLSPServer (JSONOutput &Out, unsigned AsyncThreadsCount,
@@ -270,17 +259,16 @@ ClangdLSPServer::getFixIts(StringRef File, const clangd::Diagnostic &D) {
270
259
271
260
void ClangdLSPServer::onDiagnosticsReady (
272
261
PathRef File, Tagged<std::vector<DiagWithFixIts>> Diagnostics) {
273
- std::string DiagnosticsJSON;
262
+ json::ary DiagnosticsJSON;
274
263
275
264
DiagnosticToReplacementMap LocalFixIts; // Temporary storage
276
265
for (auto &DiagWithFixes : Diagnostics.Value ) {
277
266
auto Diag = DiagWithFixes.Diag ;
278
- DiagnosticsJSON +=
279
- R"( {"range":)" + Range::unparse (Diag.range ) +
280
- R"( ,"severity":)" + std::to_string (Diag.severity ) +
281
- R"( ,"message":")" + llvm::yaml::escape (Diag.message ) +
282
- R"( "},)" ;
283
-
267
+ DiagnosticsJSON.push_back (json::obj{
268
+ {" range" , Diag.range },
269
+ {" severity" , Diag.severity },
270
+ {" message" , Diag.message },
271
+ });
284
272
// We convert to Replacements to become independent of the SourceManager.
285
273
auto &FixItsForDiagnostic = LocalFixIts[Diag];
286
274
std::copy (DiagWithFixes.FixIts .begin (), DiagWithFixes.FixIts .end (),
@@ -295,10 +283,13 @@ void ClangdLSPServer::onDiagnosticsReady(
295
283
}
296
284
297
285
// Publish diagnostics.
298
- if (!DiagnosticsJSON.empty ())
299
- DiagnosticsJSON.pop_back (); // Drop trailing comma.
300
- Out.writeMessage (
301
- R"( {"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":")" +
302
- URI::fromFile (File).uri + R"( ","diagnostics":[)" + DiagnosticsJSON +
303
- R"( ]}})" );
286
+ Out.writeMessage (json::obj{
287
+ {" jsonrpc" , " 2.0" },
288
+ {" method" , " textDocument/publishDiagnostics" },
289
+ {" params" ,
290
+ json::obj{
291
+ {" uri" , URI::fromFile (File)},
292
+ {" diagnostics" , std::move (DiagnosticsJSON)},
293
+ }},
294
+ });
304
295
}
0 commit comments