@@ -44,22 +44,21 @@ export async function checkoutPRInWorktree(
4444 // Prepare for operations
4545 const repoRootPath = repositoryToUse . rootUri . fsPath ;
4646 const parentDir = path . dirname ( repoRootPath ) ;
47- const defaultWorktreePath = path . join ( parentDir , `pr-${ pullRequestModel . number } ` ) ;
47+ const worktreeName = `pr-${ pullRequestModel . number } ` ;
48+ // Match the default location convention used by VS Code's built-in `Git: Create Worktree...` command:
49+ // `<parentDir>/<repoBasename>.worktrees/<worktreeName>`.
50+ const defaultWorktreePath = path . join ( parentDir , `${ path . basename ( repoRootPath ) } .worktrees` , worktreeName ) ;
4851 const branchName = prHead . ref ;
4952 const remoteName = pullRequestModel . remote . remoteName ;
5053
51- // Ask user for worktree location first (not in progress)
52- const worktreeUri = await vscode . window . showSaveDialog ( {
53- defaultUri : vscode . Uri . file ( defaultWorktreePath ) ,
54- title : vscode . l10n . t ( 'Select Worktree Location' ) ,
55- saveLabel : vscode . l10n . t ( 'Create Worktree' ) ,
56- } ) ;
57-
58- if ( ! worktreeUri ) {
54+ // Ask user for worktree location using a custom InputBox UI (matches the built-in
55+ // `Git: Create Worktree...` experience instead of showing the OS save dialog).
56+ const worktreePath = await promptForWorktreePath ( repositoryToUse , worktreeName , defaultWorktreePath ) ;
57+ if ( ! worktreePath ) {
5958 return ; // User cancelled
6059 }
6160
62- const worktreePath = worktreeUri . fsPath ;
61+ const worktreeUri = vscode . Uri . file ( worktreePath ) ;
6362 const trackedBranchName = `${ remoteName } /${ branchName } ` ;
6463
6564 try {
@@ -126,3 +125,105 @@ export async function checkoutPRInWorktree(
126125 vscode . window . showErrorMessage ( vscode . l10n . t ( 'Failed to create worktree: {0}' , errorMessage ) ) ;
127126 }
128127}
128+
129+ /**
130+ * Prompts the user for a worktree path using an `InputBox` that mirrors VS Code's
131+ * built-in `Git: Create Worktree...` UI: the path is pre-filled and editable, the
132+ * worktree-name segment is pre-selected, and an inline folder-picker button lets
133+ * the user browse to a parent directory.
134+ *
135+ * @param repository The repository the worktree will be created from (used to detect
136+ * conflicts with existing worktrees).
137+ * @param worktreeName The default leaf folder name for the new worktree (e.g. `pr-123`).
138+ * @param defaultWorktreePath The default full path to suggest in the input box.
139+ * @returns The chosen absolute path, or `undefined` if the user cancelled.
140+ */
141+ async function promptForWorktreePath (
142+ repository : Repository ,
143+ worktreeName : string ,
144+ defaultWorktreePath : string
145+ ) : Promise < string | undefined > {
146+ const getValueSelection = ( value : string ) : [ number , number ] | undefined => {
147+ if ( ! value || ! worktreeName || ! value . endsWith ( worktreeName ) ) {
148+ return undefined ;
149+ }
150+ const start = value . length - worktreeName . length ;
151+ return [ start , value . length ] ;
152+ } ;
153+
154+ const getValidationMessage = ( value : string ) : vscode . InputBoxValidationMessage | undefined => {
155+ const normalized = path . normalize ( value ) ;
156+ const conflict = repository . state . worktrees ?. find ( w => path . normalize ( w . path ) === normalized ) ;
157+ return conflict ? {
158+ message : vscode . l10n . t ( 'A worktree already exists at "{0}".' , value ) ,
159+ severity : vscode . InputBoxValidationSeverity . Warning
160+ } : undefined ;
161+ } ;
162+
163+ const browseForParent = async ( ) : Promise < string | undefined > => {
164+ const currentValue = inputBox . value ;
165+ const defaultUri = currentValue
166+ ? vscode . Uri . file ( path . dirname ( currentValue ) )
167+ : vscode . Uri . file ( path . dirname ( defaultWorktreePath ) ) ;
168+
169+ const uris = await vscode . window . showOpenDialog ( {
170+ defaultUri,
171+ canSelectFiles : false ,
172+ canSelectFolders : true ,
173+ canSelectMany : false ,
174+ openLabel : vscode . l10n . t ( 'Select as Worktree Destination' ) ,
175+ } ) ;
176+
177+ if ( ! uris || uris . length === 0 ) {
178+ return undefined ;
179+ }
180+ return path . join ( uris [ 0 ] . fsPath , worktreeName ) ;
181+ } ;
182+
183+ const disposables : vscode . Disposable [ ] = [ ] ;
184+ const inputBox = vscode . window . createInputBox ( ) ;
185+ disposables . push ( inputBox ) ;
186+
187+ inputBox . title = vscode . l10n . t ( 'Create Worktree' ) ;
188+ inputBox . placeholder = vscode . l10n . t ( 'Worktree path' ) ;
189+ inputBox . prompt = vscode . l10n . t ( 'Please provide a worktree path' ) ;
190+ inputBox . value = defaultWorktreePath ;
191+ inputBox . valueSelection = getValueSelection ( inputBox . value ) ;
192+ inputBox . validationMessage = getValidationMessage ( inputBox . value ) ;
193+ inputBox . ignoreFocusOut = true ;
194+ inputBox . buttons = [
195+ {
196+ iconPath : new vscode . ThemeIcon ( 'folder' ) ,
197+ tooltip : vscode . l10n . t ( 'Select Worktree Destination' ) ,
198+ location : vscode . QuickInputButtonLocation . Inline
199+ }
200+ ] ;
201+
202+ try {
203+ inputBox . show ( ) ;
204+
205+ return await new Promise < string | undefined > ( ( resolve ) => {
206+ disposables . push ( inputBox . onDidHide ( ( ) => resolve ( undefined ) ) ) ;
207+ disposables . push ( inputBox . onDidAccept ( ( ) => {
208+ if ( ! inputBox . value ) {
209+ return ;
210+ }
211+ resolve ( inputBox . value ) ;
212+ inputBox . hide ( ) ;
213+ } ) ) ;
214+ disposables . push ( inputBox . onDidChangeValue ( value => {
215+ inputBox . validationMessage = getValidationMessage ( value ) ;
216+ } ) ) ;
217+ disposables . push ( inputBox . onDidTriggerButton ( async ( ) => {
218+ const chosen = await browseForParent ( ) ;
219+ if ( chosen ) {
220+ inputBox . value = chosen ;
221+ inputBox . valueSelection = getValueSelection ( inputBox . value ) ;
222+ inputBox . validationMessage = getValidationMessage ( inputBox . value ) ;
223+ }
224+ } ) ) ;
225+ } ) ;
226+ } finally {
227+ disposables . forEach ( d => d . dispose ( ) ) ;
228+ }
229+ }
0 commit comments