Skip to content

Commit

Permalink
Apply ResourceLabelFormatter API
Browse files Browse the repository at this point in the history
Signed-off-by: Igor Vinokur <[email protected]>
  • Loading branch information
vinokurig committed Aug 4, 2020
1 parent fe24630 commit 52fc292
Show file tree
Hide file tree
Showing 18 changed files with 366 additions and 102 deletions.
148 changes: 146 additions & 2 deletions packages/core/src/browser/label-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import { inject, injectable, named } from 'inversify';
import { inject, injectable, named, postConstruct } from 'inversify';
import * as fileIcons from 'file-icons-js';
import URI from '../common/uri';
import { ContributionProvider } from '../common/contribution-provider';
import { Prioritizeable } from '../common/types';
import { Event, Emitter } from '../common';
import { Event, Emitter, Disposable, Path } from '../common';
import { FrontendApplicationContribution } from './frontend-application';
import { EnvVariablesServer } from '../common/env-variables/env-variables-protocol';

/**
* @internal don't export it, use `LabelProvider.folderIcon` instead.
Expand Down Expand Up @@ -88,6 +89,41 @@ export interface DidChangeLabelEvent {
affects(element: object): boolean;
}

// copied and modified from https://github.com/microsoft/vscode/blob/1.44.2/src/vs/platform/label/common/label.ts#L31-L33
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export interface FormatterChangeEvent {
scheme: string;
}

// copied from https://github.com/microsoft/vscode/blob/1.44.2/src/vs/platform/label/common/label.ts#L35-L40
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export interface ResourceLabelFormatter {
scheme: string;
authority?: string;
priority?: boolean;
formatting: ResourceLabelFormatting;
}

// copied from https://github.com/microsoft/vscode/blob/1.44.2/src/vs/platform/label/common/label.ts#L42-L49
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export interface ResourceLabelFormatting {
label: string; // myLabel:/${path}
separator: '/' | '\\' | '';
tildify?: boolean;
normalizeDriveLetter?: boolean;
workspaceSuffix?: string;
authorityPrefix?: string;
}

export interface URIIconReference {
kind: 'uriIconReference';
id: 'file' | 'folder';
Expand All @@ -106,6 +142,19 @@ export namespace URIIconReference {
@injectable()
export class DefaultUriLabelProviderContribution implements LabelProviderContribution {

protected formatters: ResourceLabelFormatter[] = [];
protected readonly onDidChangeEmitter = new Emitter<DidChangeLabelEvent>();
protected homePath: string | undefined;
@inject(EnvVariablesServer) protected readonly envVariablesServer: EnvVariablesServer;

@postConstruct()
init(): void {
this.envVariablesServer.getHomeDirUri().then(result => {
this.homePath = result;
this.fireOnDidChange();
});
}

canHandle(element: object): number {
if (element instanceof URI || URIIconReference.is(element)) {
return 1;
Expand Down Expand Up @@ -148,12 +197,107 @@ export class DefaultUriLabelProviderContribution implements LabelProviderContrib

getLongName(element: URI | URIIconReference): string | undefined {
const uri = this.getUri(element);
if (uri) {
const formatting = this.findFormatting(uri);
if (formatting) {
return this.formatUri(uri, formatting);
}
}
return uri && uri.path.toString();
}

protected getUri(element: URI | URIIconReference): URI | undefined {
return URIIconReference.is(element) ? element.uri : element;
}

registerFormatter(formatter: ResourceLabelFormatter): Disposable {
this.formatters.push(formatter);
this.fireOnDidChange();
return Disposable.create(() => {
this.formatters = this.formatters.filter(f => f !== formatter);
this.fireOnDidChange();
});
}

get onDidChange(): Event<DidChangeLabelEvent> {
return this.onDidChangeEmitter.event;
}

private fireOnDidChange(): void {
this.onDidChangeEmitter.fire({
affects: (element: URI) => this.canHandle(element) >= 1
});
}

// copied and modified from https://github.com/microsoft/vscode/blob/1.44.2/src/vs/workbench/services/label/common/labelService.ts#L240-L274
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
private readonly labelMatchingRegexp = /\${(scheme|authority|path|query)}/g;
protected formatUri(resource: URI, formatting: ResourceLabelFormatting): string {
let label = formatting.label.replace(this.labelMatchingRegexp, (match, token) => {
switch (token) {
case 'scheme': return resource.scheme;
case 'authority': return resource.authority;
case 'path': return resource.path.toString();
case 'query': return resource.query;
default: return '';
}
});

// convert \c:\something => C:\something
if (formatting.normalizeDriveLetter && this.hasDriveLetter(label)) {
label = label.charAt(1).toUpperCase() + label.substr(2);
}

if (formatting.tildify) {
label = Path.tildify(label, this.homePath ? this.homePath : '');
}
if (formatting.authorityPrefix && resource.authority) {
label = formatting.authorityPrefix + label;
}

return label.replace(/\//g, formatting.separator);
}

// copied and modified from https://github.com/microsoft/vscode/blob/1.44.2/src/vs/workbench/services/label/common/labelService.ts#L72-L74
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
private hasDriveLetter(path: string): boolean {
return !!(path && path[2] === ':');
}

// copied and modified from https://github.com/microsoft/vscode/blob/1.44.2/src/vs/workbench/services/label/common/labelService.ts#L108-L128
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
protected findFormatting(resource: URI): ResourceLabelFormatting | undefined {
let bestResult: ResourceLabelFormatter | undefined;

this.formatters.forEach(formatter => {
if (formatter.scheme === resource.scheme) {
if (!bestResult && !formatter.authority) {
bestResult = formatter;
return;
}
if (!formatter.authority) {
return;
}

if ((formatter.authority.toLowerCase() === resource.authority.toLowerCase()) &&
(!bestResult || !bestResult.authority || formatter.authority.length > bestResult.authority.length ||
((formatter.authority.length === bestResult.authority.length) && formatter.priority))) {
bestResult = formatter;
}
}
});

return bestResult ? bestResult.formatting : undefined;
}
}

@injectable()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export class MockEnvVariablesServerImpl implements EnvVariablesServer {
constructor(protected readonly configDirUri: URI) { }

getHomeDirUri(): Promise<string> {
throw new Error('Method not implemented.');
return Promise.resolve('');
}
getDrives(): Promise<string[]> {
throw new Error('Method not implemented.');
Expand Down
49 changes: 49 additions & 0 deletions packages/core/src/common/path.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import * as assert from 'assert';
import { Path } from './path';
import { expect } from 'chai';

describe('Path', () => {

Expand Down Expand Up @@ -256,4 +257,52 @@ describe('Path', () => {
});
}

const linuxHome = '/home/test-user';
const windowsHome = '/C:/Users/test-user';

describe('Linux', () => {
it('should shorten path on Linux, path starting with home', async () => {
const path = `${linuxHome}/a/b/theia`;
const expected = '~/a/b/theia';
expect(Path.tildify(path, linuxHome)).eq(expected);
});

it('should shorten path on Linux, path starting with home with duplication', async () => {
const path = `${linuxHome}/${linuxHome}/a/b/theia`;
const expected = `~/${linuxHome}/a/b/theia`;
expect(Path.tildify(path, linuxHome)).eq(expected);
});

it('should not shorten path on Linux, path not starting with home', async () => {
const path = `/test/${linuxHome}/a/b/theia`;
const expected = `/test/${linuxHome}/a/b/theia`;
expect(Path.tildify(path, linuxHome)).eq(expected);
});

it('should not shorten path on Linux, path not starting with correct home', async () => {
const path = `/test/${linuxHome}123/a/b/theia`;
const expected = `/test/${linuxHome}123/a/b/theia`;
expect(Path.tildify(path, linuxHome)).eq(expected);
});

it('should not shorten path on Linux when home is empty', async () => {
const path = `${linuxHome}/a/b/theia`;
const expected = `${linuxHome}/a/b/theia`;
expect(Path.tildify(path, '')).eq(expected);
});
});

describe('Windows', () => {
it('should not shorten path on Windows', async () => {
const path = `${windowsHome}/a/b/theia`;
const expected = `${windowsHome}/a/b/theia`;
expect(Path.tildify(path, windowsHome)).eq(expected);
});

it('should not shorten path on Windows when home is empty', async () => {
const path = `${windowsHome}/a/b/theia`;
const expected = `${windowsHome}/a/b/theia`;
expect(Path.tildify(path, '')).eq(expected);
});
});
});
18 changes: 18 additions & 0 deletions packages/core/src/common/path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,24 @@ export class 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.
*
* @param resourcePath
* @param home
*/
static tildify(resourcePath: string, home: string): string {
const path = new Path(resourcePath);
const isWindows = path.root && Path.isDrive(path.root.base);

if (!isWindows && home && resourcePath.indexOf(`${home}/`) === 0) {
return resourcePath.replace(`${home}/`, '~/');
}

return resourcePath;
}

