Skip to content

Commit cd17659

Browse files
h9jianggopherbot
authored andcommitted
extension/src: extract all interactive LSP call as function
This CL explicitly extract all thee LSP calls as individual functions. Each function accept a client and arguments as input parameters. InteractiveExtractCommand function slightly different accepting "command, arguments, formAnswers" as input parameters to follow existing "executeCommand" signature accepting "command, arguments" All three LSP calls will have embedded error handling follow vscode-languageserver-node practice. Since all the interactive refactoring methods use plain text input, the "onfullfilled" can be left as "undefined". https://github.com/microsoft/vscode-languageserver-node/blob/49c79657d5bd51ec84562424a276a06b056ddd60/client/src/common/executeCommand.ts#L67 For golang/go#76331 Change-Id: Ifba739a90a3399feaed3076fbb8016811611fed4 Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/779880 LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Madeline Kalil <mkalil@google.com> Auto-Submit: Hongxiang Jiang <hxjiang@golang.org>
1 parent c21c1e4 commit cd17659

2 files changed

Lines changed: 81 additions & 42 deletions

File tree

extension/src/language/form.ts

Lines changed: 74 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*--------------------------------------------------------*/
66

77
import * as vscode from 'vscode';
8-
import { LanguageClient } from 'vscode-languageclient/node';
8+
import { LanguageClient, RequestType } from 'vscode-languageclient/node';
99

