Theia 属性视图
许多 IDE(例如传统的 Eclipse IDE)都具有全局、可扩展属性视图的概念,它显示IDE 中与当前选择有关 的附加信息。这些 IDE 中大量使用属性视图来显示元素的详细信息,例如图表编辑器、复杂树编辑器或文件资源管理器。主要思想是在 IDE 中拥有一个全局的、通用的属性视图,但允许特定的实现使用特定的附加信息来扩展全局属性视图的内容。
@theia/property-view 扩展提供了一个基于 Theia 全局选择的通用全局属性视图。属性视图组件可以通过菜单 View->Properties 或快捷键 Shift+Alt+P 打开/切换。它默认位于底部停靠区。
此扩展中实现了以下两个默认内容组件:
EmptyPropertyViewWidget:如果无法提供其他组件,则会显示一条简单消息(无可用属性)。
ResourcePropertyViewWidget:显示在文件资源管理器或monaco 编辑器中选中的文件的附加信息(例如位置、名称、上次修改时间)。
创建自定义属性视图
要提供特定的属性视图,必须实现一个 PropertyViewDataService 来收集选择的属性数据,以及一个 PropertyViewWidgetProvider ,它提供合适的内容组件来显示属性视图组件内特定选择的属性数据。
这是一个关于如何实现附加属性视图的简短示例,它根据文件资源管理器中的选择在简单的 React 组件中显示名称以及它是文件还是目录(假设没有 ResourcePropertyViewWidget):
FileInfoPropertyDataService 收集文件信息并提供自定义对象:
custom-data-service.ts
:
export interface FileInfoPropertyObject { name: string; isDirectory: boolean; } @injectable() export class FileInfoPropertyDataService implements PropertyDataService { readonly id = 'fileinfo'; readonly label = 'FileInfoPropertyDataService'; @inject(LabelProvider) protected readonly labelProvider: LabelProvider; canHandleSelection(selection: Object | undefined): number { return this.isFileSelection(selection) ? 1 : 0; } private isFileSelection(selection: Object | undefined): boolean { return !!selection && Array.isArray(selection) && FileSelection.is(selection[0]); } async providePropertyData(selection: Object | undefined): Promise<FileInfoPropertyObject | undefined> { if (this.isFileSelection(selection) && Array.isArray(selection)) { return { name: this.labelProvider.getName(selection[0].fileStat.resource), isDirectory: (selection[0].fileStat as FileStat).isDirectory }; } return Promise.reject(); } }
FileInfoPropertyWidget 是一个简单的 React Widget 并显示所选节点以及它是文件还是目录:
custom-content-widget.tsx
:
export class FileInfoPropertyViewWidget extends ReactWidget implements PropertyViewContentWidget { static readonly ID = 'file-info-property-view'; static readonly LABEL = 'File Information'; protected currentFileInfo: FileInfoPropertyObject; constructor() { super(); this.id = FileInfoPropertyViewWidget.ID; this.title.label = FileInfoPropertyViewWidget.LABEL; this.title.caption = FileInfoPropertyViewWidget.LABEL; this.title.closable = false; this.node.tabIndex = 0; } updatePropertyViewContent(propertyDataService?: PropertyDataService, selection?: Object | undefined): void { if (propertyDataService) { propertyDataService.providePropertyData(selection).then((fileInfo: FileInfoPropertyObject) => this.currentFileInfo = fileInfo); } this.update(); } protected render(): React.ReactNode { return (<div> {`Selected node in explorer: ${this.currentFileInfo.name} ${this.currentFileInfo.isDirectory ? '(Directory)' : '(File)'}` } </div>); } }
FileInfoPropertyViewWidgetProvider 负责根据选择提供正确的 PropertyViewContentWidget:
custom-widget-provider.ts
:
@injectable() export class FileInfoPropertyViewWidgetProvider extends DefaultPropertyViewWidgetProvider { override readonly id = 'fileinfo'; override readonly label = 'FileInfoPropertyViewWidgetProvider'; private fileInfoWidget: FileInfoPropertyViewWidget; constructor() { super(); this.fileInfoWidget = new FileInfoPropertyViewWidget(); } override canHandle(selection: Object | undefined): number { return this.isFileSelection(selection) ? 1 : 0; } private isFileSelection(selection: Object | undefined): boolean { return !!selection && Array.isArray(selection) && FileSelection.is(selection[0]); } override provideWidget(selection: Object | undefined): Promise<FileInfoPropertyViewWidget> { return Promise.resolve(this.fileInfoWidget); } override updateContentWidget(selection: Object | undefined): void { this.getPropertyDataService(selection).then(service => this.fileInfoWidget.updatePropertyViewContent(service, selection)); } }
在应用程序的前端模块中,FileInfoPropertyDataService 以及 FileInfoPropertyViewWidgetProvider 注册如下:
bind(PropertyDataService).to(FileInfoPropertyDataService).inSingletonScope();
bind(PropertyViewWidgetProvider).to(FileInfoPropertyViewWidgetProvider).inSingletonScope();
遵循这几个步骤应该让读者了解如何实现自己的由特定的 PropertyViewWidgetProvider 和 PropertyViewDataService 组成的属性视图。
生成的属性视图将显示如下: