deploy: current vibn theia state
Made-with: Cursor
This commit is contained in:
10
packages/property-view/.eslintrc.js
Normal file
10
packages/property-view/.eslintrc.js
Normal file
@@ -0,0 +1,10 @@
|
||||
/** @type {import('eslint').Linter.Config} */
|
||||
module.exports = {
|
||||
extends: [
|
||||
'../../configs/build.eslintrc.json'
|
||||
],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: 'tsconfig.json'
|
||||
}
|
||||
};
|
||||
42
packages/property-view/README.md
Normal file
42
packages/property-view/README.md
Normal file
@@ -0,0 +1,42 @@
|
||||
<div align='center'>
|
||||
|
||||
<br />
|
||||
|
||||
<img src='https://raw.githubusercontent.com/eclipse-theia/theia/master/logo/theia.svg?sanitize=true' alt='theia-ext-logo' width='100px' />
|
||||
|
||||
<h2>ECLIPSE THEIA - PROPERTY-VIEW EXTENSION</h2>
|
||||
|
||||
<hr />
|
||||
|
||||
</div>
|
||||
|
||||
## Description
|
||||
|
||||
The `@theia/property-view` extension contributes a generic, global property view based on Theia's global selection.
|
||||
|
||||
The property view widget can be opened/toggled either via menu _View->Properties_ or via shortcut <kbd>Shift+Alt+P</kbd>. It is located in the bottom dock area by default.
|
||||
|
||||
The following two default content widgets are implemented in this extension:
|
||||
|
||||
- EmptyPropertyViewWidget: If no other widget can be provided, a simple message (_No properties available_) is shown.
|
||||
- ResourcePropertyViewWidget: Can handle `FileSelection`s and `Navigatable` selections (which provide their resource URI) and displays the general `FileStat` information (e.g. location, name, last modified) in a TreeWidget.
|
||||
|
||||
To contribute a specific property view, it is necessary to implement a `PropertyViewDataService` which gathers the property data for a selection as well as a `PropertyViewWidgetProvider` which provides a suitable content widget to display the property data for a specific selection inside the property view widget.
|
||||
|
||||
</br>
|
||||
|
||||
## Additional Information
|
||||
|
||||
- [API documentation for `@theia/property-view`](https://eclipse-theia.github.io/theia/docs/next/modules/_theia_property-view.html)
|
||||
- [Theia - GitHub](https://github.com/eclipse-theia/theia)
|
||||
- [Theia - Website](https://theia-ide.org/)
|
||||
|
||||
## License
|
||||
|
||||
- [Eclipse Public License 2.0](http://www.eclipse.org/legal/epl-2.0/)
|
||||
- [一 (Secondary) GNU General Public License, version 2 with the GNU Classpath Exception](https://projects.eclipse.org/license/secondary-gpl-2.0-cp)
|
||||
|
||||
## Trademark
|
||||
|
||||
"Theia" is a trademark of the Eclipse Foundation
|
||||
<https://www.eclipse.org/theia>
|
||||
49
packages/property-view/package.json
Normal file
49
packages/property-view/package.json
Normal file
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"name": "@theia/property-view",
|
||||
"version": "1.68.0",
|
||||
"description": "Theia - Property View Extension",
|
||||
"dependencies": {
|
||||
"@theia/core": "1.68.0",
|
||||
"@theia/filesystem": "1.68.0",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"theiaExtensions": [
|
||||
{
|
||||
"frontend": "lib/browser/property-view-frontend-module"
|
||||
}
|
||||
],
|
||||
"keywords": [
|
||||
"theia-extension"
|
||||
],
|
||||
"license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/eclipse-theia/theia.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/eclipse-theia/theia/issues"
|
||||
},
|
||||
"homepage": "https://github.com/eclipse-theia/theia",
|
||||
"files": [
|
||||
"lib",
|
||||
"src"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "theiaext build",
|
||||
"clean": "theiaext clean",
|
||||
"compile": "theiaext compile",
|
||||
"lint": "theiaext lint",
|
||||
"test": "theiaext test",
|
||||
"watch": "theiaext watch"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@theia/ext-scripts": "1.68.0"
|
||||
},
|
||||
"nyc": {
|
||||
"extends": "../../configs/nyc.json"
|
||||
},
|
||||
"gitHead": "21358137e41342742707f660b8e222f940a27652"
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
// *****************************************************************************
|
||||
// Copyright (C) 2020 EclipseSource 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-only WITH Classpath-exception-2.0
|
||||
// *****************************************************************************
|
||||
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
import { ReactWidget } from '@theia/core/lib/browser';
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import { PropertyViewContentWidget } from './property-view-content-widget';
|
||||
import { DefaultPropertyViewWidgetProvider } from './property-view-widget-provider';
|
||||
|
||||
/**
|
||||
* Property view widget that is shown if no property data or selection is available.
|
||||
* This widget is provided by the {@link EmptyPropertyViewWidgetProvider}.
|
||||
*/
|
||||
class EmptyPropertyViewWidget extends ReactWidget implements PropertyViewContentWidget {
|
||||
|
||||
static readonly ID = 'theia-empty-property-view';
|
||||
static readonly LABEL = 'No Properties';
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.id = EmptyPropertyViewWidget.ID;
|
||||
this.title.label = EmptyPropertyViewWidget.LABEL;
|
||||
this.title.caption = EmptyPropertyViewWidget.LABEL;
|
||||
this.title.closable = false;
|
||||
this.node.tabIndex = 0;
|
||||
}
|
||||
|
||||
updatePropertyViewContent(): void {
|
||||
this.update();
|
||||
}
|
||||
|
||||
protected render(): React.ReactNode {
|
||||
return this.emptyComponent;
|
||||
}
|
||||
|
||||
protected emptyComponent: JSX.Element = <div className={'theia-widget-noInfo'}>{nls.localize('theia/property-view/noProperties', 'No properties available.')}</div>;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* `EmptyPropertyViewWidgetProvider` is implemented to provide the {@link EmptyPropertyViewWidget}
|
||||
* if the given selection is undefined or no other provider can handle the given selection.
|
||||
*/
|
||||
@injectable()
|
||||
export class EmptyPropertyViewWidgetProvider extends DefaultPropertyViewWidgetProvider {
|
||||
|
||||
static readonly ID = 'no-properties';
|
||||
override readonly id = EmptyPropertyViewWidgetProvider.ID;
|
||||
override readonly label = 'DefaultPropertyViewWidgetProvider';
|
||||
|
||||
private emptyWidget: EmptyPropertyViewWidget;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.emptyWidget = new EmptyPropertyViewWidget();
|
||||
}
|
||||
|
||||
override canHandle(selection: Object | undefined): number {
|
||||
return selection === undefined ? 1 : 0;
|
||||
}
|
||||
|
||||
override provideWidget(selection: Object | undefined): Promise<EmptyPropertyViewWidget> {
|
||||
return Promise.resolve(this.emptyWidget);
|
||||
}
|
||||
|
||||
override updateContentWidget(selection: Object | undefined): void {
|
||||
this.emptyWidget.updatePropertyViewContent();
|
||||
}
|
||||
}
|
||||
48
packages/property-view/src/browser/property-data-service.ts
Normal file
48
packages/property-view/src/browser/property-data-service.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
// *****************************************************************************
|
||||
// Copyright (C) 2020 EclipseSource 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-only WITH Classpath-exception-2.0
|
||||
// *****************************************************************************
|
||||
|
||||
export const PropertyDataService = Symbol('PropertyDataService');
|
||||
/**
|
||||
* `PropertyDataService` should be implemented to provide property data for the given selection.
|
||||
*/
|
||||
export interface PropertyDataService {
|
||||
|
||||
/**
|
||||
* A unique id for this provider.
|
||||
*/
|
||||
readonly id: string;
|
||||
/**
|
||||
* A human-readable name for this provider.
|
||||
*/
|
||||
readonly label?: string;
|
||||
|
||||
/**
|
||||
* Test whether this provider can provide property data for the given selection.
|
||||
* Return a nonzero number if this provider can provide; otherwise it cannot.
|
||||
* Never reject.
|
||||
*
|
||||
* A returned value indicating a priority of this provider.
|
||||
*/
|
||||
canHandleSelection(selection: Object | undefined): number;
|
||||
|
||||
/**
|
||||
* Provide property data for the given selection.
|
||||
* Resolve to a property view content widget.
|
||||
* Never reject if `canHandle` returns a positive number; otherwise should reject.
|
||||
*/
|
||||
providePropertyData(selection: Object | undefined): Promise<Object | undefined>;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// *****************************************************************************
|
||||
// Copyright (C) 2020 EclipseSource 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-only WITH Classpath-exception-2.0
|
||||
// *****************************************************************************
|
||||
|
||||
import { Widget } from '@theia/core/lib/browser/widgets/widget';
|
||||
import { PropertyDataService } from './property-data-service';
|
||||
|
||||
/**
|
||||
* A widget that fetches the property data via the given {@link PropertyDataService} and the given selection
|
||||
* and renders that property data.
|
||||
* This widget can be provided by a registered `PropertyViewWidgetProvider`.
|
||||
*/
|
||||
export interface PropertyViewContentWidget extends Widget {
|
||||
updatePropertyViewContent(propertyDataService?: PropertyDataService, selection?: Object): void;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// *****************************************************************************
|
||||
// Copyright (C) 2020 EclipseSource 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-only WITH Classpath-exception-2.0
|
||||
// *****************************************************************************
|
||||
|
||||
import { AbstractViewContribution } from '@theia/core/lib/browser/shell/view-contribution';
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import { PropertyViewWidget } from './property-view-widget';
|
||||
|
||||
@injectable()
|
||||
export class PropertyViewContribution extends AbstractViewContribution<PropertyViewWidget> {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
widgetId: PropertyViewWidget.ID,
|
||||
widgetName: PropertyViewWidget.LABEL,
|
||||
defaultWidgetOptions: {
|
||||
area: 'bottom'
|
||||
},
|
||||
toggleCommandId: 'property-view:toggle',
|
||||
toggleKeybinding: 'shift+alt+p'
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// *****************************************************************************
|
||||
// Copyright (C) 2020 EclipseSource 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-only WITH Classpath-exception-2.0
|
||||
// *****************************************************************************
|
||||
|
||||
import { bindViewContribution, WidgetFactory } from '@theia/core/lib/browser';
|
||||
import { bindContributionProvider } from '@theia/core/lib/common/contribution-provider';
|
||||
import { ContainerModule } from '@theia/core/shared/inversify';
|
||||
import { EmptyPropertyViewWidgetProvider } from './empty-property-view-widget-provider';
|
||||
import { PropertyDataService } from './property-data-service';
|
||||
import { PropertyViewContribution } from './property-view-contribution';
|
||||
import { PropertyViewService } from './property-view-service';
|
||||
import { PropertyViewWidget } from './property-view-widget';
|
||||
import { PropertyViewWidgetProvider } from './property-view-widget-provider';
|
||||
import { bindResourcePropertyView } from './resource-property-view';
|
||||
import '../../src/browser/style/property-view.css';
|
||||
|
||||
export default new ContainerModule(bind => {
|
||||
bind(PropertyViewService).toSelf().inSingletonScope();
|
||||
|
||||
bindContributionProvider(bind, PropertyDataService);
|
||||
bindContributionProvider(bind, PropertyViewWidgetProvider);
|
||||
|
||||
bind(EmptyPropertyViewWidgetProvider).toSelf().inSingletonScope();
|
||||
bind(PropertyViewWidgetProvider).to(EmptyPropertyViewWidgetProvider);
|
||||
|
||||
bind(PropertyViewWidget).toSelf();
|
||||
bind(WidgetFactory).toDynamicValue(({ container }) => ({
|
||||
id: PropertyViewWidget.ID,
|
||||
createWidget: () => container.get(PropertyViewWidget)
|
||||
})).inSingletonScope();
|
||||
|
||||
bindViewContribution(bind, PropertyViewContribution);
|
||||
|
||||
bindResourcePropertyView(bind);
|
||||
});
|
||||
62
packages/property-view/src/browser/property-view-service.ts
Normal file
62
packages/property-view/src/browser/property-view-service.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
// *****************************************************************************
|
||||
// Copyright (C) 2020 EclipseSource 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-only WITH Classpath-exception-2.0
|
||||
// *****************************************************************************
|
||||
|
||||
import { ContributionProvider, Prioritizeable } from '@theia/core';
|
||||
import { inject, injectable, named, postConstruct } from '@theia/core/shared/inversify';
|
||||
import { EmptyPropertyViewWidgetProvider } from './empty-property-view-widget-provider';
|
||||
import { PropertyViewWidgetProvider } from './property-view-widget-provider';
|
||||
|
||||
/**
|
||||
* `PropertyViewService` provides an access to existing property view widget providers.
|
||||
*/
|
||||
@injectable()
|
||||
export class PropertyViewService {
|
||||
|
||||
@inject(ContributionProvider) @named(PropertyViewWidgetProvider)
|
||||
private readonly contributions: ContributionProvider<PropertyViewWidgetProvider>;
|
||||
|
||||
@inject(EmptyPropertyViewWidgetProvider)
|
||||
private readonly emptyWidgetProvider: EmptyPropertyViewWidgetProvider;
|
||||
|
||||
private providers: PropertyViewWidgetProvider[] = [];
|
||||
|
||||
@postConstruct()
|
||||
init(): void {
|
||||
this.providers = this.providers.concat(this.contributions.getContributions());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a property view widget provider with the highest priority for the given selection.
|
||||
* Never reject, return the default provider ({@link EmptyPropertyViewWidgetProvider};
|
||||
* displays `No properties available`) if there are no other matches.
|
||||
*/
|
||||
async getProvider(selection: Object | undefined): Promise<PropertyViewWidgetProvider> {
|
||||
const provider = await this.prioritize(selection);
|
||||
return provider ?? this.emptyWidgetProvider;
|
||||
}
|
||||
|
||||
protected async prioritize(selection: Object | undefined): Promise<PropertyViewWidgetProvider | undefined> {
|
||||
const prioritized = await Prioritizeable.prioritizeAll(this.providers, async (provider: PropertyViewWidgetProvider) => {
|
||||
try {
|
||||
return await provider.canHandle(selection);
|
||||
} catch {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
return prioritized.length !== 0 ? prioritized[0].value : undefined;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
// *****************************************************************************
|
||||
// Copyright (C) 2020 EclipseSource 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-only WITH Classpath-exception-2.0
|
||||
// *****************************************************************************
|
||||
|
||||
import { ContributionProvider, MaybePromise, Prioritizeable } from '@theia/core';
|
||||
import { inject, injectable, named, postConstruct } from '@theia/core/shared/inversify';
|
||||
import { PropertyDataService } from './property-data-service';
|
||||
import { PropertyViewContentWidget } from './property-view-content-widget';
|
||||
|
||||
export const PropertyViewWidgetProvider = Symbol('PropertyViewWidgetProvider');
|
||||
/**
|
||||
* The `PropertyViewWidgetProvider` should be implemented to provide a property view content widget for the given selection..
|
||||
*/
|
||||
export interface PropertyViewWidgetProvider {
|
||||
/**
|
||||
* A unique id for this provider.
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* A human-readable name for this provider.
|
||||
*/
|
||||
label?: string;
|
||||
|
||||
/**
|
||||
* Test whether this provider can provide a widget for the given selection.
|
||||
* A returned value indicating a priority of this provider.
|
||||
*
|
||||
* @param selection the global selection object
|
||||
* @returns a nonzero number if this provider can provide; otherwise it cannot; never reject
|
||||
*/
|
||||
canHandle(selection: Object | undefined): MaybePromise<number>;
|
||||
|
||||
/**
|
||||
* Provide a widget for the given selection.
|
||||
* Never reject if `canHandle` return a positive number; otherwise should reject.
|
||||
*
|
||||
* @param selection the global selection object
|
||||
* @returns a resolved property view content widget.
|
||||
*/
|
||||
provideWidget(selection: Object | undefined): Promise<PropertyViewContentWidget>;
|
||||
|
||||
/**
|
||||
* Update the widget with the given selection.
|
||||
* Never reject if `canHandle` return a positive number; otherwise should reject.
|
||||
*
|
||||
* @param selection the global selection object
|
||||
* @returns a resolved property view content widget.
|
||||
*/
|
||||
updateContentWidget(selection: Object | undefined): void;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The `DefaultPropertyViewWidgetProvider` is the default abstract implementation of the {@link PropertyViewWidgetProvider}
|
||||
* and should be extended to provide a property view content widget for the given selection.
|
||||
*/
|
||||
@injectable()
|
||||
export abstract class DefaultPropertyViewWidgetProvider implements PropertyViewWidgetProvider {
|
||||
|
||||
@inject(ContributionProvider) @named(PropertyDataService)
|
||||
protected readonly contributions: ContributionProvider<PropertyDataService>;
|
||||
|
||||
protected propertyDataServices: PropertyDataService[] = [];
|
||||
|
||||
id = 'default';
|
||||
label = 'DefaultPropertyViewWidgetProvider';
|
||||
|
||||
@postConstruct()
|
||||
init(): void {
|
||||
this.propertyDataServices = this.propertyDataServices.concat(this.contributions.getContributions());
|
||||
}
|
||||
|
||||
canHandle(selection: Object | undefined): MaybePromise<number> {
|
||||
return 0;
|
||||
}
|
||||
|
||||
provideWidget(selection: Object | undefined): Promise<PropertyViewContentWidget> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
updateContentWidget(selection: Object | undefined): void {
|
||||
// no-op
|
||||
}
|
||||
|
||||
protected async getPropertyDataService(selection: Object | undefined): Promise<PropertyDataService> {
|
||||
const dataService = await this.prioritize(selection);
|
||||
return dataService ?? this.propertyDataServices[0];
|
||||
}
|
||||
|
||||
protected async prioritize(selection: Object | undefined): Promise<PropertyDataService | undefined> {
|
||||
const prioritized = await Prioritizeable.prioritizeAll(this.propertyDataServices, async (service: PropertyDataService) => {
|
||||
try {
|
||||
return service.canHandleSelection(selection);
|
||||
} catch {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
return prioritized.length !== 0 ? prioritized[0].value : undefined;
|
||||
}
|
||||
}
|
||||
118
packages/property-view/src/browser/property-view-widget.tsx
Normal file
118
packages/property-view/src/browser/property-view-widget.tsx
Normal file
@@ -0,0 +1,118 @@
|
||||
// *****************************************************************************
|
||||
// Copyright (C) 2020 EclipseSource 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-only WITH Classpath-exception-2.0
|
||||
// *****************************************************************************
|
||||
|
||||
import { Message } from '@theia/core/shared/@lumino/messaging';
|
||||
import { Disposable, SelectionService } from '@theia/core';
|
||||
import { BaseWidget, codicon, MessageLoop, Widget } from '@theia/core/lib/browser/widgets/widget';
|
||||
import { DisposableCollection } from '@theia/core/lib/common/disposable';
|
||||
import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
|
||||
import { PropertyViewContentWidget } from './property-view-content-widget';
|
||||
import { PropertyViewService } from './property-view-service';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
|
||||
/**
|
||||
* The main container for the selection-specific property widgets.
|
||||
* Based on the given selection, the registered `PropertyViewWidgetProvider` provides the
|
||||
* content widget that displays the corresponding properties.
|
||||
*/
|
||||
@injectable()
|
||||
export class PropertyViewWidget extends BaseWidget {
|
||||
|
||||
static readonly ID = 'property-view';
|
||||
static readonly LABEL = nls.localize('theia/property-view/properties', 'Properties');
|
||||
|
||||
protected contentWidget: PropertyViewContentWidget;
|
||||
|
||||
protected override toDisposeOnDetach = new DisposableCollection();
|
||||
|
||||
@inject(PropertyViewService) protected readonly propertyViewService: PropertyViewService;
|
||||
@inject(SelectionService) protected readonly selectionService: SelectionService;
|
||||
|
||||
@postConstruct()
|
||||
init(): void {
|
||||
this.id = PropertyViewWidget.ID;
|
||||
this.title.label = PropertyViewWidget.LABEL;
|
||||
this.title.caption = PropertyViewWidget.LABEL;
|
||||
this.title.iconClass = codicon('table');
|
||||
this.title.closable = true;
|
||||
|
||||
this.addClass('theia-property-view-widget');
|
||||
this.node.tabIndex = 0;
|
||||
|
||||
let disposed = false;
|
||||
this.toDispose.push(Disposable.create(() => disposed = true));
|
||||
this.toDispose.push(this.selectionService.onSelectionChanged((selection: Object | undefined) => {
|
||||
this.propertyViewService.getProvider(selection).then(provider => {
|
||||
provider.provideWidget(selection).then(contentWidget => {
|
||||
if (!disposed) {
|
||||
this.replaceContentWidget(contentWidget);
|
||||
provider.updateContentWidget(selection);
|
||||
}
|
||||
});
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
protected initializeContentWidget(selection: Object | undefined): void {
|
||||
this.propertyViewService.getProvider(selection).then(provider => {
|
||||
provider.provideWidget(selection).then(contentWidget => {
|
||||
this.attachContentWidget(contentWidget);
|
||||
provider.updateContentWidget(selection);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
protected replaceContentWidget(newContentWidget: PropertyViewContentWidget): void {
|
||||
if (this.contentWidget.id !== newContentWidget.id) {
|
||||
if (this.contentWidget) {
|
||||
Widget.detach(this.contentWidget);
|
||||
}
|
||||
this.attachContentWidget(newContentWidget);
|
||||
}
|
||||
}
|
||||
|
||||
protected attachContentWidget(newContentWidget: PropertyViewContentWidget): void {
|
||||
this.contentWidget = newContentWidget;
|
||||
Widget.attach(this.contentWidget, this.node);
|
||||
this.toDisposeOnDetach = new DisposableCollection();
|
||||
this.toDisposeOnDetach.push(Disposable.create(() => {
|
||||
if (this.contentWidget) {
|
||||
Widget.detach(this.contentWidget);
|
||||
}
|
||||
}));
|
||||
this.update();
|
||||
}
|
||||
|
||||
protected override onAfterAttach(msg: Message): void {
|
||||
super.onAfterAttach(msg);
|
||||
this.initializeContentWidget(this.selectionService.selection);
|
||||
}
|
||||
|
||||
protected override onActivateRequest(msg: Message): void {
|
||||
super.onActivateRequest(msg);
|
||||
this.node.focus();
|
||||
if (this.contentWidget) {
|
||||
this.contentWidget.activate();
|
||||
}
|
||||
}
|
||||
|
||||
protected override onResize(msg: Widget.ResizeMessage): void {
|
||||
super.onResize(msg);
|
||||
if (this.contentWidget) {
|
||||
MessageLoop.sendMessage(this.contentWidget, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// *****************************************************************************
|
||||
// Copyright (C) 2020 EclipseSource 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-only WITH Classpath-exception-2.0
|
||||
// *****************************************************************************
|
||||
|
||||
export * from './resource-property-view-tree-container';
|
||||
@@ -0,0 +1,127 @@
|
||||
// *****************************************************************************
|
||||
// Copyright (C) 2021 Ericsson 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-only WITH Classpath-exception-2.0
|
||||
// *****************************************************************************
|
||||
|
||||
import { enableJSDOM } from '@theia/core/lib/browser/test/jsdom';
|
||||
|
||||
let disableJSDOM = enableJSDOM();
|
||||
|
||||
import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
|
||||
FrontendApplicationConfigProvider.set({});
|
||||
|
||||
import { expect } from 'chai';
|
||||
import { Container } from '@theia/core/shared/inversify';
|
||||
import { ResourcePropertyDataService } from './resource-property-data-service';
|
||||
import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { PropertyDataService } from '../property-data-service';
|
||||
import { FileSelection } from '@theia/filesystem/lib/browser/file-selection';
|
||||
import { Navigatable } from '@theia/core/lib/browser/navigatable';
|
||||
import { FileStat } from '@theia/filesystem/lib/common/files';
|
||||
|
||||
disableJSDOM();
|
||||
|
||||
let resourcePropertyDataService: ResourcePropertyDataService;
|
||||
|
||||
const mockFileStat: FileStat = {
|
||||
isFile: false,
|
||||
isDirectory: true,
|
||||
isSymbolicLink: false,
|
||||
isReadonly: false,
|
||||
resource: new URI('resource'),
|
||||
name: 'name'
|
||||
};
|
||||
|
||||
describe('resource-property-data-service', () => {
|
||||
|
||||
before(() => {
|
||||
disableJSDOM = enableJSDOM();
|
||||
const container = new Container();
|
||||
container.bind(ResourcePropertyDataService).toSelf().inSingletonScope();
|
||||
container.bind(FileService).toConstantValue({
|
||||
async resolve(uri: URI): Promise<FileStat> {
|
||||
return mockFileStat;
|
||||
}
|
||||
} as FileService);
|
||||
container.bind(PropertyDataService).to(ResourcePropertyDataService).inSingletonScope();
|
||||
resourcePropertyDataService = container.get(ResourcePropertyDataService);
|
||||
});
|
||||
|
||||
after(() => {
|
||||
disableJSDOM();
|
||||
});
|
||||
|
||||
const navigatableSelection: Navigatable = {
|
||||
getResourceUri(): URI | undefined {
|
||||
return new URI('resource-uri');
|
||||
},
|
||||
createMoveToUri(): URI | undefined {
|
||||
return new URI('move-uri');
|
||||
}
|
||||
};
|
||||
|
||||
const fileSelection: FileSelection[] = [
|
||||
{ fileStat: mockFileStat }
|
||||
];
|
||||
|
||||
describe('#canHandle', () => {
|
||||
|
||||
it('should not handle an empty object selection', () => {
|
||||
expect(resourcePropertyDataService.canHandleSelection({})).eq(0);
|
||||
});
|
||||
|
||||
it('should not handle an undefined selection', () => {
|
||||
expect(resourcePropertyDataService.canHandleSelection(undefined)).eq(0);
|
||||
});
|
||||
|
||||
it('should handle a file selection', () => {
|
||||
expect(resourcePropertyDataService.canHandleSelection(fileSelection)).to.be.greaterThan(0);
|
||||
});
|
||||
|
||||
it('should handle a navigatable selection', () => {
|
||||
expect(resourcePropertyDataService.canHandleSelection(navigatableSelection)).to.be.greaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#providePropertyData', () => {
|
||||
|
||||
it('should return the file-stat of a file selection', async () => {
|
||||
const data = await resourcePropertyDataService.providePropertyData(fileSelection);
|
||||
expect(data).to.equal(mockFileStat);
|
||||
});
|
||||
|
||||
it('should return the first file-stat for multiple file selections', async () => {
|
||||
const arrayFileSelection: FileSelection[] = [
|
||||
{ fileStat: mockFileStat },
|
||||
{ fileStat: { ...mockFileStat, resource: new URI('secondURI') } }
|
||||
];
|
||||
const data = await resourcePropertyDataService.providePropertyData(arrayFileSelection);
|
||||
expect(data).to.equal(arrayFileSelection[0].fileStat);
|
||||
});
|
||||
|
||||
it('should return the file-stat for a navigatable selection', async () => {
|
||||
const data = await resourcePropertyDataService.providePropertyData(navigatableSelection);
|
||||
expect(data).to.equal(mockFileStat);
|
||||
});
|
||||
|
||||
it('should return undefined if the selection is undefined', async () => {
|
||||
const data = await resourcePropertyDataService.providePropertyData(undefined);
|
||||
expect(data).to.equal(undefined);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
// *****************************************************************************
|
||||
// Copyright (C) 2020 EclipseSource 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-only WITH Classpath-exception-2.0
|
||||
// *****************************************************************************
|
||||
|
||||
import { Navigatable } from '@theia/core/lib/browser';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { FileSelection } from '@theia/filesystem/lib/browser/file-selection';
|
||||
import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
||||
import { FileStat } from '@theia/filesystem/lib/common/files';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { PropertyDataService } from '../property-data-service';
|
||||
|
||||
/**
|
||||
* This data service provides property data for {@link FileSelection}s and selections of {@link Navigatable}s.
|
||||
*/
|
||||
@injectable()
|
||||
export class ResourcePropertyDataService implements PropertyDataService {
|
||||
|
||||
readonly id = 'resources';
|
||||
readonly label = 'ResourcePropertyDataService';
|
||||
|
||||
@inject(FileService) protected readonly fileService: FileService;
|
||||
|
||||
canHandleSelection(selection: Object | undefined): number {
|
||||
return (this.isFileSelection(selection) || this.isNavigatableSelection(selection)) ? 1 : 0;
|
||||
}
|
||||
|
||||
protected isFileSelection(selection: Object | undefined): boolean {
|
||||
return !!selection && Array.isArray(selection) && FileSelection.is(selection[0]);
|
||||
}
|
||||
|
||||
protected isNavigatableSelection(selection: Object | undefined): boolean {
|
||||
return !!selection && Navigatable.is(selection);
|
||||
}
|
||||
|
||||
protected async getFileStat(uri: URI): Promise<FileStat> {
|
||||
return this.fileService.resolve(uri);
|
||||
}
|
||||
|
||||
async providePropertyData(selection: Object | undefined): Promise<FileStat | undefined> {
|
||||
if (this.isFileSelection(selection) && Array.isArray(selection)) {
|
||||
return this.getFileStat(selection[0].fileStat.resource);
|
||||
} else if (this.isNavigatableSelection(selection)) {
|
||||
const navigatableUri = (selection as Navigatable).getResourceUri();
|
||||
if (navigatableUri) {
|
||||
return this.getFileStat(navigatableUri);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
// *****************************************************************************
|
||||
// Copyright (C) 2021 Ericsson 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-only WITH Classpath-exception-2.0
|
||||
// *****************************************************************************
|
||||
|
||||
import { enableJSDOM } from '@theia/core/lib/browser/test/jsdom';
|
||||
|
||||
let disableJSDOM = enableJSDOM();
|
||||
|
||||
import { expect } from 'chai';
|
||||
import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
|
||||
FrontendApplicationConfigProvider.set({});
|
||||
|
||||
import { Container } from '@theia/core/shared/inversify';
|
||||
import { TreeNode } from '@theia/core/lib/browser/tree/tree';
|
||||
import { DEFAULT_INFO_ICON, ResourcePropertiesLabelProvider, } from './resource-property-view-label-provider';
|
||||
import { LabelProvider, LabelProviderContribution } from '@theia/core/lib/browser/label-provider';
|
||||
import { ContributionProvider } from '@theia/core/lib/common';
|
||||
import { ResourcePropertiesCategoryNode, ResourcePropertiesItemNode } from './resource-property-view-tree-items';
|
||||
|
||||
disableJSDOM();
|
||||
|
||||
let resourcePropertiesLabelProvider: ResourcePropertiesLabelProvider;
|
||||
|
||||
describe('resource-property-view-label', () => {
|
||||
|
||||
before(() => {
|
||||
disableJSDOM = enableJSDOM();
|
||||
const container = new Container();
|
||||
container.bind(ResourcePropertiesLabelProvider).toSelf().inSingletonScope();
|
||||
container.bind(LabelProvider).toSelf().inSingletonScope();
|
||||
container.bind<Partial<ContributionProvider<LabelProviderContribution>>>(ContributionProvider)
|
||||
.toConstantValue({
|
||||
getContributions: () => [],
|
||||
})
|
||||
.whenTargetNamed(LabelProviderContribution);
|
||||
resourcePropertiesLabelProvider = container.get(ResourcePropertiesLabelProvider);
|
||||
});
|
||||
|
||||
after(() => {
|
||||
disableJSDOM();
|
||||
});
|
||||
|
||||
const categoryNode: ResourcePropertiesCategoryNode = {
|
||||
name: 'category',
|
||||
id: '',
|
||||
icon: 'iconCategory',
|
||||
children: [],
|
||||
parent: {
|
||||
id: '',
|
||||
parent: undefined,
|
||||
children: []
|
||||
},
|
||||
categoryId: '',
|
||||
expanded: false,
|
||||
selected: false,
|
||||
};
|
||||
|
||||
const itemNode: ResourcePropertiesItemNode = {
|
||||
name: 'item',
|
||||
id: '',
|
||||
icon: 'iconItem',
|
||||
selected: false,
|
||||
parent: {
|
||||
name: 'category',
|
||||
id: '',
|
||||
icon: '',
|
||||
children: [],
|
||||
parent: {
|
||||
id: '',
|
||||
parent: undefined,
|
||||
children: []
|
||||
},
|
||||
categoryId: '',
|
||||
expanded: false,
|
||||
selected: false,
|
||||
},
|
||||
property: 'property'
|
||||
};
|
||||
|
||||
describe('#canHandle', () => {
|
||||
it('should handle a category node', () => {
|
||||
expect(resourcePropertiesLabelProvider.canHandle(categoryNode)).to.be.greaterThan(0);
|
||||
});
|
||||
|
||||
it('should handle an item node', () => {
|
||||
expect(resourcePropertiesLabelProvider.canHandle(itemNode)).to.be.greaterThan(0);
|
||||
});
|
||||
|
||||
it('should not handle a tree node (not an item nor a category)', () => {
|
||||
const node: TreeNode = {
|
||||
id: '',
|
||||
parent: undefined
|
||||
};
|
||||
expect(resourcePropertiesLabelProvider.canHandle(node)).eq(0);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('#getIcon', () => {
|
||||
it('should get the icon of a category node', () => {
|
||||
expect(resourcePropertiesLabelProvider.getIcon(categoryNode)).eq('iconCategory');
|
||||
});
|
||||
|
||||
it('should get the default icon if a category node has an undefined icon field', () => {
|
||||
const emptyIconCategory: ResourcePropertiesCategoryNode = categoryNode;
|
||||
emptyIconCategory.icon = undefined;
|
||||
expect(resourcePropertiesLabelProvider.getIcon(emptyIconCategory)).eq(DEFAULT_INFO_ICON);
|
||||
});
|
||||
|
||||
it('should get the icon of an item node', () => {
|
||||
expect(resourcePropertiesLabelProvider.getIcon(itemNode)).eq('iconItem');
|
||||
});
|
||||
|
||||
it('should get an empty string if an item node has an undefined icon field', () => {
|
||||
const emptyIconItem: ResourcePropertiesItemNode = itemNode;
|
||||
emptyIconItem.icon = undefined;
|
||||
expect(resourcePropertiesLabelProvider.getIcon(emptyIconItem)).eq('');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getName', () => {
|
||||
it('should get the name of a category node', () => {
|
||||
expect(resourcePropertiesLabelProvider.getName(categoryNode)).eq('category');
|
||||
});
|
||||
|
||||
it('should get the name of an item node', () => {
|
||||
expect(resourcePropertiesLabelProvider.getName(itemNode)).eq('item');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getLongName', () => {
|
||||
it('should get the property of an item node', () => {
|
||||
expect(resourcePropertiesLabelProvider.getLongName(itemNode)).eq('property');
|
||||
});
|
||||
|
||||
it('should get the name of a category node', () => {
|
||||
expect(resourcePropertiesLabelProvider.getLongName(categoryNode)).eq('category');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,49 @@
|
||||
// *****************************************************************************
|
||||
// Copyright (C) 2020 EclipseSource 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-only WITH Classpath-exception-2.0
|
||||
// *****************************************************************************
|
||||
|
||||
import { codicon, LabelProvider, LabelProviderContribution, TreeNode } from '@theia/core/lib/browser';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { ResourcePropertiesCategoryNode, ResourcePropertiesItemNode } from './resource-property-view-tree-items';
|
||||
|
||||
export const DEFAULT_INFO_ICON = codicon('info');
|
||||
|
||||
@injectable()
|
||||
export class ResourcePropertiesLabelProvider implements LabelProviderContribution {
|
||||
|
||||
@inject(LabelProvider) protected readonly labelProvider: LabelProvider;
|
||||
|
||||
canHandle(element: TreeNode): number {
|
||||
return (ResourcePropertiesCategoryNode.is(element) || ResourcePropertiesItemNode.is(element)) ? 75 : 0;
|
||||
}
|
||||
|
||||
getIcon(node: ResourcePropertiesCategoryNode | ResourcePropertiesItemNode): string {
|
||||
if (ResourcePropertiesCategoryNode.is(node)) {
|
||||
return node.icon ?? DEFAULT_INFO_ICON;
|
||||
}
|
||||
return node.icon ?? '';
|
||||
}
|
||||
|
||||
getName(node: ResourcePropertiesCategoryNode | ResourcePropertiesItemNode): string {
|
||||
return node.name;
|
||||
}
|
||||
|
||||
getLongName(node: ResourcePropertiesCategoryNode | ResourcePropertiesItemNode): string {
|
||||
if (ResourcePropertiesItemNode.is(node)) {
|
||||
return node.property;
|
||||
}
|
||||
return this.getName(node);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// *****************************************************************************
|
||||
// Copyright (C) 2020 EclipseSource 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-only WITH Classpath-exception-2.0
|
||||
// *****************************************************************************
|
||||
|
||||
import { createTreeContainer, LabelProviderContribution, TreeProps } from '@theia/core/lib/browser';
|
||||
import { interfaces } from '@theia/core/shared/inversify';
|
||||
import { PropertyDataService } from '../property-data-service';
|
||||
import { PropertyViewWidgetProvider } from '../property-view-widget-provider';
|
||||
import { ResourcePropertyDataService } from './resource-property-data-service';
|
||||
import { ResourcePropertiesLabelProvider } from './resource-property-view-label-provider';
|
||||
import { ResourcePropertyViewTreeWidget } from './resource-property-view-tree-widget';
|
||||
import { ResourcePropertyViewWidgetProvider } from './resource-property-view-widget-provider';
|
||||
|
||||
const RESOURCE_PROPERTY_VIEW_TREE_PROPS = {
|
||||
multiSelect: true,
|
||||
search: true,
|
||||
} as TreeProps;
|
||||
|
||||
function createResourcePropertyViewTreeWidget(parent: interfaces.Container): ResourcePropertyViewTreeWidget {
|
||||
const child = createTreeContainer(parent, {
|
||||
props: RESOURCE_PROPERTY_VIEW_TREE_PROPS,
|
||||
widget: ResourcePropertyViewTreeWidget,
|
||||
});
|
||||
return child.get(ResourcePropertyViewTreeWidget);
|
||||
}
|
||||
|
||||
export function bindResourcePropertyView(bind: interfaces.Bind): void {
|
||||
bind(LabelProviderContribution).to(ResourcePropertiesLabelProvider).inSingletonScope();
|
||||
bind(PropertyDataService).to(ResourcePropertyDataService).inSingletonScope();
|
||||
bind(PropertyViewWidgetProvider).to(ResourcePropertyViewWidgetProvider).inSingletonScope();
|
||||
|
||||
bind(ResourcePropertyViewTreeWidget).toDynamicValue(ctx =>
|
||||
createResourcePropertyViewTreeWidget(ctx.container)
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// *****************************************************************************
|
||||
// Copyright (C) 2020 EclipseSource 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-only WITH Classpath-exception-2.0
|
||||
// *****************************************************************************
|
||||
|
||||
import { CompositeTreeNode, ExpandableTreeNode, SelectableTreeNode, TreeNode } from '@theia/core/lib/browser';
|
||||
|
||||
export const ROOT_ID = 'ResourcePropertiesTree';
|
||||
|
||||
export interface ResourcePropertiesRoot extends CompositeTreeNode {
|
||||
children: ResourcePropertiesCategoryNode[];
|
||||
}
|
||||
export namespace ResourcePropertiesRoot {
|
||||
export function is(node: unknown): node is ResourcePropertiesRoot {
|
||||
return CompositeTreeNode.is(node) && node.id === ROOT_ID;
|
||||
}
|
||||
}
|
||||
|
||||
export interface ResourcePropertiesCategoryNode extends ExpandableTreeNode, SelectableTreeNode {
|
||||
name: string;
|
||||
icon?: string;
|
||||
children: ResourcePropertiesItemNode[];
|
||||
parent: ResourcePropertiesRoot;
|
||||
categoryId: string;
|
||||
}
|
||||
export namespace ResourcePropertiesCategoryNode {
|
||||
export function is(node: TreeNode | undefined): node is ResourcePropertiesCategoryNode {
|
||||
return ExpandableTreeNode.is(node) && SelectableTreeNode.is(node) && 'categoryId' in node;
|
||||
}
|
||||
}
|
||||
|
||||
export interface ResourcePropertiesItemNode extends SelectableTreeNode {
|
||||
name: string;
|
||||
icon?: string;
|
||||
parent: ResourcePropertiesCategoryNode;
|
||||
property: string;
|
||||
}
|
||||
export namespace ResourcePropertiesItemNode {
|
||||
export function is(node: TreeNode | undefined): node is ResourcePropertiesItemNode {
|
||||
return !!node && SelectableTreeNode.is(node) && 'property' in node;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
// *****************************************************************************
|
||||
// Copyright (C) 2020 EclipseSource 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-only WITH Classpath-exception-2.0
|
||||
// *****************************************************************************
|
||||
|
||||
import {
|
||||
ContextMenuRenderer,
|
||||
NodeProps,
|
||||
TreeModel,
|
||||
TreeNode,
|
||||
TreeProps,
|
||||
TreeWidget
|
||||
} from '@theia/core/lib/browser';
|
||||
import { FileStat } from '@theia/filesystem/lib/common/files';
|
||||
import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
|
||||
import * as React from '@theia/core/shared/react';
|
||||
import { PropertyDataService } from '../property-data-service';
|
||||
import { PropertyViewContentWidget } from '../property-view-content-widget';
|
||||
import {
|
||||
ResourcePropertiesCategoryNode,
|
||||
ResourcePropertiesItemNode,
|
||||
ResourcePropertiesRoot,
|
||||
ROOT_ID
|
||||
} from './resource-property-view-tree-items';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
|
||||
/**
|
||||
* This widget fetches the property data for {@link FileSelection}s and selections of {@link Navigatable}s
|
||||
* and renders that property data as a {@link TreeWidget}.
|
||||
* This widget is provided by the registered `ResourcePropertyViewWidgetProvider`.
|
||||
*/
|
||||
@injectable()
|
||||
export class ResourcePropertyViewTreeWidget extends TreeWidget implements PropertyViewContentWidget {
|
||||
|
||||
static readonly ID = 'resource-properties-tree-widget';
|
||||
static readonly LABEL = 'Resource Properties Tree';
|
||||
|
||||
protected propertiesTree: Map<string, ResourcePropertiesCategoryNode>;
|
||||
protected currentSelection: Object | undefined;
|
||||
|
||||
constructor(
|
||||
@inject(TreeProps) props: TreeProps,
|
||||
@inject(TreeModel) model: TreeModel,
|
||||
@inject(ContextMenuRenderer) contextMenuRenderer: ContextMenuRenderer
|
||||
) {
|
||||
super(props, model, contextMenuRenderer);
|
||||
|
||||
model.root = {
|
||||
id: ROOT_ID,
|
||||
name: ResourcePropertyViewTreeWidget.LABEL,
|
||||
parent: undefined,
|
||||
visible: false,
|
||||
children: []
|
||||
} as ResourcePropertiesRoot;
|
||||
|
||||
this.propertiesTree = new Map<string, ResourcePropertiesCategoryNode>();
|
||||
}
|
||||
|
||||
@postConstruct()
|
||||
protected override init(): void {
|
||||
super.init();
|
||||
|
||||
this.id = ResourcePropertyViewTreeWidget.ID + '-treeContainer';
|
||||
this.addClass('treeContainer');
|
||||
|
||||
this.fillPropertiesTree();
|
||||
}
|
||||
|
||||
protected updateNeeded(selection: Object | undefined): boolean {
|
||||
return this.currentSelection !== selection;
|
||||
}
|
||||
|
||||
updatePropertyViewContent(propertyDataService?: PropertyDataService, selection?: Object | undefined): void {
|
||||
if (this.updateNeeded(selection)) {
|
||||
this.currentSelection = selection;
|
||||
if (propertyDataService) {
|
||||
propertyDataService.providePropertyData(selection).then((fileStatObject?: FileStat) => {
|
||||
this.fillPropertiesTree(fileStatObject);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected fillPropertiesTree(fileStatObject?: FileStat): void {
|
||||
if (fileStatObject) {
|
||||
this.propertiesTree.clear();
|
||||
const infoNode = this.createCategoryNode('info', nls.localizeByDefault('Info'));
|
||||
this.propertiesTree.set('info', infoNode);
|
||||
|
||||
infoNode.children.push(this.createResultLineNode('isDirectory', nls.localize('theia/property-view/directory', 'Directory'), fileStatObject.isDirectory, infoNode));
|
||||
infoNode.children.push(this.createResultLineNode('isFile', nls.localizeByDefault('File'), fileStatObject.isFile, infoNode));
|
||||
infoNode.children.push(this.createResultLineNode('isSymbolicLink', nls.localize('theia/property-view/symbolicLink', 'Symbolic link'),
|
||||
fileStatObject.isSymbolicLink, infoNode));
|
||||
infoNode.children.push(this.createResultLineNode('location', nls.localize('theia/property-view/location', 'Location'),
|
||||
this.getLocationString(fileStatObject), infoNode));
|
||||
infoNode.children.push(this.createResultLineNode('name', nls.localizeByDefault('Name'), this.getFileName(fileStatObject), infoNode));
|
||||
infoNode.children.push(this.createResultLineNode('path', nls.localizeByDefault('Path'), this.getFilePath(fileStatObject), infoNode));
|
||||
infoNode.children.push(this.createResultLineNode('lastModification', nls.localize('theia/property-view/lastModified', 'Last modified'),
|
||||
this.getLastModificationString(fileStatObject), infoNode));
|
||||
infoNode.children.push(this.createResultLineNode('created', nls.localize('theia/property-view/created', 'Created'),
|
||||
this.getCreationTimeString(fileStatObject), infoNode));
|
||||
infoNode.children.push(this.createResultLineNode('size', nls.localizeByDefault('Size'), this.getSizeString(fileStatObject), infoNode));
|
||||
this.refreshModelChildren();
|
||||
}
|
||||
}
|
||||
|
||||
protected getLocationString(fileStat: FileStat): string {
|
||||
return fileStat.resource.path.fsPath();
|
||||
}
|
||||
|
||||
protected getFileName(fileStat: FileStat): string {
|
||||
return this.labelProvider.getName(fileStat.resource);
|
||||
}
|
||||
|
||||
protected getFilePath(fileStat: FileStat): string {
|
||||
return this.labelProvider.getLongName(fileStat.resource);
|
||||
}
|
||||
|
||||
protected getLastModificationString(fileStat: FileStat): string {
|
||||
return fileStat.mtime ? new Date(fileStat.mtime).toLocaleString() : '';
|
||||
}
|
||||
|
||||
protected getCreationTimeString(fileStat: FileStat): string {
|
||||
return fileStat.ctime ? new Date(fileStat.ctime).toLocaleString() : '';
|
||||
}
|
||||
|
||||
protected getSizeString(fileStat: FileStat): string {
|
||||
return fileStat.size ? nls.localizeByDefault('{0}B', fileStat.size.toString()) : '';
|
||||
}
|
||||
|
||||
/*
|
||||
* Creating TreeNodes
|
||||
*/
|
||||
|
||||
protected createCategoryNode(categoryId: string, name: string): ResourcePropertiesCategoryNode {
|
||||
return {
|
||||
id: categoryId,
|
||||
parent: this.model.root as ResourcePropertiesRoot,
|
||||
name,
|
||||
children: [],
|
||||
categoryId,
|
||||
selected: false,
|
||||
expanded: true
|
||||
};
|
||||
}
|
||||
|
||||
protected createResultLineNode(id: string, name: string, property: boolean | string | undefined, parent: ResourcePropertiesCategoryNode): ResourcePropertiesItemNode {
|
||||
return {
|
||||
id: `${parent.id}::${id}`,
|
||||
parent,
|
||||
name: name,
|
||||
property: property !== undefined ? String(property) : '',
|
||||
selected: false
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Rendering
|
||||
*/
|
||||
|
||||
protected async refreshModelChildren(): Promise<void> {
|
||||
if (ResourcePropertiesRoot.is(this.model.root)) {
|
||||
this.model.root.children = Array.from(this.propertiesTree.values());
|
||||
this.model.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
protected override renderCaption(node: TreeNode, props: NodeProps): React.ReactNode {
|
||||
if (ResourcePropertiesCategoryNode.is(node)) {
|
||||
return this.renderExpandableNode(node);
|
||||
} else if (ResourcePropertiesItemNode.is(node)) {
|
||||
return this.renderItemNode(node);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
protected renderExpandableNode(node: ResourcePropertiesCategoryNode): React.ReactNode {
|
||||
return <React.Fragment>
|
||||
<div className={`theia-resource-tree-node-icon ${this.toNodeIcon(node)}`}></div>
|
||||
<div className={'theia-resource-tree-node-name theia-TreeNodeSegment theia-TreeNodeSegmentGrow'}>{this.toNodeName(node)}</div>
|
||||
</React.Fragment>;
|
||||
}
|
||||
|
||||
protected renderItemNode(node: ResourcePropertiesItemNode): React.ReactNode {
|
||||
return <React.Fragment>
|
||||
<div className={`theia-resource-tree-node-icon ${this.toNodeIcon(node)}`}></div>
|
||||
<div className={'theia-resource-tree-node-name theia-TreeNodeSegment theia-TreeNodeSegmentGrow'}>{this.toNodeName(node)}</div>
|
||||
<div className={'theia-resource-tree-node-property theia-TreeNodeSegment theia-TreeNodeSegmentGrow'}>{this.toNodeDescription(node)}</div>
|
||||
</React.Fragment>;
|
||||
}
|
||||
|
||||
protected override createNodeAttributes(node: TreeNode, props: NodeProps): React.Attributes & React.HTMLAttributes<HTMLElement> {
|
||||
return {
|
||||
...super.createNodeAttributes(node, props),
|
||||
title: this.getNodeTooltip(node)
|
||||
};
|
||||
}
|
||||
|
||||
protected getNodeTooltip(node: TreeNode): string | undefined {
|
||||
if (ResourcePropertiesCategoryNode.is(node)) {
|
||||
return this.labelProvider.getName(node);
|
||||
} else if (ResourcePropertiesItemNode.is(node)) {
|
||||
return `${this.labelProvider.getName(node)}: ${this.labelProvider.getLongName(node)}`;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// *****************************************************************************
|
||||
// Copyright (C) 2020 EclipseSource 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-only WITH Classpath-exception-2.0
|
||||
// *****************************************************************************
|
||||
|
||||
import { Navigatable } from '@theia/core/lib/browser';
|
||||
import { FileSelection } from '@theia/filesystem/lib/browser/file-selection';
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { DefaultPropertyViewWidgetProvider } from '../property-view-widget-provider';
|
||||
import { ResourcePropertyViewTreeWidget } from './resource-property-view-tree-widget';
|
||||
|
||||
/**
|
||||
* Provides the {@link ResourcePropertyViewTreeWidget} for
|
||||
* {@link FileSelection}s and selections of {@link Navigatable}s.
|
||||
*/
|
||||
@injectable()
|
||||
export class ResourcePropertyViewWidgetProvider extends DefaultPropertyViewWidgetProvider {
|
||||
|
||||
@inject(ResourcePropertyViewTreeWidget) protected treeWidget: ResourcePropertyViewTreeWidget;
|
||||
|
||||
override readonly id = 'resources';
|
||||
override readonly label = 'ResourcePropertyViewWidgetProvider';
|
||||
|
||||
override canHandle(selection: Object | undefined): number {
|
||||
return (this.isFileSelection(selection) || this.isNavigatableSelection(selection)) ? 1 : 0;
|
||||
}
|
||||
|
||||
protected isFileSelection(selection: Object | undefined): boolean {
|
||||
return !!selection && Array.isArray(selection) && FileSelection.is(selection[0]);
|
||||
}
|
||||
|
||||
protected isNavigatableSelection(selection: Object | undefined): boolean {
|
||||
return !!selection && Navigatable.is(selection);
|
||||
}
|
||||
|
||||
override provideWidget(selection: Object | undefined): Promise<ResourcePropertyViewTreeWidget> {
|
||||
return Promise.resolve(this.treeWidget);
|
||||
}
|
||||
|
||||
override updateContentWidget(selection: Object | undefined): void {
|
||||
this.getPropertyDataService(selection).then(service => this.treeWidget.updatePropertyViewContent(service, selection));
|
||||
}
|
||||
|
||||
}
|
||||
51
packages/property-view/src/browser/style/property-view.css
Normal file
51
packages/property-view/src/browser/style/property-view.css
Normal file
@@ -0,0 +1,51 @@
|
||||
/********************************************************************************
|
||||
* Copyright (C) 2020 EclipseSource 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-only WITH Classpath-exception-2.0
|
||||
********************************************************************************/
|
||||
|
||||
:root {
|
||||
--theia-property-view-widget-padding: 5px;
|
||||
--theia-empty-property-view-widget-padding: 8px;
|
||||
--theia-resource-tree-node-icon-margin: 0 3px;
|
||||
--theia-resource-tree-node-icon-flex-basis: 1.5%;
|
||||
--theia-resource-tree-node-name-flex-basis: 30%;
|
||||
--theia-resource-tree-node-property-flex-basis: 70%;
|
||||
}
|
||||
|
||||
.theia-property-view-widget {
|
||||
padding: var(--theia-border-width);
|
||||
}
|
||||
|
||||
#theia-empty-property-view .theia-widget-noInfo {
|
||||
padding: var(--theia-empty-property-view-widget-padding);
|
||||
}
|
||||
|
||||
.theia-property-view-widget .treeContainer {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.theia-resource-tree-node-icon {
|
||||
margin: var(--theia-resource-tree-node-icon-margin);
|
||||
flex-basis: var(--theia-resource-tree-node-icon-flex-basis);
|
||||
align-self: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.theia-resource-tree-node-name {
|
||||
flex-basis: var(--theia-resource-tree-node-name-flex-basis);
|
||||
}
|
||||
|
||||
.theia-resource-tree-node-property {
|
||||
flex-basis: var(--theia-resource-tree-node-property-flex-basis);
|
||||
}
|
||||
29
packages/property-view/src/package.spec.ts
Normal file
29
packages/property-view/src/package.spec.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
// *****************************************************************************
|
||||
// Copyright (C) 2020 EclipseSource 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-only WITH Classpath-exception-2.0
|
||||
// *****************************************************************************
|
||||
|
||||
/* note: this bogus test file is required so that
|
||||
we are able to run mocha unit tests on this
|
||||
package, without having any actual unit tests in it.
|
||||
This way a coverage report will be generated,
|
||||
showing 0% coverage, instead of no report.
|
||||
This file can be removed once we have real unit
|
||||
tests in place. */
|
||||
|
||||
describe('property-view package', () => {
|
||||
|
||||
it('support code coverage statistics', () => true);
|
||||
|
||||
});
|
||||
19
packages/property-view/tsconfig.json
Normal file
19
packages/property-view/tsconfig.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"extends": "../../configs/base.tsconfig",
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"rootDir": "src",
|
||||
"outDir": "lib"
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "../core"
|
||||
},
|
||||
{
|
||||
"path": "../filesystem"
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user