readonly isAbsolute: boolean;
readonly isRoot: boolean;
readonly root: Path | undefined;
Expand Down
2 changes: 1 addition & 1 deletion packages/editor/src/browser/editor-widget-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,11 @@ export class EditorWidgetFactory implements WidgetFactory {

newEditor.id = this.id + ':' + uri.toString();
newEditor.title.closable = true;
newEditor.title.caption = uri.path.toString();
return newEditor;
}

private setLabels(editor: EditorWidget, uri: URI): void {
editor.title.caption = this.labelProvider.getLongName(uri);
const icon = this.labelProvider.getIcon(uri);
editor.title.label = this.labelProvider.getName(uri);
editor.title.iconClass = icon + ' file-icon';
Expand Down
70 changes: 0 additions & 70 deletions packages/filesystem/src/common/filesystem-utils.spec.ts

This file was deleted.

19 changes: 0 additions & 19 deletions packages/filesystem/src/common/filesystem-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,9 @@

import { FileStat } from '../common/files';
import URI from '@theia/core/lib/common/uri';
import { Path } from '@theia/core/lib/common';

export namespace FileSystemUtils {

/**
* Tildify path, replacing `home` with `~` if user's `home` is present at the beginning of the path.
* This is a non-operation for Windows.
*
* @param resourcePath
* @param home
*/
export function tildifyPath(resourcePath: string, home: string): string {
const path = new Path(resourcePath);
const isWindows = path.root && Path.isDrive(path.root.base);

if (!isWindows && home && resourcePath.indexOf(`${home}/`) === 0) {
return resourcePath.replace(`${home}/`, '~/');
}

return resourcePath;
}

/**
* Generate unique URI for a given parent which does not collide
*
Expand Down
Loading

0 comments on commit 52fc292

Please sign in to comment.