33 * Licensed under the MIT License. See License.txt in the project root for license information.
44 *--------------------------------------------------------------------------------------------*/
55
6- import { basename , normalize , join , dirname } from 'path' ;
6+ import { basename , normalize , join , dirname , extname } from 'path' ;
77import * as fs from 'fs' ;
88import { localize } from 'vs/nls' ;
99import * as arrays from 'vs/base/common/arrays' ;
@@ -18,15 +18,15 @@ import { IPathWithLineAndColumn, parseLineAndColumnAware } from 'vs/code/node/pa
1818import { ILifecycleService , UnloadReason , IWindowUnloadEvent , LifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain' ;
1919import { IConfigurationService } from 'vs/platform/configuration/common/configuration' ;
2020import { ILogService } from 'vs/platform/log/common/log' ;
21- import { IWindowSettings , OpenContext , IPath , IWindowConfiguration , INativeOpenDialogOptions , IPathsToWaitFor , IEnterWorkspaceResult , IMessageBoxResult , INewWindowOptions } from 'vs/platform/windows/common/windows' ;
21+ import { IWindowSettings , OpenContext , IPath , IWindowConfiguration , INativeOpenDialogOptions , IPathsToWaitFor , IEnterWorkspaceResult , IMessageBoxResult , INewWindowOptions , IURIToOpen , URIType } from 'vs/platform/windows/common/windows' ;
2222import { getLastActiveWindow , findBestWindowOrFolderForFile , findWindowOnWorkspace , findWindowOnExtensionDevelopmentPath , findWindowOnWorkspaceOrFolderUri } from 'vs/code/node/windowsFinder' ;
2323import { Event as CommonEvent , Emitter } from 'vs/base/common/event' ;
2424import product from 'vs/platform/node/product' ;
2525import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry' ;
2626import { IWindowsMainService , IOpenConfiguration , IWindowsCountChangedEvent , ICodeWindow , IWindowState as ISingleWindowState , WindowMode } from 'vs/platform/windows/electron-main/windows' ;
2727import { IHistoryMainService } from 'vs/platform/history/common/history' ;
2828import { IProcessEnvironment , isLinux , isMacintosh , isWindows } from 'vs/base/common/platform' ;
29- import { IWorkspacesMainService , IWorkspaceIdentifier , WORKSPACE_FILTER , ISingleFolderWorkspaceIdentifier , isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces' ;
29+ import { IWorkspacesMainService , IWorkspaceIdentifier , WORKSPACE_FILTER , ISingleFolderWorkspaceIdentifier , isSingleFolderWorkspaceIdentifier , WORKSPACE_EXTENSION } from 'vs/platform/workspaces/common/workspaces' ;
3030import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation' ;
3131import { mnemonicButtonLabel } from 'vs/base/common/labels' ;
3232import { Schemas } from 'vs/base/common/network' ;
@@ -118,10 +118,6 @@ interface IFileInputs {
118118 remoteAuthority ?: string ;
119119}
120120
121- enum URIType {
122- FILE , FOLDER , WORKSPACE
123- }
124-
125121interface IPathToOpen extends IPath {
126122
127123 // the workspace for a Code instance to open
@@ -485,7 +481,7 @@ export class WindowsManager implements IWindowsMainService {
485481 // Make sure to pass focus to the most relevant of the windows if we open multiple
486482 if ( usedWindows . length > 1 ) {
487483
488- let focusLastActive = this . windowsState . lastActiveWindow && ! openConfig . forceEmpty && ! hasArgs ( openConfig . cli . _ ) && ! hasArgs ( openConfig . cli [ 'file-uri' ] ) && ! hasArgs ( openConfig . cli [ 'folder-uri' ] ) && ! hasArgs ( openConfig . cli [ 'workspace-uri' ] ) && ! ( openConfig . urisToOpen && openConfig . urisToOpen . length ) ;
484+ let focusLastActive = this . windowsState . lastActiveWindow && ! openConfig . forceEmpty && ! hasArgs ( openConfig . cli . _ ) && ! hasArgs ( openConfig . cli [ 'file-uri' ] ) && ! hasArgs ( openConfig . cli [ 'folder-uri' ] ) && ! ( openConfig . urisToOpen && openConfig . urisToOpen . length ) ;
489485 let focusLastOpened = true ;
490486 let focusLastWindow = true ;
491487
@@ -847,7 +843,7 @@ export class WindowsManager implements IWindowsMainService {
847843 }
848844
849845 // Extract paths: from CLI
850- else if ( hasArgs ( openConfig . cli . _ ) || hasArgs ( openConfig . cli [ 'folder-uri' ] ) || hasArgs ( openConfig . cli [ 'file-uri' ] ) || hasArgs ( openConfig . cli [ 'workspace-uri' ] ) ) {
846+ else if ( hasArgs ( openConfig . cli . _ ) || hasArgs ( openConfig . cli [ 'folder-uri' ] ) || hasArgs ( openConfig . cli [ 'file-uri' ] ) ) {
851847 windowsToOpen = this . doExtractPathsFromCLI ( openConfig . cli ) ;
852848 isCommandLineOrAPICall = true ;
853849 }
@@ -884,19 +880,19 @@ export class WindowsManager implements IWindowsMainService {
884880 continue ;
885881 }
886882
887- const path = this . parseUri ( pathToOpen , openConfig . forceOpenWorkspaceAsFile ? URIType . FILE : URIType . FOLDER , parseOptions ) ;
883+ const path = this . parseUri ( pathToOpen . uri , pathToOpen . typeHint , parseOptions ) ;
888884 if ( path ) {
889885 pathsToOpen . push ( path ) ;
890886 } else {
891887
892888 // Warn about the invalid URI or path
893889 let message , detail ;
894- if ( pathToOpen . scheme === Schemas . file ) {
890+ if ( pathToOpen . uri . scheme === Schemas . file ) {
895891 message = localize ( 'pathNotExistTitle' , "Path does not exist" ) ;
896- detail = localize ( 'pathNotExistDetail' , "The path '{0}' does not seem to exist anymore on disk." , pathToOpen . fsPath ) ;
892+ detail = localize ( 'pathNotExistDetail' , "The path '{0}' does not seem to exist anymore on disk." , pathToOpen . uri . fsPath ) ;
897893 } else {
898894 message = localize ( 'uriInvalidTitle' , "URI can not be opened" ) ;
899- detail = localize ( 'uriInvalidDetail' , "The URI '{0}' is not valid and can not be opened." , pathToOpen . toString ( ) ) ;
895+ detail = localize ( 'uriInvalidDetail' , "The URI '{0}' is not valid and can not be opened." , pathToOpen . uri . toString ( ) ) ;
900896 }
901897 const options : Electron . MessageBoxOptions = {
902898 title : product . nameLong ,
@@ -920,7 +916,7 @@ export class WindowsManager implements IWindowsMainService {
920916 // folder uris
921917 const folderUris = asArray ( cli [ 'folder-uri' ] ) ;
922918 for ( let folderUri of folderUris ) {
923- const path = this . parseUri ( this . argToUri ( folderUri ) , URIType . FOLDER , parseOptions ) ;
919+ const path = this . parseUri ( this . argToUri ( folderUri ) , 'folder' , parseOptions ) ;
924920 if ( path ) {
925921 pathsToOpen . push ( path ) ;
926922 }
@@ -929,21 +925,12 @@ export class WindowsManager implements IWindowsMainService {
929925 // file uris
930926 const fileUris = asArray ( cli [ 'file-uri' ] ) ;
931927 for ( let fileUri of fileUris ) {
932- const path = this . parseUri ( this . argToUri ( fileUri ) , URIType . FILE , parseOptions ) ;
933- if ( path ) {
934- pathsToOpen . push ( path ) ;
935- }
936- }
937-
938- const workspaceUris = asArray ( cli [ 'workspace-uri' ] ) ;
939- for ( let workspaceUri of workspaceUris ) {
940- const path = this . parseUri ( this . argToUri ( workspaceUri ) , URIType . WORKSPACE , parseOptions ) ;
928+ const path = this . parseUri ( this . argToUri ( fileUri ) , 'file' ) ;
941929 if ( path ) {
942930 pathsToOpen . push ( path ) ;
943931 }
944932 }
945933
946-
947934 // folder or file paths
948935 const cliArgs = asArray ( cli . _ ) ;
949936 for ( let cliArg of cliArgs ) {
@@ -987,12 +974,12 @@ export class WindowsManager implements IWindowsMainService {
987974 const windowsToOpen : IPathToOpen [ ] = [ ] ;
988975 for ( const openedWindow of openedWindows ) {
989976 if ( openedWindow . workspace ) { // Workspaces
990- const pathToOpen = this . parseUri ( openedWindow . workspace . configPath , URIType . WORKSPACE , { remoteAuthority : openedWindow . remoteAuthority } ) ;
977+ const pathToOpen = this . parseUri ( openedWindow . workspace . configPath , 'file' , { remoteAuthority : openedWindow . remoteAuthority } ) ;
991978 if ( pathToOpen && pathToOpen . workspace ) {
992979 windowsToOpen . push ( pathToOpen ) ;
993980 }
994981 } else if ( openedWindow . folderUri ) { // Folders
995- const pathToOpen = this . parseUri ( openedWindow . folderUri , URIType . FOLDER , { remoteAuthority : openedWindow . remoteAuthority } ) ;
982+ const pathToOpen = this . parseUri ( openedWindow . folderUri , 'folder' , { remoteAuthority : openedWindow . remoteAuthority } ) ;
996983 if ( pathToOpen && pathToOpen . folderUri ) {
997984 windowsToOpen . push ( pathToOpen ) ;
998985 }
@@ -1042,7 +1029,7 @@ export class WindowsManager implements IWindowsMainService {
10421029 return null ;
10431030 }
10441031
1045- private parseUri ( uri : URI , type : URIType , options ? : IPathParseOptions ) : IPathToOpen | null {
1032+ private parseUri ( uri : URI , typeHint ? : URIType , options : IPathParseOptions = { } ) : IPathToOpen | null {
10461033 if ( ! uri || ! uri . scheme ) {
10471034 return null ;
10481035 }
@@ -1051,16 +1038,31 @@ export class WindowsManager implements IWindowsMainService {
10511038 }
10521039
10531040 // open remote if either specified in the cli or if it's a remotehost URI
1054- const remoteAuthority = options && options . remoteAuthority || getRemoteAuthority ( uri ) ;
1041+ const remoteAuthority = options . remoteAuthority || getRemoteAuthority ( uri ) ;
10551042
10561043 // normalize URI
10571044 uri = normalizePath ( uri ) ;
1045+
1046+
1047+ // remove trailing slash
10581048 const uriPath = uri . path ;
1059- if ( uriPath . length > 2 && endsWith ( uriPath , '/' ) ) {
1060- uri = uri . with ( { path : uriPath . substr ( 0 , uriPath . length - 1 ) } ) ;
1049+ if ( endsWith ( uriPath , '/' ) ) {
1050+ if ( uriPath . length > 2 ) {
1051+ // only remove if the path has some content
1052+ uri = uri . with ( { path : uriPath . substr ( 0 , uriPath . length - 1 ) } ) ;
1053+ }
1054+ if ( ! typeHint ) {
1055+ typeHint = 'folder' ;
1056+ }
1057+ }
1058+
1059+ // if there's no type hint
1060+ if ( ! typeHint && ( extname ( uri . path ) === WORKSPACE_EXTENSION || options . gotoLineMode ) ) {
1061+ typeHint = 'file' ;
10611062 }
1062- if ( type === URIType . FILE ) {
1063- if ( options && options . gotoLineMode ) {
1063+
1064+ if ( typeHint === 'file' ) {
1065+ if ( options . gotoLineMode ) {
10641066 const parsedPath = parseLineAndColumnAware ( uri . path ) ;
10651067 return {
10661068 fileUri : uri . with ( { path : parsedPath . path } ) ,
@@ -1069,37 +1071,37 @@ export class WindowsManager implements IWindowsMainService {
10691071 remoteAuthority
10701072 } ;
10711073 }
1074+ if ( extname ( uri . path ) === WORKSPACE_EXTENSION && ! options . forceOpenWorkspaceAsFile ) {
1075+ return {
1076+ workspace : this . workspacesMainService . getWorkspaceIdentifier ( uri ) ,
1077+ remoteAuthority
1078+ } ;
1079+ }
10721080 return {
10731081 fileUri : uri ,
10741082 remoteAuthority
10751083 } ;
1076- } else if ( type === URIType . WORKSPACE ) {
1077- return {
1078- workspace : this . workspacesMainService . getWorkspaceIdentifier ( uri ) ,
1079- remoteAuthority
1080- } ;
10811084 }
10821085 return {
10831086 folderUri : uri ,
10841087 remoteAuthority
10851088 } ;
10861089 }
10871090
1088- private parsePath ( anyPath : string , options ? : IPathParseOptions ) : IPathToOpen | null {
1091+ private parsePath ( anyPath : string , options : IPathParseOptions ) : IPathToOpen | null {
10891092 if ( ! anyPath ) {
10901093 return null ;
10911094 }
10921095
10931096 let parsedPath : IPathWithLineAndColumn ;
10941097
1095- const gotoLineMode = options && options . gotoLineMode ;
1096- if ( options && options . gotoLineMode ) {
1098+ if ( options . gotoLineMode ) {
10971099 parsedPath = parseLineAndColumnAware ( anyPath ) ;
10981100 anyPath = parsedPath . path ;
10991101 }
11001102
11011103 // open remote if either specified in the cli even if it is a local file. TODO: Future idea: resolve in remote host context.
1102- const remoteAuthority = options && options . remoteAuthority ;
1104+ const remoteAuthority = options . remoteAuthority ;
11031105
11041106 const candidate = normalize ( anyPath ) ;
11051107 try {
@@ -1108,7 +1110,7 @@ export class WindowsManager implements IWindowsMainService {
11081110 if ( candidateStat . isFile ( ) ) {
11091111
11101112 // Workspace (unless disabled via flag)
1111- if ( ! options || ! options . forceOpenWorkspaceAsFile ) {
1113+ if ( ! options . forceOpenWorkspaceAsFile ) {
11121114 const workspace = this . workspacesMainService . resolveWorkspaceSync ( candidate ) ;
11131115 if ( workspace ) {
11141116 return { workspace : { id : workspace . id , configPath : workspace . configPath } , remoteAuthority } ;
@@ -1118,8 +1120,8 @@ export class WindowsManager implements IWindowsMainService {
11181120 // File
11191121 return {
11201122 fileUri : URI . file ( candidate ) ,
1121- lineNumber : gotoLineMode ? parsedPath . line : undefined ,
1122- columnNumber : gotoLineMode ? parsedPath . column : undefined ,
1123+ lineNumber : options . gotoLineMode ? parsedPath . line : undefined ,
1124+ columnNumber : options . gotoLineMode ? parsedPath . column : undefined ,
11231125 remoteAuthority
11241126 } ;
11251127 }
@@ -1201,7 +1203,6 @@ export class WindowsManager implements IWindowsMainService {
12011203 }
12021204 let folderUris = asArray ( openConfig . cli [ 'folder-uri' ] ) ;
12031205 let fileUris = asArray ( openConfig . cli [ 'file-uri' ] ) ;
1204- let workspaceUris = asArray ( openConfig . cli [ 'workspace-uri' ] ) ;
12051206 let cliArgs = openConfig . cli . _ ;
12061207
12071208 // Fill in previously opened workspace unless an explicit path is provided and we are not unit testing
@@ -1219,7 +1220,7 @@ export class WindowsManager implements IWindowsMainService {
12191220 if ( workspaceToOpen . configPath . scheme === Schemas . file ) {
12201221 cliArgs = [ fsPath ( workspaceToOpen . configPath ) ] ;
12211222 } else {
1222- workspaceUris = [ workspaceToOpen . configPath . toString ( ) ] ;
1223+ fileUris = [ workspaceToOpen . configPath . toString ( ) ] ;
12231224 }
12241225 }
12251226 }
@@ -1238,17 +1239,12 @@ export class WindowsManager implements IWindowsMainService {
12381239 fileUris = [ ] ;
12391240 }
12401241
1241- if ( workspaceUris . length && workspaceUris . some ( uri => ! ! findWindowOnWorkspaceOrFolderUri ( WindowsManager . WINDOWS , this . argToUri ( uri ) ) ) ) {
1242- workspaceUris = [ ] ;
1243- }
1244-
12451242 openConfig . cli . _ = cliArgs ;
12461243 openConfig . cli [ 'folder-uri' ] = folderUris ;
12471244 openConfig . cli [ 'file-uri' ] = fileUris ;
1248- openConfig . cli [ 'workspace-uri' ] = workspaceUris ;
12491245
12501246 // Open it
1251- this . open ( { context : openConfig . context , cli : openConfig . cli , forceNewWindow : true , forceEmpty : ! cliArgs . length && ! folderUris . length && ! fileUris . length && ! workspaceUris . length , userEnv : openConfig . userEnv } ) ;
1247+ this . open ( { context : openConfig . context , cli : openConfig . cli , forceNewWindow : true , forceEmpty : ! cliArgs . length && ! folderUris . length && ! fileUris . length , userEnv : openConfig . userEnv } ) ;
12521248 }
12531249
12541250 private openInBrowserWindow ( options : IOpenBrowserWindowOptions ) : ICodeWindow {
@@ -1897,7 +1893,7 @@ class Dialogs {
18971893 } ) ;
18981894 }
18991895
1900- private getFileOrFolderUris ( options : IInternalNativeOpenDialogOptions ) : Promise < URI [ ] > {
1896+ private getFileOrFolderUris ( options : IInternalNativeOpenDialogOptions ) : Promise < IURIToOpen [ ] > {
19011897
19021898 // Ensure dialog options
19031899 if ( ! options . dialogOptions ) {
@@ -1935,7 +1931,12 @@ class Dialogs {
19351931 // Remember path in storage for next time
19361932 this . stateService . setItem ( Dialogs . workingDirPickerStorageKey , dirname ( paths [ 0 ] ) ) ;
19371933
1938- return paths . map ( path => URI . file ( path ) ) ;
1934+ const result : IURIToOpen [ ] = [ ] ;
1935+ for ( const path of paths ) {
1936+ result . push ( { uri : URI . file ( path ) } ) ;
1937+ }
1938+
1939+ return result ;
19391940 }
19401941
19411942 return undefined ;
0 commit comments