@opensumi/ide-opened-editor 2.21.13 → 2.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/lib/browser/index.js.map +1 -1
  2. package/lib/browser/opened-editor-node.d.ts +1 -1
  3. package/lib/browser/opened-editor-node.d.ts.map +1 -1
  4. package/lib/browser/opened-editor-node.define.d.ts +1 -2
  5. package/lib/browser/opened-editor-node.define.d.ts.map +1 -1
  6. package/lib/browser/opened-editor-node.define.js +5 -9
  7. package/lib/browser/opened-editor-node.define.js.map +1 -1
  8. package/lib/browser/opened-editor-node.js +1 -1
  9. package/lib/browser/opened-editor-node.js.map +1 -1
  10. package/lib/browser/opened-editor-node.module.less +2 -14
  11. package/lib/browser/opened-editor.contribution.js.map +1 -1
  12. package/lib/browser/opened-editor.d.ts.map +1 -1
  13. package/lib/browser/opened-editor.js +15 -15
  14. package/lib/browser/opened-editor.js.map +1 -1
  15. package/lib/browser/services/opened-editor-decoration.service.js.map +1 -1
  16. package/lib/browser/services/opened-editor-event.service.d.ts +1 -1
  17. package/lib/browser/services/opened-editor-event.service.d.ts.map +1 -1
  18. package/lib/browser/services/opened-editor-event.service.js.map +1 -1
  19. package/lib/browser/services/opened-editor-model.js.map +1 -1
  20. package/lib/browser/services/opened-editor-model.service.d.ts +2 -2
  21. package/lib/browser/services/opened-editor-model.service.d.ts.map +1 -1
  22. package/lib/browser/services/opened-editor-model.service.js.map +1 -1
  23. package/lib/browser/services/opened-editor-tree.service.js.map +1 -1
  24. package/package.json +16 -15
  25. package/src/browser/index.module.less +24 -0
  26. package/src/browser/index.ts +26 -0
  27. package/src/browser/opened-editor-node.define.ts +82 -0
  28. package/src/browser/opened-editor-node.module.less +182 -0
  29. package/src/browser/opened-editor-node.tsx +232 -0
  30. package/src/browser/opened-editor.contribution.ts +193 -0
  31. package/src/browser/opened-editor.tsx +183 -0
  32. package/src/browser/services/opened-editor-decoration.service.ts +63 -0
  33. package/src/browser/services/opened-editor-event.service.ts +51 -0
  34. package/src/browser/services/opened-editor-model.service.ts +549 -0
  35. package/src/browser/services/opened-editor-model.ts +47 -0
  36. package/src/browser/services/opened-editor-tree.service.ts +151 -0
