@@ -135,19 +135,69 @@ export interface FormField {
135135 error ?: string ;
136136}
137137
138+ // InteractiveParams facilitates a multi-step, interactive dialogue between the
139+ // client and server during a Language Server Protocol (LSP) request.
140+ //
141+ // It implements a non-standard protocol extension microsoft/language-server-protocol#1164
142+ // . By embedding this type into standard request parameters (such as
143+ // [ExecuteCommandParams] or [RenameParams]) and pairing them with dedicated
144+ // resolution methods (like [Server.ResolveCommand] or other ResolveXXX handlers),
145+ // standard operations can be transformed into interactive workflows.
146+ //
147+ // Standard LSP resolution methods (like "codeAction/resolve") cannot be used
148+ // for these interactive forms because editors often trigger them eagerly to
149+ // render previews, which would prematurely present UI forms to the user.
150+ // The dedicated ResolveXXX pattern ensures the interactive dialogue strictly
151+ // begins only *after* the user has explicitly indicated intent (for example,
152+ // by clicking a specific Code Action).
153+ //
154+ // The following sequence illustrates the typical handshake, using a code action
155+ // that resolves to a command as an example:
156+ //
157+ // 1. The client requests code actions for the current text selection.
158+ // 2. The server responds with a code action containing a standard LSP Command
159+ // (title, command, and arguments).
160+ // 3. The client calls [Server.ResolveCommand] with the initial command details
161+ // wrapped in an [ExecuteCommandParams] to determine if the execution requires
162+ // interactive input.
163+ // 4. The server responds with an [ExecuteCommandParams]. If user input is
164+ // required, the server populates the FormFields array with the required schema.
165+ // 5. The client observes the non-empty FormFields and presents a corresponding
166+ // user interface.
167+ // 6. The user submits their input, and the client issues another
168+ // [Server.ResolveCommand] request, this time populating the FormAnswers array.
169+ // 7. The server validates the answers. If invalid, it returns a form with error
170+ // messages attached to specific FormFields. Steps 5-7 repeat until the server
171+ // omits FormFields entirely, indicating the answers are valid and complete.
172+ // 8. The client calls [Server.ExecuteCommand] with the finalized FormAnswers to
173+ // execute the action.
174+ //
175+ // The server populates FormFields to define the input schema. If FormFields is
176+ // omitted or empty, the interactive phase is considered complete and the provided
177+ // FormAnswers have been fully validated.
178+ //
179+ // The server may optionally populate FormAnswers alongside FormFields to preserve
180+ // previous user input or provide default values for the client to render.
138181export interface InteractiveParams {
139- /**
140- * FormFields defines the questions and validation errors.
141- * This is a server-to-client field.
142- */
182+ // FormFields defines the questions and validation errors in previous
183+ // answers to the same questions.
184+ //
185+ // This is a server-to-client field. The language server defines these, and
186+ // the client uses them to render the form.
187+ //
188+ // The interactive phase is considered complete when the server returns a
189+ // response where this slice is omitted.
143190 formFields ?: FormField [ ] ;
144191
145- /**
146- * FormAnswers contains the values for the form questions.
147- * When sent by the language server, this acts as preserved/previous input.
148- * When sent by the client (in a resolve request), this is required when
149- * formFields are defined.
150- */
192+ // FormAnswers contains the values for the form questions.
193+ //
194+ // When sent by the language server, this field is optional but recommended
195+ // to support editing previous values.
196+ //
197+ // When sent by the language client as part of the ResolveXXX request, this
198+ // field is required. The slice must have the same length as FormFields (one
199+ // answer per question), where the answer at index i corresponds to the
200+ // field at index i.
151201 formAnswers ?: any [ ] ;
152202}
153203
@@ -174,11 +224,27 @@ export interface InteractiveExecuteCommandParams extends InteractiveParams {
174224 */
175225const MAX_RETRY = 5 ;
176226
227+ // ResolveCommand handles the interactive resolution of a command prior to
228+ // its execution.
229+ //
230+ // It processes an [ExecuteCommandParams] to determine if the command requires
231+ // interactive input, or to validate user-provided answers submitted via the
232+ // embedded [InteractiveParams].
233+ //
234+ // If the command requires user input (e.g., the initial probe) or if the
235+ // provided answers are invalid, it returns a modified [ExecuteCommandParams]
236+ // populated with FormFields to prompt the user. If the input is valid and
237+ // complete, or if the command requires no interaction at all, it returns an
238+ // [ExecuteCommandParams] with an empty form, signaling the client to proceed
239+ // with execution.
240+ //
241+ // See [InteractiveParams] for the complete multi-step client-server handshake
242+ // and the architectural reasoning behind dedicated ResolveXXX methods.
177243export async function ResolveCommand (
178244 goCtx : GoExtensionContext ,
179245 command : string ,
180246 args : any [ ]
181- ) : Promise < { command : string ; args : any [ ] } | undefined > {
247+ ) : Promise < { command : string ; args : any [ ] ; formAnswers ?: any [ ] } | undefined > {
182248 // Avoid resolving for frequently triggered commands for performance.
183249 if ( command === 'gopls.package_symbols' ) {
184250 return { command : command , args : args } ;
@@ -220,7 +286,7 @@ export async function ResolveCommand(
220286
221287 param = response as InteractiveExecuteCommandParams ;
222288
223- // No information needed from the gopls .
289+ // "formAnswers" are validated by the language server .
224290 if ( param . formFields === undefined ) {
225291 break ;
226292 }
@@ -245,7 +311,7 @@ export async function ResolveCommand(
245311 param . formFields = undefined ;
246312 }
247313
248- return { command : param . command , args : param . arguments ? param . arguments : [ ] } ;
314+ return { command : param . command , args : param . arguments ? param . arguments : [ ] , formAnswers : param . formAnswers } ;
249315}
250316
251317/**
@@ -460,7 +526,7 @@ async function promptForField(field: FormField, prevAnswer: any | undefined): Pr
460526 if ( action . target === 'open' ) {
461527 const uri = await vscode . window . showOpenDialog ( {
462528 canSelectFiles : true ,
463- canSelectFolders : false ,
529+ canSelectFolders : true ,
464530 canSelectMany : false ,
465531 openLabel : 'Select' ,
466532 defaultUri : defaultUri ,
0 commit comments