Skip to content

Commit

Permalink
Apply ResourceLabelFormatter API
Browse files Browse the repository at this point in the history
Signed-off-by: Igor Vinokur <ivinokur@redhat.com>
  • Loading branch information
vinokurig committed Aug 6, 2020
1 parent 8d8406d commit 21540c4
Show file tree
Hide file tree
Showing 22 changed files with 394 additions and 105 deletions.
104 changes: 102 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,15 @@
* 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';
import { ResourceLabelFormatter, ResourceLabelFormatting } from '../common/label-protocol';

/**
* @internal don't export it, use `LabelProvider.folderIcon` instead.
Expand Down Expand Up @@ -106,6 +108,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 +163,97 @@ 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) > 0
});
}

// copied and modified from https://github.com/microsoft/vscode/blob/1.44.2/src/vs/workbench/services/label/common/labelService.ts
/*---------------------------------------------------------------------------------------------
* 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);
}

private hasDriveLetter(path: string): boolean {
return !!(path && path[2] === ':');
}

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
35 changes: 35 additions & 0 deletions packages/core/src/common/label-protocol.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/********************************************************************************
* Copyright (C) 2020 Red Hat, Inc. and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

// copied and modified from https://github.com/microsoft/vscode/blob/1.44.2/src/vs/platform/label/common/label.ts#L35-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 ResourceLabelFormatter {
scheme: string;
authority?: string;
priority?: boolean;
formatting: ResourceLabelFormatting;
}

export interface ResourceLabelFormatting {
label: string; // myLabel:/${path}
separator: '/' | '\\' | '';
tildify?: boolean;
normalizeDriveLetter?: boolean;
authorityPrefix?: string;
}
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
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,8 @@ import * as React from 'react';
import URI from '@theia/core/lib/common/uri';
import { injectable, inject, postConstruct } from 'inversify';
import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget';
import { CommandRegistry, isOSX, environment } from '@theia/core/lib/common';
import { CommandRegistry, isOSX, environment, Path } from '@theia/core/lib/common';
import { WorkspaceCommands, WorkspaceService } from '@theia/workspace/lib/browser';
import { FileSystemUtils } from '@theia/filesystem/lib/common/filesystem-utils';
import { KeymapsCommands } from '@theia/keymaps/lib/browser';
import { CommonCommands, LabelProvider } from '@theia/core/lib/browser';
import { ApplicationInfo, ApplicationServer } from '@theia/core/lib/common/application-protocol';
Expand Down Expand Up @@ -258,7 +257,7 @@ export class GettingStartedWidget extends ReactWidget {
workspaces.forEach(workspace => {
const uri = new URI(workspace);
const pathLabel = this.labelProvider.getLongName(uri);
const path = this.home ? FileSystemUtils.tildifyPath(pathLabel, this.home) : pathLabel;
const path = this.home ? Path.tildify(pathLabel, this.home) : pathLabel;
paths.push(path);
});
return paths;
Expand Down
Loading

0 comments on commit 21540c4

Please sign in to comment.