diff --git a/packages/core/src/browser/resource-context-key.ts b/packages/core/src/browser/resource-context-key.ts index 31948c9070890..a1d0b89d2572d 100644 --- a/packages/core/src/browser/resource-context-key.ts +++ b/packages/core/src/browser/resource-context-key.ts @@ -19,6 +19,8 @@ import URI from '../common/uri'; import { URI as Uri } from 'vscode-uri'; import { ContextKeyService, ContextKey } from './context-key-service'; import { LanguageService } from './language-service'; +import { ApplicationServer } from '../common/application-protocol'; +import { OS } from '../common/os'; @injectable() export class ResourceContextKey { @@ -29,6 +31,9 @@ export class ResourceContextKey { @inject(ContextKeyService) protected readonly contextKeyService: ContextKeyService; + @inject(ApplicationServer) + protected readonly applicationService: ApplicationServer; + protected resource: ContextKey; protected resourceSchemeKey: ContextKey; protected resourceFileName: ContextKey; @@ -36,9 +41,10 @@ export class ResourceContextKey { protected resourceLangId: ContextKey; protected resourceDirName: ContextKey; protected resourcePath: ContextKey; + protected isPosix: boolean; @postConstruct() - protected init(): void { + protected async init(): Promise { this.resource = this.contextKeyService.createKey('resource', undefined); this.resourceSchemeKey = this.contextKeyService.createKey('resourceScheme', undefined); this.resourceFileName = this.contextKeyService.createKey('resourceFilename', undefined); @@ -46,6 +52,7 @@ export class ResourceContextKey { this.resourceLangId = this.contextKeyService.createKey('resourceLangId', undefined); this.resourceDirName = this.contextKeyService.createKey('resourceDirName', undefined); this.resourcePath = this.contextKeyService.createKey('resourcePath', undefined); + this.isPosix = await this.applicationService.getBackendOS() !== OS.Type.Windows; } get(): URI | undefined { @@ -59,8 +66,8 @@ export class ResourceContextKey { this.resourceFileName.set(resourceUri && resourceUri.path.base); this.resourceExtname.set(resourceUri && resourceUri.path.ext); this.resourceLangId.set(resourceUri && this.getLanguageId(resourceUri)); - this.resourceDirName.set(resourceUri && Uri.parse(resourceUri.path.dir.toString()).fsPath); - this.resourcePath.set(resourceUri && resourceUri['codeUri'].fsPath); + this.resourceDirName.set(resourceUri && resourceUri.path.dir.fsPath(this.isPosix)); + this.resourcePath.set(resourceUri && resourceUri.path.fsPath(this.isPosix)); } protected getLanguageId(uri: URI | undefined): string | undefined { diff --git a/packages/core/src/common/path.spec.ts b/packages/core/src/common/path.spec.ts index cad2fa694323a..9ca3d7907e6ef 100644 --- a/packages/core/src/common/path.spec.ts +++ b/packages/core/src/common/path.spec.ts @@ -356,4 +356,60 @@ describe('Path', () => { }); }); + + describe('fsPath#undefined', () => { + it('should retain windows style path', () => { + const path = 'C:\\path\\to\\file.txt'; + expect(new Path(path).fsPath()).eq(path); + }); + + it('should create windows style path with slashes', () => { + const path = 'C:/path/to/file.txt'; + const expected = 'C:\\path\\to\\file.txt'; + expect(new Path(path).fsPath()).eq(expected); + }); + + it('should retain posix style path', () => { + const path = '/path/to/file.txt'; + expect(new Path(path).fsPath()).eq(path); + }); + }); + + describe('fsPath#windows', () => { + it('should retain windows style path', () => { + const path = 'C:\\path\\to\\file.txt'; + expect(new Path(path).fsPath(false)).eq(path); + }); + + it('should create windows style path with slashes', () => { + const path = 'C:/path/to/file.txt'; + const expected = 'C:\\path\\to\\file.txt'; + expect(new Path(path).fsPath(false)).eq(expected); + }); + + it('should create windows style path from posix', () => { + const path = '/path/to/file.txt'; + const expected = '\\path\\to\\file.txt'; + expect(new Path(path).fsPath(false)).eq(expected); + }); + }); + + describe('fsPath#posix', () => { + it('should retain posix style path', () => { + const path = '/path/to/file.txt'; + expect(new Path(path).fsPath(true)).eq(path); + }); + + it('should create posix style path from windows with slashes', () => { + const path = 'C:/path/to/file.txt'; + const expected = '/c:/path/to/file.txt'; + expect(new Path(path).fsPath(true)).eq(expected); + }); + + it('should create posix style path from windows', () => { + const path = 'C:\\path\\to\\file.txt'; + const expected = '/c:/path/to/file.txt'; + expect(new Path(path).fsPath(true)).eq(expected); + }); + }); }); diff --git a/packages/core/src/common/path.ts b/packages/core/src/common/path.ts index b2613bc31e164..54430b0c99f67 100644 --- a/packages/core/src/common/path.ts +++ b/packages/core/src/common/path.ts @@ -70,6 +70,35 @@ export class Path { return path.split(/[\\]/).join(Path.separator); } + /** + * Creates a windows path from the given path string. + * A windows path uses an upper case drive letter and backwards slashes. + * @param path The input path + * @returns Windows style path + */ + static windowsPath(path: string): string { + const offset = path.charAt(0) === '/' ? 1 : 0; + if (path.charAt(offset + 2) === '/' && path.charAt(offset + 1) === ':') { + const driveLetter = path.charAt(offset).toUpperCase(); + const substring = path.substring(offset + 1).replace(/\//g, '\\'); + return `${driveLetter}${substring}`; + } + return path.replace(/\//g, '\\'); + } + + /** + * Creates a posix path from the given path string. + * A posix path always starts with a forward slash and contains only forward slashes. + * @param path The input path + * @returns A posix style path + */ + static posixPath(path: string): string { + if (path.charAt(0) !== '/') { + path = '/' + path; + } + return path; + } + /** * Tildify path, replacing `home` with `~` if user's `home` is present at the beginning of the path. * This is a non-operation for Windows. @@ -206,6 +235,26 @@ export class Path { return this.raw; } + /** + * Converts the current path into a file system path. + * @param posix Indicates posix or windows file path. + * If `undefined`, the format will be determined by whether the raw path starts with a drive letter. + * @returns A file system path. + */ + fsPath(posix?: boolean): string { + if (posix === undefined) { + if (this.raw.charAt(1) === ':' || this.raw.charAt(2) === ':') { + return Path.windowsPath(this.raw); + } else { + return Path.posixPath(this.raw); + } + } else if (posix) { + return Path.posixPath(this.raw); + } else { + return Path.windowsPath(this.raw); + } + } + relative(path: Path): Path | undefined { if (this.raw === path.raw) { return new Path(''); diff --git a/packages/filesystem/src/browser/file-tree/file-tree-widget.tsx b/packages/filesystem/src/browser/file-tree/file-tree-widget.tsx index 2300015220af4..52fc902f57745 100644 --- a/packages/filesystem/src/browser/file-tree/file-tree-widget.tsx +++ b/packages/filesystem/src/browser/file-tree/file-tree-widget.tsx @@ -100,7 +100,7 @@ export class FileTreeWidget extends TreeViewWelcomeWidget { protected getNodeTooltip(node: TreeNode): string | undefined { const uri = UriSelection.getUri(node); - return uri ? uri.path.toString() : undefined; + return uri ? uri.path.fsPath() : undefined; } protected handleDragStartEvent(node: TreeNode, event: React.DragEvent): void {