Skip to content

Commit

Permalink
[theming] fix #6788: introduce URIIconReference
Browse files Browse the repository at this point in the history
To distinguis between folder and file URIs.
By default file icons are computed for URIs,
but clients can provide `folder` as an id of `URIIconReference`
to compute folder icons.

Signed-off-by: Anton Kosyakov <anton.kosyakov@typefox.io>
  • Loading branch information
akosyakov committed Dec 27, 2019
1 parent ea270ba commit 7b42a8e
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export class SampleDynamicLabelProviderContribution extends DefaultUriLabelProvi
});
}

private getUri(element: URI): URI {
protected getUri(element: URI): URI {
return new URI(element.toString());
}

Expand All @@ -70,7 +70,7 @@ export class SampleDynamicLabelProviderContribution extends DefaultUriLabelProvi
protected readonly onDidChangeEmitter = new Emitter<DidChangeLabelEvent>();
private x: number = 0;

getName(element: URI): string {
getName(element: URI): string | undefined {
const uri = this.getUri(element);
if (this.isActive && uri.toString().includes('test')) {
return super.getName(uri) + '-' + this.x.toString(10);
Expand All @@ -79,7 +79,7 @@ export class SampleDynamicLabelProviderContribution extends DefaultUriLabelProvi
}
}

getLongName(element: URI): string {
getLongName(element: URI): string | undefined {
const uri = this.getUri(element);
return super.getLongName(uri);
}
Expand Down
11 changes: 9 additions & 2 deletions packages/core/src/browser/label-provider.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
********************************************************************************/

import { expect } from 'chai';
import { DefaultUriLabelProviderContribution } from './label-provider';
import { DefaultUriLabelProviderContribution, URIIconReference } from './label-provider';
import URI from '../common/uri';

describe('DefaultUriLabelProviderContribution', function (): void {
Expand All @@ -41,10 +41,17 @@ describe('DefaultUriLabelProviderContribution', function (): void {
expect(icon).eq('text-icon medium-blue theia-file-icons-js');
});

it('should return icon class for something that seems to be a directory', function (): void {
it('should return file icon class for something that seems to be a directory', function (): void {
const prov = new DefaultUriLabelProviderContribution();
const icon = prov.getIcon(new URI('file:///tmp/hello'));

expect(icon).eq(prov.defaultFileIcon);
});

it('should return folder icon class for something that is a directory', function (): void {
const prov = new DefaultUriLabelProviderContribution();
const icon = prov.getIcon(URIIconReference.create('folder', new URI('file:///tmp/hello')));

expect(icon).eq(prov.defaultFolderIcon);
});
});
52 changes: 35 additions & 17 deletions packages/core/src/browser/label-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,26 +88,38 @@ export interface DidChangeLabelEvent {
affects(element: object): boolean;
}

export interface URIIconReference {
kind: 'uriIconReference';
id: 'file' | 'folder';
uri?: URI
}
export namespace URIIconReference {
// tslint:disable-next-line:no-any
export function is(element: any | undefined): element is URIIconReference {
return !!element && typeof element === 'object' && 'kind' in element && element['kind'] === 'uriIconReference';
}
export function create(id: URIIconReference['id'], uri?: URI): URIIconReference {
return { kind: 'uriIconReference', id, uri };
}
}

@injectable()
export class DefaultUriLabelProviderContribution implements LabelProviderContribution {

canHandle(uri: object): number {
if (uri instanceof URI) {
canHandle(element: object): number {
if (element instanceof URI || URIIconReference.is(element)) {
return 1;
}
return 0;
}

getIcon(uri: URI): string {
const iconClass = this.getFileIcon(uri);
if (!iconClass) {
if (uri.displayName.indexOf('.') === -1) {
return this.defaultFolderIcon;
} else {
return this.defaultFileIcon;
}
getIcon(element: URI | URIIconReference): string {
if (URIIconReference.is(element) && element.id === 'folder') {
return this.defaultFolderIcon;
}
return iconClass;
const uri = URIIconReference.is(element) ? element.uri : element;
const iconClass = uri && this.getFileIcon(uri);
return iconClass || this.defaultFileIcon;
}

get defaultFolderIcon(): string {
Expand All @@ -126,12 +138,18 @@ export class DefaultUriLabelProviderContribution implements LabelProviderContrib
return fileIcon + ' theia-file-icons-js';
}

getName(uri: URI): string {
return uri.displayName;
getName(element: URI | URIIconReference): string | undefined {
const uri = this.getUri(element);
return uri && uri.displayName;
}

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

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

Expand Down Expand Up @@ -183,14 +201,14 @@ export class LabelProvider implements FrontendApplicationContribution {
* Return a default file icon for the current icon theme.
*/
get fileIcon(): string {
return this.getIcon(new URI('file:///foo/foo.txt'));
return this.getIcon(URIIconReference.create('file'));
}

/**
* Return a default folder icon for the current icon theme.
*/
get folderIcon(): string {
return this.getIcon(new URI('file:///foo'));
return this.getIcon(URIIconReference.create('folder'));
}

getIcon(element: object): string {
Expand Down
5 changes: 5 additions & 0 deletions packages/core/src/common/uri.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ export default class URI {
}
}

/**
* TODO move implementation to `DefaultUriLabelProviderContribution.getName`
*
* @deprecated use `LabelProvider.getName` instead
*/
get displayName(): string {
const base = this.path.base;
if (base) {
Expand Down
28 changes: 17 additions & 11 deletions packages/plugin-ext/src/main/browser/plugin-icon-theme-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import URI from '@theia/core/lib/common/uri';
import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposable';
import { Emitter } from '@theia/core/lib/common/event';
import { RecursivePartial } from '@theia/core/lib/common/types';
import { LabelProviderContribution, DidChangeLabelEvent, LabelProvider } from '@theia/core/lib/browser/label-provider';
import { LabelProviderContribution, DidChangeLabelEvent, LabelProvider, URIIconReference } from '@theia/core/lib/browser/label-provider';
import { ThemeType } from '@theia/core/lib/browser/theming';
import { FileStatNode, DirNode, FileSystemWatcher, FileChangeEvent } from '@theia/filesystem/lib/browser';
import { WorkspaceRootNode } from '@theia/navigator/lib/browser/navigator-tree';
Expand Down Expand Up @@ -457,7 +457,7 @@ export class PluginIconTheme extends PluginIconThemeDefinition implements IconTh
* This should be aligned with
* https://github.com/microsoft/vscode/blob/7cf4cca47aa025a590fc939af54932042302be63/src/vs/editor/common/services/getIconClasses.ts#L5
*/
getIcon(element: URI | FileStat | FileStatNode | WorkspaceRootNode): string {
getIcon(element: URI | URIIconReference | FileStat | FileStatNode | WorkspaceRootNode): string {
let icon = '';
for (const className of this.getClassNames(element)) {
if (this.icons.has(className)) {
Expand All @@ -470,14 +470,15 @@ export class PluginIconTheme extends PluginIconThemeDefinition implements IconTh
return icon;
}

protected getClassNames(element: URI | FileStat | FileStatNode | WorkspaceRootNode): string[] {
protected getClassNames(element: URI | URIIconReference | FileStat | FileStatNode | WorkspaceRootNode): string[] {
if (WorkspaceRootNode.is(element)) {
const name = this.labelProvider.getName(element);
if (element.expanded) {
return [this.rootFolderExpandedIcon, this.expandedFolderNameIcon(name)];
}
return [this.rootFolderIcon, this.folderNameIcon(name)];
} if (DirNode.is(element)) {
}
if (DirNode.is(element)) {
if (element.expanded) {
const name = this.labelProvider.getName(element);
return [this.folderExpandedIcon, this.expandedFolderNameIcon(name)];
Expand All @@ -493,8 +494,11 @@ export class PluginIconTheme extends PluginIconThemeDefinition implements IconTh
}
return this.getFileClassNames(element, element.uri);
}
if (!element.path.ext) {
return this.getFolderClassNames(element);
if (URIIconReference.is(element)) {
if (element.id === 'folder') {
return this.getFolderClassNames(element);
}
return this.getFileClassNames(element, element.uri && element.uri.toString());
}
return this.getFileClassNames(element, element.toString());
}
Expand All @@ -504,12 +508,14 @@ export class PluginIconTheme extends PluginIconThemeDefinition implements IconTh
return [this.folderIcon, this.folderNameIcon(name)];
}

protected getFileClassNames(element: URI | FileStat | FileStatNode, uri: string): string[] {
protected getFileClassNames(element: object, uri?: string): string[] {
const name = this.labelProvider.getName(element);
const classNames = this.fileNameIcon(name);
if (uri) {
const language = monaco.services.StaticServices.modeService.get().createByFilepathOrFirstLine(monaco.Uri.parse(uri));
classNames.push(this.languageIcon(language.languageIdentifier.language));
}
classNames.unshift(this.fileIcon);
const language = monaco.services.StaticServices.modeService.get().createByFilepathOrFirstLine(monaco.Uri.parse(uri));
classNames.push(this.languageIcon(language.languageIdentifier.language));
return classNames;
}

Expand Down Expand Up @@ -553,14 +559,14 @@ export class PluginIconThemeService implements LabelProviderContribution {
canHandle(element: object): number {
const current = this.iconThemeService.getDefinition(this.iconThemeService.current);
if (current instanceof PluginIconTheme && (
(element instanceof URI && element.scheme === 'file') || FileStat.is(element) || FileStatNode.is(element)
(element instanceof URI && element.scheme === 'file') || URIIconReference.is(element) || FileStat.is(element) || FileStatNode.is(element)
)) {
return Number.MAX_SAFE_INTEGER;
}
return 0;
}

getIcon(element: URI | FileStat | FileStatNode | WorkspaceRootNode): string | undefined {
getIcon(element: URI | URIIconReference | FileStat | FileStatNode | WorkspaceRootNode): string | undefined {
const current = this.iconThemeService.getDefinition(this.iconThemeService.current);
if (current instanceof PluginIconTheme) {
return current.getIcon(element);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,10 @@

import { injectable, inject } from 'inversify';
import URI from '@theia/core/lib/common/uri';
import { LabelProviderContribution, LabelProvider } from '@theia/core/lib/browser/label-provider';
import { LabelProviderContribution, LabelProvider, URIIconReference } from '@theia/core/lib/browser/label-provider';
import { TreeLabelProvider } from '@theia/core/lib/browser/tree/tree-label-provider';
import { TreeViewNode } from './tree-view-widget';
import { TreeNode } from '@theia/core/lib/browser/tree/tree';
import { FileStat } from '@theia/filesystem/lib/common';

@injectable()
export class PluginTreeViewNodeLabelProvider implements LabelProviderContribution {
Expand All @@ -44,10 +43,8 @@ export class PluginTreeViewNodeLabelProvider implements LabelProviderContributio
return node.icon;
}
if (node.themeIconId) {
const isDirectory = node.themeIconId === 'folder';
const uri = node.resourceUri || (isDirectory ? 'file:///foo' : 'file:///foo.txt');
const fileStat: FileStat = { uri, lastModification: 0, isDirectory };
return this.labelProvider.getIcon(fileStat);
const uri = node.resourceUri && new URI(node.resourceUri) || undefined;
return this.labelProvider.getIcon(URIIconReference.create(node.themeIconId, uri));
}
if (node.resourceUri) {
return this.labelProvider.getIcon(new URI(node.resourceUri));
Expand Down
46 changes: 23 additions & 23 deletions packages/workspace/src/browser/workspace-uri-contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import { DefaultUriLabelProviderContribution } from '@theia/core/lib/browser/label-provider';
import { DefaultUriLabelProviderContribution, URIIconReference } from '@theia/core/lib/browser/label-provider';
import URI from '@theia/core/lib/common/uri';
import { injectable, inject, postConstruct } from 'inversify';
import { FileStat } from '@theia/filesystem/lib/common';
Expand All @@ -32,40 +32,40 @@ export class WorkspaceUriLabelProviderContribution extends DefaultUriLabelProvid
}

canHandle(element: object): number {
if ((element instanceof URI && element.scheme === 'file' || FileStat.is(element))) {
if ((element instanceof URI && element.scheme === 'file' || URIIconReference.is(element) || FileStat.is(element))) {
return 10;
}
return 0;
}

private getUri(element: URI | FileStat): URI {
if (FileStat.is(element)) {
return new URI(element.uri);
}
return new URI(element.toString());
}

getIcon(element: URI | FileStat): string {
if (!FileStat.is(element)) {
return super.getIcon(element);
}
if (element.isDirectory) {
return this.defaultFolderIcon;
}
const icon = super.getFileIcon(new URI(element.uri));
return icon || this.defaultFileIcon;
getIcon(element: URI | URIIconReference | FileStat): string {
return super.getIcon(this.asURIIconReference(element));
}

getName(element: URI | FileStat): string {
return super.getName(this.getUri(element));
getName(element: URI | URIIconReference | FileStat): string | undefined {
return super.getName(this.asURIIconReference(element));
}

/**
* trims the workspace root from a file uri, if it is a child.
*/
getLongName(element: URI | FileStat): string {
getLongName(element: URI | URIIconReference | FileStat): string | undefined {
const uri = this.getUri(element);
const relativePath = this.workspaceVariable.getWorkspaceRelativePath(uri);
return relativePath || super.getLongName(uri);
const relativePath = uri && this.workspaceVariable.getWorkspaceRelativePath(uri);
return relativePath || super.getLongName(this.asURIIconReference(element));
}

protected asURIIconReference(element: URI | URIIconReference | FileStat): URI | URIIconReference {
if (FileStat.is(element)) {
return URIIconReference.create(element.isDirectory ? 'folder' : 'file', new URI(element.uri));
}
return element;
}

protected getUri(element: URI | URIIconReference | FileStat): URI | undefined {
if (FileStat.is(element)) {
return new URI(element.uri);
}
return super.getUri(element);
}
}

0 comments on commit 7b42a8e

Please sign in to comment.