@@ -445,6 +445,15 @@ export abstract class Server {
445445 }
446446}
447447
448+ interface StartPath {
449+ path ?: string [ ] | string ;
450+ workspace ?: boolean ;
451+ }
452+
453+ interface Settings {
454+ lastVisited ?: StartPath ;
455+ }
456+
448457export class MainServer extends Server {
449458 public readonly _onDidClientConnect = new Emitter < ClientConnectionEvent > ( ) ;
450459 public readonly onDidClientConnect = this . _onDidClientConnect . event ;
@@ -461,6 +470,8 @@ export class MainServer extends Server {
461470 private _proxyServer ?: Promise < net . Server > ;
462471 private readonly proxyTimeout = 5000 ;
463472
473+ private settings : Settings = { } ;
474+
464475 public constructor ( options : ServerOptions , args : ParsedArgs ) {
465476 super ( options ) ;
466477 this . servicesPromise = this . initializeServices ( args ) ;
@@ -528,44 +539,37 @@ export class MainServer extends Server {
528539
529540 private async getRoot ( request : http . IncomingMessage , parsedUrl : url . UrlWithParsedQuery ) : Promise < Response > {
530541 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 ( [
532543 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+ ] ) ,
533550 this . servicesPromise ,
534551 ] ) ;
535552
553+ if ( startPath ) {
554+ this . writeSettings ( {
555+ lastVisited : {
556+ path : startPath . uri . fsPath ,
557+ workspace : startPath . workspace
558+ } ,
559+ } ) ;
560+ }
561+
536562 const logger = this . services . get ( ILogService ) as ILogService ;
537563 logger . info ( "request.url" , `"${ request . url } "` ) ;
538564
539- const cwd = process . env . VSCODE_CWD || process . cwd ( ) ;
540-
541565 const remoteAuthority = request . headers . host as string ;
542566 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- } ;
563567
564568 const environment = this . services . get ( IEnvironmentService ) as IEnvironmentService ;
565569 const options : Options = {
566570 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 ,
569573 remoteAuthority,
570574 productConfiguration : product ,
571575 } ,
@@ -581,6 +585,34 @@ export class MainServer extends Server {
581585 return { content, filePath } ;
582586 }
583587
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+
584616 private async connect ( message : ConnectionTypeRequest , protocol : Protocol ) : Promise < void > {
585617 if ( product . commit && message . commit !== product . commit ) {
586618 throw new Error ( `Version mismatch (${ message . commit } instead of ${ product . commit } )` ) ;
@@ -810,4 +842,41 @@ export class MainServer extends Server {
810842 }
811843 return path ;
812844 }
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+ }
813882}
0 commit comments