diff --git a/packages/core/src/browser/tree/tree-model.ts b/packages/core/src/browser/tree/tree-model.ts index deb34055fd4e2..c51b5a14b36bb 100644 --- a/packages/core/src/browser/tree/tree-model.ts +++ b/packages/core/src/browser/tree/tree-model.ts @@ -147,6 +147,7 @@ export class TreeModelImpl implements TreeModel, SelectionProvider(); + protected readonly onNodesAddedEmitter = new Emitter(); protected readonly onOpenNodeEmitter = new Emitter(); protected readonly toDispose = new DisposableCollection(); @@ -154,6 +155,7 @@ export class TreeModelImpl implements TreeModel, SelectionProvider this.fireChanged())); + this.toDispose.push(this.tree.onNodesAdded(nodes => this.onNodesAddedEmitter.fire(nodes))); this.toDispose.push(this.selectionService); @@ -199,6 +201,10 @@ export class TreeModelImpl implements TreeModel, SelectionProvider { + return this.onNodesAddedEmitter.event; + } + get onOpenNode(): Event { return this.onOpenNodeEmitter.event; } diff --git a/packages/core/src/browser/tree/tree.ts b/packages/core/src/browser/tree/tree.ts index 924cb3cd0f796..706060a390507 100644 --- a/packages/core/src/browser/tree/tree.ts +++ b/packages/core/src/browser/tree/tree.ts @@ -37,6 +37,11 @@ export interface Tree extends Disposable { * Emit when the tree is changed. */ readonly onChanged: Event; + + /** + * Emit when new nodes are added to the tree. + */ + readonly onNodesAdded: Event; /** * Return a node for the given identifier or undefined if such does not exist. */ @@ -233,6 +238,7 @@ export class TreeImpl implements Tree { protected _root: TreeNode | undefined; protected readonly onChangedEmitter = new Emitter(); + protected readonly onNodesAddedEmitter = new Emitter(); protected readonly onNodeRefreshedEmitter = new Emitter(); protected readonly toDispose = new DisposableCollection(); @@ -247,6 +253,7 @@ export class TreeImpl implements Tree { this.toDispose.push(this.onChangedEmitter); this.toDispose.push(this.onNodeRefreshedEmitter); this.toDispose.push(this.onDidChangeBusyEmitter); + this.toDispose.push(this.onNodesAddedEmitter); } dispose(): void { @@ -269,6 +276,10 @@ export class TreeImpl implements Tree { return this.onChangedEmitter.event; } + get onNodesAdded(): Event { + return this.onNodesAddedEmitter.event; + } + protected fireChanged(): void { this.onChangedEmitter.fire(undefined); } @@ -314,6 +325,12 @@ export class TreeImpl implements Tree { } protected async setChildren(parent: CompositeTreeNode, children: TreeNode[]): Promise { + const newNodes: TreeNode[] = []; + children.forEach(newNode => { + if (!parent.children.find(node => node.id === newNode.id)) { + newNodes.push(newNode); + } + }); const root = this.getRootNode(parent); if (this.nodes[root.id] && this.nodes[root.id] !== root) { console.error(`Child node '${parent.id}' does not belong to this '${root.id}' tree.`); @@ -323,6 +340,9 @@ export class TreeImpl implements Tree { parent.children = children; this.addNode(parent); await this.fireNodeRefreshed(parent); + if (newNodes.length > 0) { + this.onNodesAddedEmitter.fire(newNodes); + } return parent; } diff --git a/packages/navigator/src/browser/navigator-contribution.ts b/packages/navigator/src/browser/navigator-contribution.ts index 7b30a5ac28378..ffd87df45e48a 100644 --- a/packages/navigator/src/browser/navigator-contribution.ts +++ b/packages/navigator/src/browser/navigator-contribution.ts @@ -14,27 +14,49 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { injectable, inject, postConstruct } from 'inversify'; +import { inject, injectable, postConstruct } from 'inversify'; import { AbstractViewContribution } from '@theia/core/lib/browser/shell/view-contribution'; import { - Navigatable, SelectableTreeNode, Widget, KeybindingRegistry, CommonCommands, - OpenerService, FrontendApplicationContribution, FrontendApplication, CompositeTreeNode, PreferenceScope + CommonCommands, + CompositeTreeNode, ExpandableTreeNode, + FrontendApplication, + FrontendApplicationContribution, + KeybindingRegistry, + Navigatable, + OpenerService, + PreferenceScope, + PreferenceService, + SelectableTreeNode, + SHELL_TABBAR_CONTEXT_MENU, + Widget } from '@theia/core/lib/browser'; import { FileDownloadCommands } from '@theia/filesystem/lib/browser/download/file-download-command-contribution'; -import { CommandRegistry, MenuModelRegistry, MenuPath, isOSX, Command, DisposableCollection, Mutable } from '@theia/core/lib/common'; -import { SHELL_TABBAR_CONTEXT_MENU } from '@theia/core/lib/browser'; -import { WorkspaceCommands, WorkspaceService, WorkspacePreferences } from '@theia/workspace/lib/browser'; -import { FILE_NAVIGATOR_ID, FileNavigatorWidget, EXPLORER_VIEW_CONTAINER_ID } from './navigator-widget'; +import { + Command, + CommandRegistry, + DisposableCollection, + isOSX, + MenuModelRegistry, + MenuPath, + Mutable +} from '@theia/core/lib/common'; +import { WorkspaceCommands, WorkspacePreferences, WorkspaceService } from '@theia/workspace/lib/browser'; +import { EXPLORER_VIEW_CONTAINER_ID, FILE_NAVIGATOR_ID, FileNavigatorWidget } from './navigator-widget'; import { FileNavigatorPreferences } from './navigator-preferences'; import { NavigatorKeybindingContexts } from './navigator-keybinding-context'; import { FileNavigatorFilter } from './navigator-filter'; import { WorkspaceNode } from './navigator-tree'; import { NavigatorContextKeyService } from './navigator-context-key-service'; -import { TabBarToolbarContribution, TabBarToolbarRegistry, TabBarToolbarItem } from '@theia/core/lib/browser/shell/tab-bar-toolbar'; +import { + TabBarToolbarContribution, + TabBarToolbarItem, + TabBarToolbarRegistry +} from '@theia/core/lib/browser/shell/tab-bar-toolbar'; import { FileSystemCommands } from '@theia/filesystem/lib/browser/filesystem-frontend-contribution'; import { NavigatorDiff, NavigatorDiffCommands } from './navigator-diff'; import { UriSelection } from '@theia/core/lib/common/selection'; -import { PreferenceService } from '@theia/core/lib/browser'; +import { FileChangeType, FileSystemWatcher } from '@theia/filesystem/lib/browser'; +import { DirNode } from '@theia/filesystem/lib/browser/file-tree/file-tree'; export namespace FileNavigatorCommands { export const REVEAL_IN_NAVIGATOR: Command = { @@ -129,6 +151,9 @@ export class FileNavigatorContribution extends AbstractViewContribution { + // Select created new file or folder. + const node = nodes[0]; + if (nodes.length === 1 && SelectableTreeNode.is(node) + && (this.fileNavigatorPreferences['explorer.autoReveal'] ? true : DirNode.is(node))) { + model.selectNode(node); + } + }); + this.watcher.onFilesChanged(changes => { + // Expand the parent of the created new file or folder. + const change = changes[0]; + if (changes.length === 1 && change.type === FileChangeType.ADDED) { + const node = model.getNodesByUri(change.uri.parent).next().value; + if (ExpandableTreeNode.is(node) && !node.expanded) { + model.expandNode(node); + } + } + }); } async onStart(app: FrontendApplication): Promise {