@@ -0,0 +1,183 @@
1
+ import React, { useState, useEffect, useCallback } from 'react';
2
+
3
+ import {
4
+ RecycleTree,
5
+ IRecycleTreeHandle,
6
+ INodeRendererWrapProps,
7
+ TreeNodeType,
8
+ TreeModel,
9
+ } from '@opensumi/ide-components';
10
+ import { ViewState, CancellationToken, localize, CancellationTokenSource } from '@opensumi/ide-core-browser';
11
+ import { Progress } from '@opensumi/ide-core-browser/lib/progress/progress-bar';
12
+ import { useInjectable } from '@opensumi/ide-core-browser/lib/react-hooks';
13
+
14
+ import styles from './index.module.less';
15
+ import { EditorTreeNode } from './opened-editor-node';
16
+ import { EditorFile, EditorFileGroup } from './opened-editor-node.define';
17
+ import { OpenedEditorModelService } from './services/opened-editor-model.service';
18
+
19
+ export const ExplorerOpenEditorPanel = ({ viewState }: React.PropsWithChildren<{ viewState: ViewState }>) => {
20
+ const OPEN_EDITOR_NODE_HEIGHT = 22;
21
+ const [isReady, setIsReady] = useState<boolean>(false);
22
+ const [isLoading, setIsLoading] = useState<boolean>(true);
23
+ const [model, setModel] = useState<TreeModel | null>();
24
+
25
+ const { width, height } = viewState;
26
+
27
+ const wrapperRef: React.RefObject<HTMLDivElement> = React.createRef();
28
+
29
+ const openedEditorModelService = useInjectable<OpenedEditorModelService>(OpenedEditorModelService);
30
+ const { decorationService, labelService, commandService } = openedEditorModelService;
31
+
32
+ const handleTreeReady = (handle: IRecycleTreeHandle) => {
33
+ openedEditorModelService.handleTreeHandler({
34
+ ...handle,
35
+ hasDirectFocus: () => wrapperRef.current === document.activeElement,
36
+ });
37
+ };
38
+
39
+ const handleItemClicked = useCallback(
40
+ (ev: React.MouseEvent, item: EditorFile | EditorFileGroup, type: TreeNodeType) => {
41
+ // 阻止点击事件冒泡
42
+ ev.stopPropagation();
43
+
44
+ const { handleItemClick } = openedEditorModelService;
45
+ if (!item) {
46
+ return;
47
+ }
48
+ handleItemClick(item, type);
49
+ },
50
+ [openedEditorModelService],
51
+ );
52
+
53
+ const handlerContextMenu = useCallback((ev: React.MouseEvent, node: EditorFile | EditorFileGroup) => {
54
+ const { handleContextMenu } = openedEditorModelService;
55
+ handleContextMenu(ev, node);
56
+ }, []);
57
+
58
+ const handleOuterContextMenu = useCallback(
59
+ (ev: React.MouseEvent) => {
60
+ const { handleContextMenu } = openedEditorModelService;
61
+ // 空白区域右键菜单
62
+ handleContextMenu(ev);
63
+ },
64
+ [openedEditorModelService],
65
+ );
66
+
67
+ const handleOuterClick = useCallback(() => {
68
+ // 空白区域点击,取消焦点状态
69
+ const { enactiveFileDecoration } = openedEditorModelService;
70
+ enactiveFileDecoration();
71
+ }, []);
72
+
73
+ const ensureIsReady = useCallback(
74
+ async (token: CancellationToken) => {
75
+ await openedEditorModelService.whenReady;
76
+ if (token.isCancellationRequested) {
77
+ return;
78
+ }
79
+ if (openedEditorModelService.treeModel) {
80
+ setModel(openedEditorModelService.treeModel);
81
+ // 确保数据初始化完毕,减少初始化数据过程中多次刷新视图
82
+ // 这里需要重新取一下treeModel的值确保为最新的TreeModel
83
+ await openedEditorModelService.treeModel.ensureReady;
84
+ if (token.isCancellationRequested) {
85
+ return;
86
+ }
87
+ }
88
+ setIsLoading(false);
89
+ setIsReady(true);
90
+ },
91
+ [isLoading, isReady, openedEditorModelService],
92
+ );
93
+
94
+ useEffect(() => {
95
+ if (isReady) {
96
+ openedEditorModelService.onTreeModelChange(async (treeModel) => {
97
+ setIsLoading(true);
98
+ if (treeModel) {
99
+ // 确保数据初始化完毕,减少初始化数据过程中多次刷新视图
100
+ await treeModel.ensureReady;
101
+ }
102
+ setModel(treeModel);
103
+ setIsLoading(false);
104
+ });
105
+ }
106
+ }, [isReady]);
107
+
108
+ useEffect(() => {
109
+ const tokenSource = new CancellationTokenSource();
110
+ ensureIsReady(tokenSource.token);
111
+ return () => {
112
+ tokenSource.cancel();
113
+ };
114
+ }, []);
115
+
116
+ useEffect(() => {
117
+ const handleBlur = () => {
118
+ openedEditorModelService.handleTreeBlur();
119
+ };
120
+ wrapperRef.current?.addEventListener('blur', handleBlur, true);
121
+ return () => {
122
+ wrapperRef.current?.removeEventListener('blur', handleBlur, true);
123
+ openedEditorModelService.handleTreeBlur();
124
+ };
125
+ }, [wrapperRef.current]);
126
+
127
+ const renderTreeNode = useCallback(
128
+ (props: INodeRendererWrapProps) => (
129
+ <EditorTreeNode
130
+ item={props.item}
131
+ itemType={props.itemType}
132
+ decorationService={decorationService}
133
+ labelService={labelService}
134
+ commandService={commandService}
135
+ decorations={openedEditorModelService.decorations.getDecorations(props.item as any)}
136
+ onClick={handleItemClicked}
137
+ onContextMenu={handlerContextMenu}
138
+ defaultLeftPadding={22}
139
+ leftPadding={0}
140
+ />
141
+ ),
142
+ [openedEditorModelService.treeModel],
143
+ );
144
+
145
+ const renderContent = () => {
146
+ if (!isReady) {
147
+ return <span className={styles.opened_editor_empty_text}>{localize('opened.editors.empty')}</span>;
148
+ } else {
149
+ if (isLoading) {
150
+ return <Progress loading />;
151
+ } else if (model) {
152
+ return (
153
+ <RecycleTree
154
+ height={height}
155
+ width={width}
156
+ itemHeight={OPEN_EDITOR_NODE_HEIGHT}
157
+ onReady={handleTreeReady}
158
+ model={model}
159
+ placeholder={() => (
160
+ <span className={styles.opened_editor_empty_text}>{localize('opened.editors.empty')}</span>
161
+ )}
162
+ >
163
+ {renderTreeNode}
164
+ </RecycleTree>
165
+ );
166
+ } else {
167
+ return <span className={styles.opened_editor_empty_text}>{localize('opened.editors.empty')}</span>;
168
+ }
169
+ }
170
+ };
171
+
172
+ return (
173
+ <div
174
+ className={styles.opened_editor_container}
175
+ tabIndex={-1}
176
+ ref={wrapperRef}
177
+ onContextMenu={handleOuterContextMenu}
178
+ onClick={handleOuterClick}
179
+ >
180
+ {renderContent()}
181
+ </div>
182
+ );
183
+ };
@@ -0,0 +1,63 @@
1
+ import { Injectable, Autowired } from '@opensumi/di';
2
+ import {
3
+ URI,
4
+ Uri,
5
+ FileDecorationsProvider,
6
+ IFileDecoration,
7
+ Emitter,
8
+ DisposableCollection,
9
+ } from '@opensumi/ide-core-browser';
10
+ import { IDecorationsService } from '@opensumi/ide-decoration';
11
+ import { IThemeService } from '@opensumi/ide-theme';
12
+
13
+ @Injectable()
14
+ export class OpenedEditorDecorationService implements FileDecorationsProvider {
15
+ @Autowired(IDecorationsService)
16
+ private readonly decorationsService: IDecorationsService;
17
+
18
+ @Autowired(IThemeService)
19
+ public readonly themeService: IThemeService;
20
+
21
+ private disposeCollection: DisposableCollection = new DisposableCollection();
22
+
23
+ private readonly onDidChangeEmitter: Emitter<void> = new Emitter();
24
+
25
+ constructor() {
26
+ this.disposeCollection.pushAll([
27
+ this.decorationsService.onDidChangeDecorations(() => {
28
+ this.onDidChangeEmitter.fire();
29
+ }),
30
+ this.themeService.onThemeChange(() => {
31
+ this.onDidChangeEmitter.fire();
32
+ }),
33
+ ]);
34
+ }
35
+
36
+ get onDidChange() {
37
+ return this.onDidChangeEmitter.event;
38
+ }
39
+
40
+ getDecoration(uri, hasChildren = false) {
41
+ // 转换URI为vscode.uri
42
+ if (uri instanceof URI) {
43
+ uri = Uri.parse(uri.toString());
44
+ }
45
+ const decoration = this.decorationsService.getDecoration(uri, hasChildren);
46
+ if (decoration) {
47
+ return {
48
+ ...decoration,
49
+ // 通过ThemeService获取颜色值
50
+ color: this.themeService.getColor({ id: decoration.color as string }),
51
+ } as IFileDecoration;
52
+ }
53
+ return {
54
+ color: '',
55
+ tooltip: '',
56
+ badge: '',
57
+ } as IFileDecoration;
58
+ }
59
+
60
+ dispose() {
61
+ this.disposeCollection.dispose();
62
+ }
63
+ }
@@ -0,0 +1,51 @@
1
+ import { Injectable } from '@opensumi/di';
2
+ import { Event, Emitter, WithEventBus, OnEvent } from '@opensumi/ide-core-browser';
3
+ import {
4
+ IResource,
5
+ IEditorGroup,
6
+ ResourceDecorationChangeEvent,
7
+ IResourceDecorationChangeEventPayload,
8
+ } from '@opensumi/ide-editor';
9
+ import { EditorGroupOpenEvent, EditorGroupCloseEvent, EditorGroupDisposeEvent } from '@opensumi/ide-editor/lib/browser';
10
+
11
+ export type OpenedEditorData = IEditorGroup | IResource;
12
+ export interface OpenedEditorEvent {
13
+ group: IEditorGroup;
14
+ resource: IResource;
15
+ }
16
+
17
+ @Injectable()
18
+ export class OpenedEditorEventService extends WithEventBus {
19
+ private _onDidChange: Emitter<OpenedEditorEvent | null> = new Emitter();
20
+ private _onDidDecorationChange: Emitter<IResourceDecorationChangeEventPayload | null> = new Emitter();
21
+ private _onDidActiveChange: Emitter<OpenedEditorEvent | null> = new Emitter();
22
+
23
+ public onDidChange: Event<OpenedEditorEvent | null> = this._onDidChange.event;
24
+ public onDidDecorationChange: Event<IResourceDecorationChangeEventPayload | null> = this._onDidDecorationChange.event;
25
+ public onDidActiveChange: Event<OpenedEditorEvent | null> = this._onDidActiveChange.event;
26
+
27
+ constructor() {
28
+ super();
29
+ }
30
+
31
+ @OnEvent(EditorGroupOpenEvent)
32
+ onEditorGroupOpenEvent(e: EditorGroupOpenEvent) {
33
+ this._onDidActiveChange.fire(e.payload);
34
+ }
35
+
36
+ @OnEvent(EditorGroupCloseEvent)
37
+ onEditorGroupCloseEvent() {
38
+ this._onDidChange.fire(null);
39
+ }
40
+
41
+ @OnEvent(EditorGroupDisposeEvent)
42
+ onEditorGroupDisposeEvent() {
43
+ this._onDidChange.fire(null);
44
+ }
45
+
46
+ // 为修改的文件添加dirty装饰
47
+ @OnEvent(ResourceDecorationChangeEvent)
48
+ onResourceDecorationChangeEvent(e: ResourceDecorationChangeEvent) {
49
+ this._onDidDecorationChange.fire(e.payload);
50
+ }
51
+ }