11import * as fs from "fs" ;
2+ import * as os from "os" ;
23import * as path from "path" ;
34import {
45 commands ,
56 Disposable ,
67 LineChange ,
78 Position ,
9+ ProgressLocation ,
810 Range ,
911 SourceControlResourceState ,
1012 TextDocumentShowOptions ,
@@ -21,20 +23,23 @@ import {
2123 inputSwitchChangelist
2224} from "./changelistItems" ;
2325import {
26+ IAuth ,
2427 ICommand ,
2528 ICommandOptions ,
29+ ICpOptions ,
2630 Status ,
2731 SvnUriAction
2832} from "./common/types" ;
2933import { getConflictPickOptions } from "./conflictItems" ;
30- import { selectBranch } from "./helpers/branch" ;
34+ import { getBranchName , selectBranch } from "./helpers/branch" ;
3135import { configuration } from "./helpers/configuration" ;
3236import { inputIgnoreList } from "./ignoreitems" ;
3337import { applyLineChanges } from "./lineChanges" ;
3438import { inputCommitMessage } from "./messages" ;
3539import { Model } from "./model" ;
3640import { Repository } from "./repository" ;
3741import { Resource } from "./resource" ;
42+ import { Svn , svnErrorCodes } from "./svn" ;
3843import IncommingChangeNode from "./treeView/nodes/incomingChangeNode" ;
3944import IncomingChangeNode from "./treeView/nodes/incomingChangeNode" ;
4045import { fromSvnUri , toSvnUri } from "./uri" ;
@@ -62,7 +67,7 @@ function command(
6267export class SvnCommands implements IDisposable {
6368 private disposables : Disposable [ ] ;
6469
65- constructor ( private model : Model ) {
70+ constructor ( private model : Model , private svn : Svn ) {
6671 this . disposables = svnCommands . map ( ( { commandId, method, options } ) => {
6772 const command = this . createCommand ( method , options ) ;
6873 if ( options . diff && hasSupportToRegisterDiffCommand ( ) ) {
@@ -121,32 +126,170 @@ export class SvnCommands implements IDisposable {
121126 await commands . executeCommand ( "vscode.open" , resourceUri ) ;
122127 }
123128
124- @command ( "svn.promptAuth" , { repository : true } )
125- public async promptAuth ( repository : Repository ) : Promise < boolean > {
129+ @command ( "svn.promptAuth" )
130+ public async promptAuth (
131+ prevUsername ?: string ,
132+ prevPassword ?: string
133+ ) : Promise < IAuth | undefined > {
126134 const username = await window . showInputBox ( {
127135 placeHolder : "Svn repository username" ,
128136 prompt : "Please enter your username" ,
129- value : repository . username
137+ value : prevUsername
130138 } ) ;
131139
132140 if ( username === undefined ) {
133- return false ;
141+ return ;
134142 }
135143
136144 const password = await window . showInputBox ( {
137145 placeHolder : "Svn repository password" ,
138146 prompt : "Please enter your password" ,
147+ value : prevPassword ,
139148 password : true
140149 } ) ;
141150
142- if ( username === undefined ) {
143- return false ;
151+ if ( password === undefined ) {
152+ return ;
153+ }
154+
155+ const auth : IAuth = {
156+ username,
157+ password
158+ } ;
159+
160+ return auth ;
161+ }
162+
163+ @command ( "svn.checkout" )
164+ public async checkout ( url ?: string ) : Promise < void > {
165+ if ( ! url ) {
166+ url = await window . showInputBox ( {
167+ prompt : "Repository URL" ,
168+ ignoreFocusOut : true
169+ } ) ;
170+ }
171+
172+ if ( ! url ) {
173+ return ;
174+ }
175+
176+ let defaultCheckoutDirectory =
177+ configuration . get < string > ( "defaultCheckoutDirectory" ) || os . homedir ( ) ;
178+ defaultCheckoutDirectory = defaultCheckoutDirectory . replace (
179+ / ^ ~ / ,
180+ os . homedir ( )
181+ ) ;
182+
183+ const uris = await window . showOpenDialog ( {
184+ canSelectFiles : false ,
185+ canSelectFolders : true ,
186+ canSelectMany : false ,
187+ defaultUri : Uri . file ( defaultCheckoutDirectory ) ,
188+ openLabel : "Select Repository Location"
189+ } ) ;
190+
191+ if ( ! uris || uris . length === 0 ) {
192+ return ;
193+ }
194+
195+ const uri = uris [ 0 ] ;
196+ const parentPath = uri . fsPath ;
197+
198+ let folderName : string | undefined ;
199+
200+ // Get folder name from branch
201+ const branch = getBranchName ( url ) ;
202+ if ( branch ) {
203+ const baseUrl = url . replace ( / \/ / g, "/" ) . replace ( branch . path , "" ) ;
204+ folderName = path . basename ( baseUrl ) ;
205+ }
206+
207+ folderName = await window . showInputBox ( {
208+ prompt : "Folder name" ,
209+ value : folderName ,
210+ ignoreFocusOut : true
211+ } ) ;
212+
213+ if ( ! folderName ) {
214+ return ;
144215 }
145216
146- repository . username = username ;
147- repository . password = password ;
217+ const repositoryPath = path . join ( parentPath , folderName ) ;
218+
219+ // Use Notification location if supported
220+ let location = ProgressLocation . Window ;
221+ if ( ( ProgressLocation as any ) . Notification ) {
222+ location = ( ProgressLocation as any ) . Notification ;
223+ }
224+
225+ const progressOptions = {
226+ location,
227+ title : `Checkout svn repository '${ url } '...` ,
228+ cancellable : true
229+ } ;
230+
231+ let attempt = 0 ;
148232
149- return true ;
233+ const opt : ICpOptions = { } ;
234+
235+ while ( true ) {
236+ attempt ++ ;
237+ try {
238+ await window . withProgress ( progressOptions , async ( ) => {
239+ const args = [ "checkout" , url , repositoryPath ] ;
240+ await this . svn . exec ( parentPath , args , opt ) ;
241+ } ) ;
242+ break ;
243+ } catch ( err ) {
244+ if (
245+ err . svnErrorCode === svnErrorCodes . AuthorizationFailed &&
246+ attempt <= 3
247+ ) {
248+ const auth = ( await commands . executeCommand (
249+ "svn.promptAuth" ,
250+ opt . username
251+ ) ) as IAuth ;
252+ if ( auth ) {
253+ opt . username = auth . username ;
254+ opt . password = auth . password ;
255+ continue ;
256+ }
257+ }
258+ throw err ;
259+ }
260+ }
261+
262+ const choices = [ ] ;
263+ let message = "Would you like to open the checked out repository?" ;
264+ const open = "Open Repository" ;
265+ choices . push ( open ) ;
266+
267+ const addToWorkspace = "Add to Workspace" ;
268+ if (
269+ workspace . workspaceFolders &&
270+ ( workspace as any ) . updateWorkspaceFolders // For VSCode >= 1.21
271+ ) {
272+ message =
273+ "Would you like to open the checked out repository, or add it to the current workspace?" ;
274+ choices . push ( addToWorkspace ) ;
275+ }
276+
277+ const result = await window . showInformationMessage ( message , ...choices ) ;
278+
279+ const openFolder = result === open ;
280+
281+ if ( openFolder ) {
282+ commands . executeCommand ( "vscode.openFolder" , Uri . file ( repositoryPath ) ) ;
283+ } else if ( result === addToWorkspace ) {
284+ // For VSCode >= 1.21
285+ ( workspace as any ) . updateWorkspaceFolders (
286+ workspace . workspaceFolders ! . length ,
287+ 0 ,
288+ {
289+ uri : Uri . file ( repositoryPath )
290+ }
291+ ) ;
292+ }
150293 }
151294
152295 @command ( "svn.commitWithMessage" , { repository : true } )
0 commit comments