@opensumi/ide-outline 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 (42) hide show
  1. package/lib/browser/index.js.map +1 -1
  2. package/lib/browser/outline-node.d.ts +1 -1
  3. package/lib/browser/outline-node.d.ts.map +1 -1
  4. package/lib/browser/outline-node.define.js +10 -10
  5. package/lib/browser/outline-node.define.js.map +1 -1
  6. package/lib/browser/outline-node.js +2 -2
  7. package/lib/browser/outline-node.js.map +1 -1
  8. package/lib/browser/outline-node.module.less +5 -23
  9. package/lib/browser/outline.contribution.js +6 -6
  10. package/lib/browser/outline.contribution.js.map +1 -1
  11. package/lib/browser/outline.d.ts +3 -3
  12. package/lib/browser/outline.d.ts.map +1 -1
  13. package/lib/browser/outline.js +11 -11
  14. package/lib/browser/outline.js.map +1 -1
  15. package/lib/browser/services/outline-contextkey.service.js.map +1 -1
  16. package/lib/browser/services/outline-decoration.service.js.map +1 -1
  17. package/lib/browser/services/outline-event.service.d.ts +1 -1
  18. package/lib/browser/services/outline-event.service.d.ts.map +1 -1
  19. package/lib/browser/services/outline-event.service.js.map +1 -1
  20. package/lib/browser/services/outline-model.js.map +1 -1
  21. package/lib/browser/services/outline-model.service.d.ts +0 -2
  22. package/lib/browser/services/outline-model.service.d.ts.map +1 -1
  23. package/lib/browser/services/outline-model.service.js +5 -27
  24. package/lib/browser/services/outline-model.service.js.map +1 -1
  25. package/lib/browser/services/outline-tree.service.js +4 -4
  26. package/lib/browser/services/outline-tree.service.js.map +1 -1
  27. package/package.json +13 -12
  28. package/src/browser/index.ts +33 -0
  29. package/src/browser/outline-node.define.ts +75 -0
  30. package/src/browser/outline-node.module.less +183 -0
  31. package/src/browser/outline-node.tsx +160 -0
  32. package/src/browser/outline.contribution.ts +146 -0
  33. package/src/browser/outline.module.less +11 -0
  34. package/src/browser/outline.tsx +159 -0
  35. package/src/browser/services/outline-contextkey.service.ts +18 -0
  36. package/src/browser/services/outline-decoration.service.ts +60 -0
  37. package/src/browser/services/outline-event.service.ts +45 -0
  38. package/src/browser/services/outline-model.service.ts +584 -0
  39. package/src/browser/services/outline-model.ts +38 -0
  40. package/src/browser/services/outline-tree.service.ts +180 -0
  41. package/src/common/index.ts +32 -0
  42. package/src/index.ts +1 -0
