Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Plugin-API] Apply ResourceLabelFormatter API #8187

Merged
merged 1 commit into from
Aug 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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