@@ -445,6 +445,15 @@ export abstract class Server {
445
445
}
446
446
}
447
447
448
+ interface StartPath {
449
+ path ?: string [ ] | string ;
450
+ workspace ?: boolean ;
451
+ }
452
+
453
+ interface Settings {
454
+ lastVisited ?: StartPath ;
455
+ }
456
+
448
457
export class MainServer extends Server {
449
458
public readonly _onDidClientConnect = new Emitter < ClientConnectionEvent > ( ) ;
450
459
public readonly onDidClientConnect = this . _onDidClientConnect . event ;
@@ -461,6 +470,8 @@ export class MainServer extends Server {
461
470
private _proxyServer ?: Promise < net . Server > ;
462
471
private readonly proxyTimeout = 5000 ;
463
472
473
+ private settings : Settings = { } ;
474
+
464
475
public constructor ( options : ServerOptions , args : ParsedArgs ) {
465
476
super ( options ) ;
466
477
this . servicesPromise = this . initializeServices ( args ) ;
@@ -528,44 +539,37 @@ export class MainServer extends Server {
528
539
529
540
private async getRoot ( request : http . IncomingMessage , parsedUrl : url . UrlWithParsedQuery ) : Promise < Response > {
530
541
const filePath = path . join ( this . rootPath , "out/vs/code/browser/workbench/workbench.html" ) ;
531
- let [ content ] = await Promise . all ( [
542
+ let [ content , startPath ] = await Promise . all ( [
532
543
util . promisify ( fs . readFile ) ( filePath , "utf8" ) ,
544
+ this . getFirstValidPath ( [
545
+ { path : parsedUrl . query . workspace , workspace : true } ,
546
+ { path : parsedUrl . query . folder } ,
547
+ ( await this . readSettings ( ) ) . lastVisited ,
548
+ { path : this . options . folderUri }
549
+ ] ) ,
533
550
this . servicesPromise ,
534
551
] ) ;
535
552
553
+ if ( startPath ) {
554
+ this . writeSettings ( {
555
+ lastVisited : {
556
+ path : startPath . uri . fsPath ,
557
+ workspace : startPath . workspace
558
+ } ,
559
+ } ) ;
560
+ }
561
+
536
562
const logger = this . services . get ( ILogService ) as ILogService ;
537
563
logger . info ( "request.url" , `"${ request . url } "` ) ;
538
564
539
- const cwd = process . env . VSCODE_CWD || process . cwd ( ) ;
540
-
541
565
const remoteAuthority = request . headers . host as string ;
542
566
const transformer = getUriTransformer ( remoteAuthority ) ;
543
- const validatePath = async ( filePath : string [ ] | string | undefined , isDirectory : boolean , unsetFallback ?: string ) : Promise < UriComponents | undefined > => {
544
- if ( ! filePath || filePath . length === 0 ) {
545
- if ( ! unsetFallback ) {
546
- return undefined ;
547
- }
548
- filePath = unsetFallback ;
549
- } else if ( Array . isArray ( filePath ) ) {
550
- filePath = filePath [ 0 ] ;
551
- }
552
- const uri = URI . file ( sanitizeFilePath ( filePath , cwd ) ) ;
553
- try {
554
- const stat = await util . promisify ( fs . stat ) ( uri . fsPath ) ;
555
- if ( isDirectory !== stat . isDirectory ( ) ) {
556
- return undefined ;
557
- }
558
- } catch ( error ) {
559
- return undefined ;
560
- }
561
- return transformer . transformOutgoing ( uri ) ;
562
- } ;
563
567
564
568
const environment = this . services . get ( IEnvironmentService ) as IEnvironmentService ;
565
569
const options : Options = {
566
570
WORKBENCH_WEB_CONGIGURATION : {
567
- workspaceUri : await validatePath ( parsedUrl . query . workspace , false ) ,
568
- folderUri : ! parsedUrl . query . workspace ? await validatePath ( parsedUrl . query . folder , true , this . options . folderUri ) : undefined ,
571
+ workspaceUri : startPath && startPath . workspace ? transformer . transformOutgoing ( startPath . uri ) : undefined ,
572
+ folderUri : startPath && ! startPath . workspace ? transformer . transformOutgoing ( startPath . uri ) : undefined ,
569
573
remoteAuthority,
570
574
productConfiguration : product ,
571
575
} ,
@@ -581,6 +585,34 @@ export class MainServer extends Server {
581
585
return { content, filePath } ;
582
586
}
583
587
588
+ /**
589
+ * Choose the first valid path.
590
+ */
591
+ private async getFirstValidPath ( startPaths : Array < StartPath | undefined > ) : Promise < { uri : URI , workspace ?: boolean } | undefined > {
592
+ const logger = this . services . get ( ILogService ) as ILogService ;
593
+ const cwd = process . env . VSCODE_CWD || process . cwd ( ) ;
594
+ for ( let i = 0 ; i < startPaths . length ; ++ i ) {
595
+ const startPath = startPaths [ i ] ;
596
+ if ( ! startPath ) {
597
+ continue ;
598
+ }
599
+ const paths = typeof startPath . path === "string" ? [ startPath . path ] : ( startPath . path || [ ] ) ;
600
+ for ( let j = 0 ; j < paths . length ; ++ j ) {
601
+ const uri = URI . file ( sanitizeFilePath ( paths [ j ] , cwd ) ) ;
602
+ try {
603
+ const stat = await util . promisify ( fs . stat ) ( uri . fsPath ) ;
604
+ // Workspace must be a file.
605
+ if ( ! ! startPath . workspace !== stat . isDirectory ( ) ) {
606
+ return { uri, workspace : startPath . workspace } ;
607
+ }
608
+ } catch ( error ) {
609
+ logger . warn ( error . message ) ;
610
+ }
611
+ }
612
+ }
613
+ return undefined ;
614
+ }
615
+
584
616
private async connect ( message : ConnectionTypeRequest , protocol : Protocol ) : Promise < void > {
585
617
if ( product . commit && message . commit !== product . commit ) {
586
618
throw new Error ( `Version mismatch (${ message . commit } instead of ${ product . commit } )` ) ;
@@ -810,4 +842,41 @@ export class MainServer extends Server {
810
842
}
811
843
return path ;
812
844
}
845
+
846
+ /**
847
+ * Return the file path for Coder settings.
848
+ */
849
+ private get settingsPath ( ) : string {
850
+ const environment = this . services . get ( IEnvironmentService ) as IEnvironmentService ;
851
+ return path . join ( environment . userDataPath , "coder.json" ) ;
852
+ }
853
+
854
+ /**
855
+ * Read settings from the file. On a failure return last known settings and
856
+ * log a warning.
857
+ *
858
+ */
859
+ private async readSettings ( ) : Promise < Settings > {
860
+ try {
861
+ const raw = ( await util . promisify ( fs . readFile ) ( this . settingsPath , "utf8" ) ) . trim ( ) ;
862
+ this . settings = raw ? JSON . parse ( raw ) : { } ;
863
+ } catch ( error ) {
864
+ if ( error . code !== "ENOENT" ) {
865
+ ( this . services . get ( ILogService ) as ILogService ) . warn ( error . message ) ;
866
+ }
867
+ }
868
+ return this . settings ;
869
+ }
870
+
871
+ /**
872
+ * Write settings combined with current settings. On failure log a warning.
873
+ */
874
+ private async writeSettings ( newSettings : Partial < Settings > ) : Promise < void > {
875
+ this . settings = { ...this . settings , ...newSettings } ;
876
+ try {
877
+ await util . promisify ( fs . writeFile ) ( this . settingsPath , JSON . stringify ( this . settings ) ) ;
878
+ } catch ( error ) {
879
+ ( this . services . get ( ILogService ) as ILogService ) . warn ( error . message ) ;
880
+ }
881
+ }
813
882
}
0 commit comments