1010
// ----------------------------------------------------------------------------
1111
// Form Field Type Definitions
@@ -253,46 +253,24 @@ export interface InteractiveExecuteCommandParams extends InteractiveParams {
253253
*/
254254
const MAX_RETRY = 5;
255255

256-
// ResolveCommand handles the interactive resolution of a command prior to
257-
// its execution.
258-
//
259-
// It processes an [ExecuteCommandParams] to determine if the command requires
260-
// interactive input, or to validate user-provided answers submitted via the
261-
// embedded [InteractiveParams].
262-
//
263-
// If the command requires user input (e.g., the initial probe) or if the
264-
// provided answers are invalid, it returns a modified [ExecuteCommandParams]
265-
// populated with FormFields to prompt the user. If the input is valid and
266-
// complete, or if the command requires no interaction at all, it returns an
267-
// [ExecuteCommandParams] with an empty form, signaling the client to proceed
268-
// with execution.
269-
//
270-
// See [InteractiveParams] for the complete multi-step client-server handshake
271-
// and the architectural reasoning behind dedicated ResolveXXX methods.
272-
export async function ResolveCommand(
256+
export async function resolveCommandInteractively(
273257
languageClient: LanguageClient,
274-
command: string,
275-
args: any[]
276-
): Promise<{ command: string; args: any[]; formAnswers?: any[] } | undefined> {
258+
param: InteractiveExecuteCommandParams
259+
): Promise<InteractiveExecuteCommandParams | undefined> {
277260
// Avoid resolving for frequently triggered commands for performance.
278-
if (command === 'gopls.package_symbols') {
279-
return { command: command, args: args };
261+
if (param.command === 'gopls.package_symbols') {
262+
return param;
280263
}
281264

282-
let param = {
283-
command: command,
284-
arguments: args
285-
} as InteractiveExecuteCommandParams;
286-
287265
// Invoke "command/resolve" at least once to ensure the command
288266
// is fully specified, as the initial input may lack necessary parameters.
289267
for (let i = 0; i < MAX_RETRY; i++) {
290-
const response = await languageClient.sendRequest<InteractiveExecuteCommandParams>('command/resolve', param);
291-
if (!response) {
268+
const result = await ResolveCommand(languageClient, param);
269+
if (!result) {
292270
return undefined;
293271
}
294272

295-
param = response;
273+
param = result;
296274

297275
// "formAnswers" are validated by the language server.
298276
if (param.formFields === undefined) {
@@ -319,7 +297,69 @@ export async function ResolveCommand(
319297
param.formFields = undefined;
320298
}
321299

322-
return { command: param.command, args: param.arguments ? param.arguments : [], formAnswers: param.formAnswers };
300+
return param;
301+
}
302+
303+
// ResolveCommand handles the interactive resolution of a command prior to its
304+
// execution.
305+
//
306+
// It processes an [InteractiveExecuteCommandParams] to determine if the command
307+
// requires interactive input, or to validate user-provided answers submitted
308+
// via the embedded [InteractiveParams].
309+
//
310+
// If the command requires user input (e.g., the initial probe) or if the
311+
// provided answers are invalid, it returns a modified [InteractiveExecuteCommandParams]
312+
// populated with FormFields to prompt the user. If the input is valid and
313+
// complete, or if the command requires no interaction at all, it returns an
314+
// [InteractiveExecuteCommandParams] with an empty form, signaling the client to
315+
// proceed with execution.
316+
//
317+
// See [InteractiveParams] for the complete multi-step client-server handshake
318+
// and the architectural reasoning behind dedicated ResolveXXX methods.
319+
export async function ResolveCommand(
320+
languageClient: LanguageClient,
321+
param: InteractiveExecuteCommandParams
322+
): Promise<InteractiveExecuteCommandParams | undefined> {
323+
const requestType = new RequestType<InteractiveExecuteCommandParams, InteractiveExecuteCommandParams, void>(
324+
'command/resolve'
325+
);
326+
return languageClient
327+
.sendRequest<InteractiveExecuteCommandParams>('command/resolve', param)
328+
.then(undefined, (error) => {
329+
return languageClient.handleFailedRequest(requestType, undefined, error, undefined);
330+
});
331+
}
332+
333+
// Executes an LSP command with an extended payload containing interactive form
334+
// answers.
335+
export async function InteractiveExecuteCommand(
336+
languageClient: LanguageClient,
337+
command: string,
338+
args: any[],
339+
formAnswers: any[]
340+
): Promise<any> {
341+
const requestType = new RequestType<InteractiveExecuteCommandParams, any, void>('workspace/executeCommand');
342+
return languageClient
343+
.sendRequest('workspace/executeCommand', {
344+
command: command,
345+
arguments: args,
346+
formAnswers: formAnswers
347+
} as InteractiveExecuteCommandParams)
348+
.then(undefined, (error) => {
349+
return languageClient.handleFailedRequest(requestType, undefined, error, undefined);
350+
});
351+
}
352+
353+
// Queries the language server to dynamically retrieve enumeration entries for
354+
// interactive form fields of type 'lazyEnum'.
355+
export async function InteractiveListEnum(
356+
languageClient: LanguageClient,
357+
param: InteractiveListEnumParams
358+
): Promise<FormEnumEntry[] | undefined> {
359+
const requestType = new RequestType<InteractiveListEnumParams, FormEnumEntry[], void>('interactive/listEnum');
360+
return languageClient.sendRequest<FormEnumEntry[]>('interactive/listEnum', param).then(undefined, (error) => {
361+
return languageClient.handleFailedRequest(requestType, undefined, error, undefined);
362+
});
323363
}
324364

325365
/**
@@ -337,7 +377,7 @@ export async function ResolveCommand(
337377
* @returns An array of answers matching the order of fields, or undefined if
338378
* the user cancelled the process.
339379
*/
340-
export async function CollectAnswers(
380+
async function CollectAnswers(
341381
languageClient: LanguageClient,
342382
formFields: FormField[] | undefined,
343383
formAnswers: any[] | undefined
@@ -432,7 +472,7 @@ export async function pickLazyEnum(
432472
config: config,
433473
query: query
434474
};
435-
const response = await languageClient?.sendRequest<FormEnumEntry[]>('interactive/listEnum', params);
475+
const response = await InteractiveListEnum(languageClient, params);
436476

437477
if (!response) {
438478
quickPick.items = [];

extension/src/language/goLanguageServer.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ import { GoDocumentSelector } from '../goMode';
6969
import { COMMAND as GOPLS_ADD_TEST_COMMAND } from '../goGenerateTests';
7070
import { COMMAND as GOPLS_MODIFY_TAGS_COMMAND } from '../goModifytags';
7171
import { TelemetryKey, telemetryReporter } from '../goTelemetry';
72-
import { InteractiveExecuteCommandParams, ResolveCommand } from './form';
72+
import { InteractiveExecuteCommand, InteractiveExecuteCommandParams, resolveCommandInteractively } from './form';
7373

7474
export interface LanguageServerConfig {
7575
serverName: string;
@@ -569,14 +569,17 @@ export async function buildLanguageClient(
569569
let formAnswers: any[] | undefined;
570570
const supported = c.initializeResult?.capabilities?.experimental?.interactiveResolveProvider;
571571
if (goCtx.languageClient && Array.isArray(supported) && supported.includes('command')) {
572-
const resolved = await ResolveCommand(goCtx.languageClient, command, args);
572+
const resolved = await resolveCommandInteractively(goCtx.languageClient, {
573+
command: command,
574+
arguments: args
575+
} as InteractiveExecuteCommandParams);
573576
if (!resolved) {
574577
return undefined;
575578
}
576579

577580
// Replace original command and result with resolved command and args.
578581
command = resolved.command;
579-
args = resolved.args;
582+
args = resolved.arguments || [];
580583
formAnswers = resolved.formAnswers;
581584
}
582585

@@ -603,11 +606,7 @@ export async function buildLanguageClient(
603606
if (formAnswers === undefined || formAnswers.length === 0) {
604607
res = await next(command, args);
605608
} else {
606-
res = await c.sendRequest('workspace/executeCommand', {
607-
command: command,
608-
arguments: args,
609-
formAnswers: formAnswers
610-
} as InteractiveExecuteCommandParams);
609+
res = await InteractiveExecuteCommand(goCtx.languageClient!, command, args, formAnswers);
611610
}
612611

613612
const progressToken = res?.Token as ProgressToken;

0 commit comments

Comments
 (0)