@@ -0,0 +1,146 @@
1
+ import { Autowired } from '@opensumi/di';
2
+ import {
3
+ OUTLINE_COMMANDS,
4
+ Domain,
5
+ localize,
6
+ TabBarToolbarContribution,
7
+ ToolbarRegistry,
8
+ CommandContribution,
9
+ CommandRegistry,
10
+ IContextKeyService,
11
+ } from '@opensumi/ide-core-browser';
12
+ import { getIcon } from '@opensumi/ide-core-browser';
13
+ import { OutlineSortTypeContext, OutlineFollowCursorContext } from '@opensumi/ide-core-browser/lib/contextkey';
14
+ import { EXPLORER_CONTAINER_ID } from '@opensumi/ide-explorer/lib/browser/explorer-contribution';
15
+ import { MainLayoutContribution, IMainLayoutService } from '@opensumi/ide-main-layout';
16
+
17
+ import { OutlineSortOrder, OUTLINE_VIEW_ID } from '../common';
18
+
19
+ import { OutlinePanel } from './outline';
20
+ import { OutlineModelService } from './services/outline-model.service';
21
+ import { OutlineTreeService } from './services/outline-tree.service';
22
+
23
+ @Domain(MainLayoutContribution, TabBarToolbarContribution, CommandContribution)
24
+ export class OutlineContribution implements MainLayoutContribution, TabBarToolbarContribution, CommandContribution {
25
+ @Autowired(IMainLayoutService)
26
+ private mainLayoutService: IMainLayoutService;
27
+
28
+ @Autowired()
29
+ private outlineTreeService: OutlineTreeService;
30
+
31
+ @Autowired()
32
+ private outlineTreeModelService: OutlineModelService;
33
+
34
+ @Autowired(IContextKeyService)
35
+ contextKey: IContextKeyService;
36
+
37
+ onDidRender() {
38
+ this.mainLayoutService.collectViewComponent(
39
+ {
40
+ component: OutlinePanel,
41
+ collapsed: true,
42
+ id: OUTLINE_VIEW_ID,
43
+ name: localize('outline.title'),
44
+ },
45
+ EXPLORER_CONTAINER_ID,
46
+ );
47
+ }
48
+
49
+ registerCommands(registry: CommandRegistry) {
50
+ registry.registerCommand(
51
+ {
52
+ id: OUTLINE_COMMANDS.OUTLINE_COLLAPSE_ALL.id,
53
+ iconClass: getIcon('collapse-all'),
54
+ label: localize('outline.collapse.all'),
55
+ },
56
+ {
57
+ execute: () => {
58
+ this.outlineTreeModelService.collapseAll();
59
+ },
60
+ },
61
+ );
62
+ registry.registerCommand(
63
+ {
64
+ id: OUTLINE_COMMANDS.OUTLINE_FOLLOW_CURSOR.id,
65
+ iconClass: getIcon('follow-cursor'),
66
+ toogleIconClass: getIcon('follow-cursor', { fill: true }),
67
+ label: localize('outline.follow.cursor'),
68
+ },
69
+ {
70
+ execute: () => {
71
+ this.outlineTreeService.followCursor = !this.outlineTreeService.followCursor;
72
+ },
73
+ },
74
+ );
75
+ registry.registerCommand(
76
+ {
77
+ id: OUTLINE_COMMANDS.OUTLINE_SORT_KIND.id,
78
+ label: localize('outline.sort.kind'),
79
+ },
80
+ {
81
+ execute: () => {
82
+ this.outlineTreeService.sortType = OutlineSortOrder.ByKind;
83
+ },
84
+ },
85
+ );
86
+ registry.registerCommand(
87
+ {
88
+ id: OUTLINE_COMMANDS.OUTLINE_SORT_NAME.id,
89
+ label: localize('outline.sort.name'),
90
+ },
91
+ {
92
+ execute: () => {
93
+ this.outlineTreeService.sortType = OutlineSortOrder.ByName;
94
+ },
95
+ },
96
+ );
97
+ registry.registerCommand(
98
+ {
99
+ id: OUTLINE_COMMANDS.OUTLINE_SORT_POSITION.id,
100
+ label: localize('outline.sort.position'),
101
+ },
102
+ {
103
+ execute: () => {
104
+ this.outlineTreeService.sortType = OutlineSortOrder.ByPosition;
105
+ },
106
+ },
107
+ );
108
+ }
109
+
110
+ registerToolbarItems(registry: ToolbarRegistry) {
111
+ registry.registerItem({
112
+ id: 'outline.action.follow.cursor',
113
+ viewId: 'outline-view',
114
+ command: OUTLINE_COMMANDS.OUTLINE_FOLLOW_CURSOR.id,
115
+ label: localize('outline.follow.cursor'),
116
+ toggledWhen: OutlineFollowCursorContext.raw,
117
+ });
118
+ registry.registerItem({
119
+ id: 'outline.action.collapse.all',
120
+ viewId: 'outline-view',
121
+ command: OUTLINE_COMMANDS.OUTLINE_COLLAPSE_ALL.id,
122
+ label: localize('outline.collapse.all'),
123
+ });
124
+ registry.registerItem({
125
+ id: 'outline.menu.sort.kind',
126
+ viewId: 'outline-view',
127
+ command: OUTLINE_COMMANDS.OUTLINE_SORT_KIND.id,
128
+ group: 'inline',
129
+ toggledWhen: `${OutlineSortTypeContext.raw} == ${OutlineSortOrder.ByKind}`,
130
+ });
131
+ registry.registerItem({
132
+ id: 'outline.menu.sort.name',
133
+ viewId: 'outline-view',
134
+ command: OUTLINE_COMMANDS.OUTLINE_SORT_NAME.id,
135
+ group: 'inline',
136
+ toggledWhen: `${OutlineSortTypeContext.raw} == ${OutlineSortOrder.ByName}`,
137
+ });
138
+ registry.registerItem({
139
+ id: 'outline.menu.sort.position',
140
+ viewId: 'outline-view',
141
+ command: OUTLINE_COMMANDS.OUTLINE_SORT_POSITION.id,
142
+ group: 'inline',
143
+ toggledWhen: `${OutlineSortTypeContext.raw} == ${OutlineSortOrder.ByPosition}`,
144
+ });
145
+ }
146
+ }
@@ -0,0 +1,11 @@
1
+ .outline_empty_text {
2
+ display: block;
3
+ text-align: center;
4
+ padding-top: 12px;
5
+ user-select: none;
6
+ color: var(--descriptionForeground);
7
+ }
8
+
9
+ .outline_container {
10
+ overflow: hidden;
11
+ }
@@ -0,0 +1,159 @@
1
+ import React, {
2
+ PropsWithChildren,
3
+ useCallback,
4
+ RefObject,
5
+ MouseEvent,
6
+ useState,
7
+ createRef,
8
+ useEffect,
9
+ memo,
10
+ } from 'react';
11
+
12
+ import { RecycleTree, IRecycleTreeHandle, INodeRendererWrapProps, TreeNodeType } from '@opensumi/ide-components';
13
+ import { ViewState } from '@opensumi/ide-core-browser';
14
+ import { localize } from '@opensumi/ide-core-browser';
15
+ import { useInjectable } from '@opensumi/ide-core-browser/lib/react-hooks';
16
+
17
+ import { OUTLINE_TREE_NODE_HEIGHT, OutlineNode } from './outline-node';
18
+ import { OutlineCompositeTreeNode, OutlineTreeNode } from './outline-node.define';
19
+ import styles from './outline.module.less';
20
+ import { OutlineTreeModel } from './services/outline-model';
21
+ import { OutlineModelService } from './services/outline-model.service';
22
+
23
+ export const OutlinePanel = ({ viewState }: PropsWithChildren<{ viewState: ViewState }>) => {
24
+ const [model, setModel] = useState<OutlineTreeModel | undefined>();
25
+
26
+ const { height } = viewState;
27
+
28
+ const wrapperRef: RefObject<HTMLDivElement> = createRef();
29
+
30
+ const outlineModelService = useInjectable<OutlineModelService>(OutlineModelService);
31
+
32
+ const handleTreeReady = useCallback(
33
+ (handle: IRecycleTreeHandle) => {
34
+ outlineModelService.handleTreeHandler({
35
+ ...handle,
36
+ getModel: () => outlineModelService.treeModel,
37
+ hasDirectFocus: () => wrapperRef.current === document.activeElement,
38
+ });
39
+ },
40
+ [outlineModelService, wrapperRef.current],
41
+ );
42
+
43
+ const handleItemClicked = useCallback(
44
+ (ev: MouseEvent, item: OutlineTreeNode | OutlineCompositeTreeNode, type: TreeNodeType) => {
45
+ // 阻止点击事件冒泡
46
+ ev.stopPropagation();
47
+
48
+ const { handleItemClick } = outlineModelService;
49
+ if (!item) {
50
+ return;
51
+ }
52
+ handleItemClick(item, type);
53
+ },
54
+ [outlineModelService],
55
+ );
56
+
57
+ const handleTwistierClicked = useCallback(
58
+ (ev: MouseEvent, item: OutlineTreeNode | OutlineCompositeTreeNode) => {
59
+ // 阻止点击事件冒泡
60
+ ev.stopPropagation();
61
+
62
+ const { toggleDirectory } = outlineModelService;
63
+ if (!item) {
64
+ return;
65
+ }
66
+ toggleDirectory(item as OutlineCompositeTreeNode);
67
+ },
68
+ [outlineModelService],
69
+ );
70
+
71
+ const handleOuterClick = useCallback(() => {
72
+ // 空白区域点击,取消焦点状态
73
+ const { enactiveNodeDecoration } = outlineModelService;
74
+ enactiveNodeDecoration();
75
+ }, [outlineModelService]);
76
+
77
+ useEffect(() => {
78
+ setModel(outlineModelService.treeModel);
79
+ const disposable = outlineModelService.onDidUpdateTreeModel((model?: OutlineTreeModel) => {
80
+ setModel(model);
81
+ });
82
+ return () => {
83
+ disposable.dispose();
84
+ };
85
+ }, []);
86
+
87
+ useEffect(() => {
88
+ const handleBlur = () => {
89
+ outlineModelService.handleTreeBlur();
90
+ };
91
+ wrapperRef.current?.addEventListener('blur', handleBlur, true);
92
+ return () => {
93
+ wrapperRef.current?.removeEventListener('blur', handleBlur, true);
94
+ outlineModelService.handleTreeBlur();
95
+ };
96
+ }, [wrapperRef.current]);
97
+
98
+ return (
99
+ <div className={styles.outline_container} tabIndex={-1} ref={wrapperRef} onClick={handleOuterClick}>
100
+ <OutlineTreeView
101
+ height={height}
102
+ model={model}
103
+ onItemClick={handleItemClicked}
104
+ onTwistierClick={handleTwistierClicked}
105
+ onDidTreeReady={handleTreeReady}
106
+ />
107
+ </div>
108
+ );
109
+ };
110
+
111
+ interface IOutlineTreeViewProps {
112
+ height: number;
113
+ model?: OutlineTreeModel;
114
+ onDidTreeReady(handle: IRecycleTreeHandle): void;
115
+ onItemClick(ev: MouseEvent, item: OutlineTreeNode | OutlineCompositeTreeNode, type: TreeNodeType): void;
116
+ onTwistierClick(ev: MouseEvent, item: OutlineTreeNode | OutlineCompositeTreeNode): void;
117
+ }
118
+
119
+ export const OutlineTreeView = memo(
120
+ ({ height, model, onItemClick, onTwistierClick, onDidTreeReady }: IOutlineTreeViewProps) => {
121
+ const outlineModelService = useInjectable<OutlineModelService>(OutlineModelService);
122
+ const { decorationService, commandService } = outlineModelService;
123
+
124
+ const renderTreeNode = useCallback(
125
+ (props: INodeRendererWrapProps) => (
126
+ <OutlineNode
127
+ item={props.item}
128
+ itemType={props.itemType}
129
+ decorationService={decorationService}
130
+ commandService={commandService}
131
+ decorations={outlineModelService.decorations.getDecorations(props.item as any)}
132
+ onClick={onItemClick}
133
+ onTwistierClick={onTwistierClick}
134
+ defaultLeftPadding={8}
135
+ leftPadding={8}
136
+ />
137
+ ),
138
+ [model],
139
+ );
140
+
141
+ if (!model) {
142
+ return <span className={styles.outline_empty_text}>{localize('outline.noinfo')}</span>;
143
+ } else {
144
+ return (
145
+ <RecycleTree
146
+ height={height}
147
+ itemHeight={OUTLINE_TREE_NODE_HEIGHT}
148
+ onReady={onDidTreeReady}
149
+ model={model}
150
+ placeholder={() => <span className={styles.outline_empty_text}>{localize('outline.noinfo')}</span>}
151
+ >
152
+ {renderTreeNode}
153
+ </RecycleTree>
154
+ );
155
+ }
156
+ },
157
+ );
158
+
159
+ OutlineTreeView.displayName = 'OutlineTreeView';
@@ -0,0 +1,18 @@
1
+ import { Optional, Injectable, Autowired } from '@opensumi/di';
2
+ import { IContextKeyService, IContextKey } from '@opensumi/ide-core-browser';
3
+ import { OutlineSortTypeContext, OutlineFollowCursorContext } from '@opensumi/ide-core-browser/lib/contextkey';
4
+
5
+ @Injectable()
6
+ export class OutlineContextKeyService {
7
+ @Autowired(IContextKeyService)
8
+ private readonly globalContextKeyService: IContextKeyService;
9
+
10
+ public readonly outlineSortTypeContext: IContextKey<number>;
11
+ public readonly outlineFollowCursorContext: IContextKey<boolean>;
12
+
13
+ constructor(@Optional() contextKeyService: IContextKeyService) {
14
+ contextKeyService = contextKeyService || this.globalContextKeyService;
15
+ this.outlineSortTypeContext = OutlineSortTypeContext.bind(contextKeyService);
16
+ this.outlineFollowCursorContext = OutlineFollowCursorContext.bind(contextKeyService);
17
+ }
18
+ }
@@ -0,0 +1,60 @@
1
+ import { Injectable, Autowired } from '@opensumi/di';
2
+ import { URI, MarkerManager, MarkerSeverity, IMarker } from '@opensumi/ide-core-browser';
3
+ import { IThemeService, listErrorForeground, listWarningForeground } from '@opensumi/ide-theme';
4
+
5
+ import { IOutlineMarker } from '../../common';
6
+ import { OutlineTreeNode } from '../outline-node.define';
7
+
8
+ @Injectable()
9
+ export class OutlineDecorationService {
10
+ @Autowired()
11
+ private markerManager: MarkerManager;
12
+
13
+ @Autowired(IThemeService)
14
+ private themeService: IThemeService;
15
+
16
+ private _diagnosisInfo: IOutlineMarker[] = [];
17
+
18
+ updateDiagnosisInfo(uri?: URI) {
19
+ this._diagnosisInfo = uri ? this.markerManager.getMarkers({ resource: uri.toString(), opened: true }) : [];
20
+ }
21
+
22
+ getDecoration(node: OutlineTreeNode) {
23
+ const markers: IOutlineMarker[] = [];
24
+ let topMarker: IOutlineMarker | undefined;
25
+ // 根据 node.raw.range 判断相交情况
26
+ // 这里的相交判断实际上可以做一下数据裁剪
27
+ const diagnosisInfos = this._diagnosisInfo.filter((marker: IMarker) => {
28
+ if (
29
+ marker.startLineNumber <= node.raw.range.startLineNumber &&
30
+ marker.endLineNumber >= node.raw.range.startLineNumber
31
+ ) {
32
+ return true;
33
+ } else if (
34
+ node.raw.range.startLineNumber <= marker.startLineNumber &&
35
+ node.raw.range.endLineNumber >= marker.startLineNumber
36
+ ) {
37
+ return true;
38
+ }
39
+ return false;
40
+ });
41
+ for (const marker of diagnosisInfos) {
42
+ if (marker.severity === MarkerSeverity.Error || marker.severity === MarkerSeverity.Warning) {
43
+ markers.push(marker);
44
+ }
45
+ if (!topMarker || marker.severity > topMarker.severity) {
46
+ topMarker = marker;
47
+ }
48
+ }
49
+ return {
50
+ color:
51
+ topMarker?.severity === MarkerSeverity.Error || topMarker?.severity === MarkerSeverity.Warning
52
+ ? this.themeService.getColor({
53
+ id: topMarker.severity === MarkerSeverity.Error ? listErrorForeground : listWarningForeground,
54
+ })
55
+ : undefined,
56
+ tooltip: topMarker?.message,
57
+ badge: markers.length > 0 ? (markers.length > 9 ? '9+' : markers.length + '') : '',
58
+ };
59
+ }
60
+ }
@@ -0,0 +1,45 @@
1
+ import { Injectable } from '@opensumi/di';
2
+ import { Emitter, WithEventBus, OnEvent, URI, Event } from '@opensumi/ide-core-browser';
3
+ import { IResource, IEditorGroup } from '@opensumi/ide-editor';
4
+ import { EditorActiveResourceStateChangedEvent, EditorSelectionChangeEvent } from '@opensumi/ide-editor/lib/browser';
5
+ import { DocumentSymbolChangedEvent } from '@opensumi/ide-editor/lib/browser/breadcrumb/document-symbol';
6
+
7
+ export type OpenedEditorData = IEditorGroup | IResource;
8
+ export interface OpenedEditorEvent {
9
+ group: IEditorGroup;
10
+ resource: IResource;
11
+ }
12
+
13
+ @Injectable()
14
+ export class OutlineEventService extends WithEventBus {
15
+ private _onDidChange: Emitter<URI | null> = new Emitter();
16
+ private _onDidSelectionChange: Emitter<URI | null> = new Emitter();
17
+ private _onDidActiveChange: Emitter<URI | null> = new Emitter();
18
+
19
+ get onDidChange(): Event<URI | null> {
20
+ return this._onDidChange.event;
21
+ }
22
+
23
+ get onDidActiveChange(): Event<URI | null> {
24
+ return this._onDidActiveChange.event;
25
+ }
26
+
27
+ get onDidSelectionChange(): Event<URI | null> {
28
+ return this._onDidSelectionChange.event;
29
+ }
30
+
31
+ @OnEvent(EditorActiveResourceStateChangedEvent)
32
+ onEditorActiveResourceStateChangedEvent(e: EditorActiveResourceStateChangedEvent) {
33
+ this._onDidActiveChange.fire(e.payload.editorUri!);
34
+ }
35
+
36
+ @OnEvent(EditorSelectionChangeEvent)
37
+ onEditorSelectionChangeEvent(e: EditorSelectionChangeEvent) {
38
+ this._onDidSelectionChange.fire(e.payload.editorUri);
39
+ }
40
+
41
+ @OnEvent(DocumentSymbolChangedEvent)
42
+ onDocumentSymbolChange(e: DocumentSymbolChangedEvent) {
43
+ this._onDidChange.fire(e.payload);
44
+ }
45
+ }