extension/src/language: support format fileURI in string input type gopls CL 739180 For golang/go#76331 Change-Id: Iae698e16274245cb0e15b68356dadb95ba1b1b9c Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/739200 Reviewed-by: Peter Weinberger <pjw@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/extension/src/language/form.ts b/extension/src/language/form.ts index e28c0fc..3cfa1a5 100644 --- a/extension/src/language/form.ts +++ b/extension/src/language/form.ts
@@ -19,6 +19,18 @@ kind: 'string'; } +// FormFieldTypeDocumentURI defines an input for a file or directory URI. +// +// The client determines the best mechanism to collect this information from +// the user (e.g., a graphical file picker, a text input with autocomplete, etc). +// +// The value returned by the client must be a valid "DocumentUri" as defined +// in the LSP specification: +// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#documentUri +export interface FormFieldTypeDocumentURI { + kind: 'documentURI'; +} + // FormFieldTypeBool defines a boolean input. export interface FormFieldTypeBool { kind: 'bool'; @@ -56,6 +68,7 @@ // FormFieldType acts as a Discriminated Union based on the 'kind' property. export type FormFieldType = | FormFieldTypeString + | FormFieldTypeDocumentURI | FormFieldTypeBool | FormFieldTypeNumber | FormFieldTypeEnum @@ -235,6 +248,78 @@ const type = field.type; switch (type.kind) { + case 'documentURI': { + // UX Decision: Explicitly separate "Open" and "Create" flows. + // + // We use this "Intent Menu" to bypass a limitation in the + // native OS Save Dialog. + // + // While vscode.window.showSaveDialog allows selecting both new + // and existing paths, it forces a system-level "Do you want to + // replace it?" warning if an existing file is selected. + // + // Since our server will NOT actually overwrite the file (it + // just needs the URI), this warning is a false alarm that + // confuses users. We cannot disable this warning in the OS, so + // we split the flow: + // + // - "Open Existing": Uses showOpenDialog (Clean UX, no warnings) + // - "Create New": Uses showSaveDialog (The "Overwrite" warning + // is unavoidable here, but users expect some friction when + // "creating" over an existing name, so it is acceptable). + const action = await vscode.window.showQuickPick( + [ + { + label: '$(file) Open Existing File', + description: 'Select a file that already exists', + target: 'open' + }, + { + label: '$(new-file) Create New File', + description: 'Select a destination for a new file', + target: 'save' + } + ], + { + placeHolder: field.description || 'Select file action', + ignoreFocusOut: true + } + ); + + if (!action) { + return undefined; // User cancelled + } + + let defaultUri: vscode.Uri | undefined; + const defaultUriString = (prevAnswer as string) || (field.default as string); + + if (defaultUriString) { + try { + defaultUri = vscode.Uri.parse(defaultUriString); + } catch { + // Ignore invalid URIs + } + } + + if (action.target === 'open') { + const uri = await vscode.window.showOpenDialog({ + canSelectFiles: true, + canSelectFolders: false, + canSelectMany: false, + openLabel: 'Select', + defaultUri: defaultUri, + title: field.description || 'Select Existing File' + }); + return uri && uri[0] ? uri[0].toString() : undefined; + } else { + const uri = await vscode.window.showSaveDialog({ + defaultUri: defaultUri, + saveLabel: 'Select', + title: field.description || 'Create New File' + }); + return uri ? uri.toString() : undefined; + } + } case 'string': return await vscode.window.showInputBox({ prompt: field.description,
diff --git a/extension/src/language/goLanguageServer.ts b/extension/src/language/goLanguageServer.ts index 7d21ed5..0adfca8 100644 --- a/extension/src/language/goLanguageServer.ts +++ b/extension/src/language/goLanguageServer.ts
@@ -365,7 +365,7 @@ // See https://github.com/microsoft/vscode-languageserver-node/issues/1607 const experimental: LSPObject = { progressMessageStyles: ['log'], - interactiveInputTypes: ['string', 'bool', 'number', 'enum'] + interactiveInputTypes: ['string', 'bool', 'number', 'enum', 'documentURI'] }; params.capabilities.experimental = experimental; }