diff --git a/src/vs/base/common/path.ts b/src/vs/base/common/path.ts index 5ab3ac2621c..28b7d5e61cc 100644 --- a/src/vs/base/common/path.ts +++ b/src/vs/base/common/path.ts @@ -169,7 +169,7 @@ function _format(sep: string, pathObject: ParsedPath) { return dir + sep + base; } -interface ParsedPath { +export interface ParsedPath { root: string; dir: string; base: string; @@ -177,7 +177,7 @@ interface ParsedPath { name: string; } -interface IPath { +export interface IPath { normalize(path: string): string; isAbsolute(path: string): boolean; join(...paths: string[]): string; diff --git a/src/vs/workbench/contrib/search/browser/openFileHandler.ts b/src/vs/workbench/contrib/search/browser/openFileHandler.ts index c2ecf876664..ffb54bbfa76 100644 --- a/src/vs/workbench/contrib/search/browser/openFileHandler.ts +++ b/src/vs/workbench/contrib/search/browser/openFileHandler.ts @@ -8,10 +8,8 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import * as errors from 'vs/base/common/errors'; import { defaultGenerator } from 'vs/base/common/idGenerator'; import { untildify } from 'vs/base/common/labels'; -import { Schemas } from 'vs/base/common/network'; import * as objects from 'vs/base/common/objects'; -import { isAbsolute } from 'vs/base/common/path'; -import { basename, dirname } from 'vs/base/common/resources'; +import { basename, dirname, toLocalResource } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { QuickOpenEntry, QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel'; import { IAutoFocus } from 'vs/base/parts/quickopen/common/quickOpen'; @@ -33,6 +31,8 @@ import { EditorInput, IWorkbenchEditorConfiguration } from 'vs/workbench/common/ import { IFileQueryBuilderOptions, QueryBuilder } from 'vs/workbench/contrib/search/common/queryBuilder'; import { getOutOfWorkspaceEditorResources } from 'vs/workbench/contrib/search/common/search'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { IRemotePathService } from 'vs/workbench/services/path/common/remotePathService'; import { IFileQuery, IFileSearchStats, ISearchComplete, ISearchService } from 'vs/workbench/services/search/common/search'; import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; @@ -122,8 +122,10 @@ export class OpenFileHandler extends QuickOpenHandler { @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @ISearchService private readonly searchService: ISearchService, @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IWorkbenchEnvironmentService private readonly workbenchEnvironmentService: IWorkbenchEnvironmentService, @IFileService private readonly fileService: IFileService, - @ILabelService private readonly labelService: ILabelService + @ILabelService private readonly labelService: ILabelService, + @IRemotePathService private readonly remotePathService: IRemotePathService, ) { super(); @@ -186,15 +188,13 @@ export class OpenFileHandler extends QuickOpenHandler { private async getAbsolutePathResult(query: IPreparedQuery): Promise { const detildifiedQuery = untildify(query.original, this.environmentService.userHome); - if (isAbsolute(detildifiedQuery)) { - const workspaceFolders = this.contextService.getWorkspace().folders; - const resource = workspaceFolders[0] && workspaceFolders[0].uri.scheme !== Schemas.file ? - workspaceFolders[0].uri.with({ path: detildifiedQuery }) : - URI.file(detildifiedQuery); + if ((await this.remotePathService.path).isAbsolute(detildifiedQuery)) { + const resource = toLocalResource( + await this.remotePathService.fileURI(detildifiedQuery), + this.workbenchEnvironmentService.configuration.remoteAuthority); try { const stat = await this.fileService.resolve(resource); - return stat.isDirectory ? undefined : resource; } catch (error) { // ignore diff --git a/src/vs/workbench/services/path/common/remotePathService.ts b/src/vs/workbench/services/path/common/remotePathService.ts new file mode 100644 index 00000000000..fa6b20e9c5c --- /dev/null +++ b/src/vs/workbench/services/path/common/remotePathService.ts @@ -0,0 +1,81 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as path from 'vs/base/common/path'; +import * as platform from 'vs/base/common/platform'; +import { URI } from 'vs/base/common/uri'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; + +const REMOTE_PATH_SERVICE_ID = 'remotePath'; +export const IRemotePathService = createDecorator(REMOTE_PATH_SERVICE_ID); + +export interface IRemotePathService { + _serviceBrand: undefined; + + path: Promise; + fileURI(path: string): Promise; +} + +/** + * Provides the correct IPath implementation for dealing with paths that refer to locations in the extension host + */ +export class RemotePathService implements IRemotePathService { + _serviceBrand: undefined; + + private _extHostOS: Promise; + + constructor( + @IRemoteAgentService readonly remoteAgentService: IRemoteAgentService + ) { + this._extHostOS = remoteAgentService.getEnvironment().then(remoteEnvironment => { + return remoteEnvironment ? remoteEnvironment.os : platform.OS; + }); + } + + get path(): Promise { + return this._extHostOS.then(os => { + return os === platform.OperatingSystem.Windows ? + path.win32 : + path.posix; + }); + } + + async fileURI(_path: string): Promise { + let authority = ''; + + // normalize to fwd-slashes on windows, + // on other systems bwd-slashes are valid + // filename character, eg /f\oo/ba\r.txt + if ((await this._extHostOS) === platform.OperatingSystem.Windows) { + _path = _path.replace(/\\/g, '/'); + } + + // check for authority as used in UNC shares + // or use the path as given + if (_path[0] === '/' && _path[1] === '/') { + const idx = _path.indexOf('/', 2); + if (idx === -1) { + authority = _path.substring(2); + _path = '/'; + } else { + authority = _path.substring(2, idx); + _path = _path.substring(idx) || '/'; + } + } + + // return new _URI('file', authority, path, '', ''); + return URI.from({ + scheme: 'file', + authority, + path: _path, + query: '', + fragment: '' + }); + } +} + +registerSingleton(IRemotePathService, RemotePathService, true);