parent
f33dabe7a4
commit
25e889af45
|
@ -634,7 +634,7 @@ export interface IEditableData {
|
|||
validationMessage: (value: string) => { content: string, severity: Severity } | null;
|
||||
placeholder?: string | null;
|
||||
startingValue?: string | null;
|
||||
onFinish: (value: string, success: boolean) => void;
|
||||
onFinish: (value: string, success: boolean) => Promise<void>;
|
||||
}
|
||||
|
||||
export interface IViewPaneContainer {
|
||||
|
|
|
@ -80,10 +80,10 @@ function onError(notificationService: INotificationService, error: any): void {
|
|||
notificationService.error(toErrorMessage(error, false));
|
||||
}
|
||||
|
||||
function refreshIfSeparator(value: string, explorerService: IExplorerService): void {
|
||||
async function refreshIfSeparator(value: string, explorerService: IExplorerService): Promise<void> {
|
||||
if (value && ((value.indexOf('/') >= 0) || (value.indexOf('\\') >= 0))) {
|
||||
// New input contains separator, multiple resources will get created workaround for #68204
|
||||
explorerService.refresh();
|
||||
await explorerService.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,10 +98,6 @@ export class NewFileAction extends Action {
|
|||
) {
|
||||
super('explorer.newFile', NEW_FILE_LABEL);
|
||||
this.class = 'explorer-action ' + Codicon.newFile.classNames;
|
||||
this._register(explorerService.onDidChangeEditable(e => {
|
||||
const elementIsBeingEdited = explorerService.isEditable(e);
|
||||
this.enabled = !elementIsBeingEdited;
|
||||
}));
|
||||
}
|
||||
|
||||
run(): Promise<void> {
|
||||
|
@ -120,10 +116,6 @@ export class NewFolderAction extends Action {
|
|||
) {
|
||||
super('explorer.newFolder', NEW_FOLDER_LABEL);
|
||||
this.class = 'explorer-action ' + Codicon.newFolder.classNames;
|
||||
this._register(explorerService.onDidChangeEditable(e => {
|
||||
const elementIsBeingEdited = explorerService.isEditable(e);
|
||||
this.enabled = !elementIsBeingEdited;
|
||||
}));
|
||||
}
|
||||
|
||||
run(): Promise<void> {
|
||||
|
@ -708,10 +700,6 @@ export class CollapseExplorerView extends Action {
|
|||
@IExplorerService readonly explorerService: IExplorerService
|
||||
) {
|
||||
super(id, label, 'explorer-action ' + Codicon.collapseAll.classNames);
|
||||
this._register(explorerService.onDidChangeEditable(e => {
|
||||
const elementIsBeingEdited = explorerService.isEditable(e);
|
||||
this.enabled = !elementIsBeingEdited;
|
||||
}));
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
|
@ -735,15 +723,11 @@ export class RefreshExplorerView extends Action {
|
|||
@IExplorerService private readonly explorerService: IExplorerService
|
||||
) {
|
||||
super(id, label, 'explorer-action ' + Codicon.refresh.classNames);
|
||||
this._register(explorerService.onDidChangeEditable(e => {
|
||||
const elementIsBeingEdited = explorerService.isEditable(e);
|
||||
this.enabled = !elementIsBeingEdited;
|
||||
}));
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
await this.viewletService.openViewlet(VIEWLET_ID);
|
||||
this.explorerService.refresh();
|
||||
await this.explorerService.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -958,7 +942,7 @@ async function openExplorerAndCreate(accessor: ServicesAccessor, isFolder: boole
|
|||
const onSuccess = async (value: string): Promise<void> => {
|
||||
try {
|
||||
const created = isFolder ? await fileService.createFolder(resources.joinPath(folder.resource, value)) : await textFileService.create(resources.joinPath(folder.resource, value));
|
||||
refreshIfSeparator(value, explorerService);
|
||||
await refreshIfSeparator(value, explorerService);
|
||||
|
||||
isFolder ?
|
||||
await explorerService.select(created.resource, true) :
|
||||
|
@ -968,11 +952,11 @@ async function openExplorerAndCreate(accessor: ServicesAccessor, isFolder: boole
|
|||
}
|
||||
};
|
||||
|
||||
explorerService.setEditable(newStat, {
|
||||
await explorerService.setEditable(newStat, {
|
||||
validationMessage: value => validateFileName(newStat, value),
|
||||
onFinish: (value, success) => {
|
||||
onFinish: async (value, success) => {
|
||||
folder.removeChild(newStat);
|
||||
explorerService.setEditable(newStat, null);
|
||||
await explorerService.setEditable(newStat, null);
|
||||
if (success) {
|
||||
onSuccess(value);
|
||||
}
|
||||
|
@ -994,7 +978,7 @@ CommandsRegistry.registerCommand({
|
|||
}
|
||||
});
|
||||
|
||||
export const renameHandler = (accessor: ServicesAccessor) => {
|
||||
export const renameHandler = async (accessor: ServicesAccessor) => {
|
||||
const explorerService = accessor.get(IExplorerService);
|
||||
const workingCopyFileService = accessor.get(IWorkingCopyFileService);
|
||||
const notificationService = accessor.get(INotificationService);
|
||||
|
@ -1005,7 +989,7 @@ export const renameHandler = (accessor: ServicesAccessor) => {
|
|||
return;
|
||||
}
|
||||
|
||||
explorerService.setEditable(stat, {
|
||||
await explorerService.setEditable(stat, {
|
||||
validationMessage: value => validateFileName(stat, value),
|
||||
onFinish: async (value, success) => {
|
||||
if (success) {
|
||||
|
@ -1014,13 +998,13 @@ export const renameHandler = (accessor: ServicesAccessor) => {
|
|||
if (stat.resource.toString() !== targetResource.toString()) {
|
||||
try {
|
||||
await workingCopyFileService.move(stat.resource, targetResource);
|
||||
refreshIfSeparator(value, explorerService);
|
||||
await refreshIfSeparator(value, explorerService);
|
||||
} catch (e) {
|
||||
notificationService.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
explorerService.setEditable(stat, null);
|
||||
await explorerService.setEditable(stat, null);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
|
@ -56,11 +56,6 @@ export class ExplorerDecorationsProvider implements IDecorationsProvider {
|
|||
this.toDispose.add(contextService.onDidChangeWorkspaceFolders(e => {
|
||||
this._onDidChange.fire(e.changed.concat(e.added).map(wf => wf.uri));
|
||||
}));
|
||||
this.toDispose.add(explorerService.onDidChangeItem(change => {
|
||||
if (change.item) {
|
||||
this._onDidChange.fire([change.item.resource]);
|
||||
}
|
||||
}));
|
||||
this.toDispose.add(explorerRootErrorEmitter.event((resource => {
|
||||
this._onDidChange.fire([resource]);
|
||||
})));
|
||||
|
|
|
@ -140,6 +140,7 @@ export class ExplorerView extends ViewPane {
|
|||
private renderer!: FilesRenderer;
|
||||
|
||||
private styleElement!: HTMLStyleElement;
|
||||
private treeContainer!: HTMLElement;
|
||||
private compressedFocusContext: IContextKey<boolean>;
|
||||
private compressedFocusFirstContext: IContextKey<boolean>;
|
||||
private compressedFocusLastContext: IContextKey<boolean>;
|
||||
|
@ -188,7 +189,7 @@ export class ExplorerView extends ViewPane {
|
|||
this.compressedFocusFirstContext = ExplorerCompressedFirstFocusContext.bindTo(contextKeyService);
|
||||
this.compressedFocusLastContext = ExplorerCompressedLastFocusContext.bindTo(contextKeyService);
|
||||
|
||||
this.explorerService.registerContextProvider(this);
|
||||
this.explorerService.registerView(this);
|
||||
}
|
||||
|
||||
get name(): string {
|
||||
|
@ -248,47 +249,17 @@ export class ExplorerView extends ViewPane {
|
|||
renderBody(container: HTMLElement): void {
|
||||
super.renderBody(container);
|
||||
|
||||
const treeContainer = DOM.append(container, DOM.$('.explorer-folders-view'));
|
||||
this.treeContainer = DOM.append(container, DOM.$('.explorer-folders-view'));
|
||||
|
||||
this.styleElement = DOM.createStyleSheet(treeContainer);
|
||||
this.styleElement = DOM.createStyleSheet(this.treeContainer);
|
||||
attachStyler<IExplorerViewColors>(this.themeService, { listDropBackground }, this.styleListDropBackground.bind(this));
|
||||
|
||||
this.createTree(treeContainer);
|
||||
this.createTree(this.treeContainer);
|
||||
|
||||
this._register(this.labelService.onDidChangeFormatters(() => {
|
||||
this._onDidChangeTitleArea.fire();
|
||||
}));
|
||||
|
||||
this._register(this.explorerService.onDidChangeRoots(() => this.setTreeInput()));
|
||||
this._register(this.explorerService.onDidChangeItem(e => {
|
||||
if (this.explorerService.isEditable(undefined)) {
|
||||
this.tree.domFocus();
|
||||
}
|
||||
this.refresh(e.recursive, e.item);
|
||||
}));
|
||||
this._register(this.explorerService.onDidChangeEditable(async e => {
|
||||
const isEditing = !!this.explorerService.getEditableData(e);
|
||||
|
||||
if (isEditing) {
|
||||
if (e.parent !== this.tree.getInput()) {
|
||||
await this.tree.expand(e.parent!);
|
||||
}
|
||||
} else {
|
||||
DOM.removeClass(treeContainer, 'highlight');
|
||||
}
|
||||
|
||||
await this.refresh(false, e.parent);
|
||||
|
||||
if (isEditing) {
|
||||
DOM.addClass(treeContainer, 'highlight');
|
||||
this.tree.reveal(e);
|
||||
} else {
|
||||
this.tree.domFocus();
|
||||
}
|
||||
}));
|
||||
this._register(this.explorerService.onDidSelectResource(e => this.onSelectResource(e.resource, e.reveal)));
|
||||
this._register(this.explorerService.onDidCopyItems(e => this.onCopyItems(e.items, e.cut, e.previouslyCutItems)));
|
||||
|
||||
// Update configuration
|
||||
const configuration = this.configurationService.getValue<IFilesConfiguration>();
|
||||
this.onConfigurationUpdated(configuration);
|
||||
|
@ -340,6 +311,25 @@ export class ExplorerView extends ViewPane {
|
|||
return getContext(this.tree.getFocus(), this.tree.getSelection(), respectMultiSelection, this.renderer);
|
||||
}
|
||||
|
||||
async setEditable(stat: ExplorerItem, isEditing: boolean): Promise<void> {
|
||||
if (isEditing) {
|
||||
if (stat.parent && stat.parent !== this.tree.getInput()) {
|
||||
await this.tree.expand(stat.parent);
|
||||
}
|
||||
} else {
|
||||
DOM.removeClass(this.treeContainer, 'highlight');
|
||||
}
|
||||
|
||||
await this.refresh(false, stat.parent);
|
||||
|
||||
if (isEditing) {
|
||||
DOM.addClass(this.treeContainer, 'highlight');
|
||||
this.tree.reveal(stat);
|
||||
} else {
|
||||
this.tree.domFocus();
|
||||
}
|
||||
}
|
||||
|
||||
private selectActiveFile(deselect?: boolean, reveal = this.autoReveal): void {
|
||||
if (this.autoReveal) {
|
||||
const activeFile = this.getActiveFile();
|
||||
|
@ -445,10 +435,10 @@ export class ExplorerView extends ViewPane {
|
|||
|
||||
this._register(this.tree.onContextMenu(e => this.onContextMenu(e)));
|
||||
|
||||
this._register(this.tree.onDidScroll(e => {
|
||||
this._register(this.tree.onDidScroll(async e => {
|
||||
let editable = this.explorerService.getEditable();
|
||||
if (e.scrollTopChanged && editable && this.tree.getRelativeTop(editable.stat) === null) {
|
||||
editable.data.onFinish('', false);
|
||||
await editable.data.onFinish('', false);
|
||||
}
|
||||
}));
|
||||
|
||||
|
@ -566,19 +556,18 @@ export class ExplorerView extends ViewPane {
|
|||
* Refresh the contents of the explorer to get up to date data from the disk about the file structure.
|
||||
* If the item is passed we refresh only that level of the tree, otherwise we do a full refresh.
|
||||
*/
|
||||
private refresh(recursive: boolean, item?: ExplorerItem): Promise<void> {
|
||||
if (!this.tree || !this.isBodyVisible()) {
|
||||
refresh(recursive: boolean, item?: ExplorerItem): Promise<void> {
|
||||
if (!this.tree || !this.isBodyVisible() || (item && !this.tree.hasNode(item))) {
|
||||
// Tree node doesn't exist yet
|
||||
this.shouldRefresh = true;
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
// Tree node doesn't exist yet
|
||||
if (item && !this.tree.hasNode(item)) {
|
||||
return Promise.resolve(undefined);
|
||||
if (this.explorerService.isEditable(undefined)) {
|
||||
this.tree.domFocus();
|
||||
}
|
||||
|
||||
const toRefresh = item || this.tree.getInput();
|
||||
|
||||
return this.tree.updateChildren(toRefresh, recursive);
|
||||
}
|
||||
|
||||
|
@ -589,7 +578,7 @@ export class ExplorerView extends ViewPane {
|
|||
return DOM.getLargestChildWidth(parentNode, childNodes);
|
||||
}
|
||||
|
||||
private async setTreeInput(): Promise<void> {
|
||||
async setTreeInput(): Promise<void> {
|
||||
if (!this.isBodyVisible()) {
|
||||
this.shouldRefresh = true;
|
||||
return Promise.resolve(undefined);
|
||||
|
@ -657,7 +646,7 @@ export class ExplorerView extends ViewPane {
|
|||
return withNullAsUndefined(toResource(input, { supportSideBySide: SideBySideEditor.MASTER }));
|
||||
}
|
||||
|
||||
private async onSelectResource(resource: URI | undefined, reveal = this.autoReveal, retry = 0): Promise<void> {
|
||||
public async selectResource(resource: URI | undefined, reveal = this.autoReveal, retry = 0): Promise<void> {
|
||||
// do no retry more than once to prevent inifinite loops in cases of inconsistent model
|
||||
if (retry === 2) {
|
||||
return;
|
||||
|
@ -674,10 +663,11 @@ export class ExplorerView extends ViewPane {
|
|||
.sort((first, second) => second.resource.path.length - first.resource.path.length)[0];
|
||||
|
||||
while (item && item.resource.toString() !== resource.toString()) {
|
||||
if (item.isDisposed) {
|
||||
return this.onSelectResource(resource, reveal, retry + 1);
|
||||
try {
|
||||
await this.tree.expand(item);
|
||||
} catch (e) {
|
||||
return this.selectResource(resource, reveal, retry + 1);
|
||||
}
|
||||
await this.tree.expand(item);
|
||||
item = first(values(item.children), i => isEqualOrParent(resource, i.resource, ignoreCase));
|
||||
}
|
||||
|
||||
|
@ -690,10 +680,6 @@ export class ExplorerView extends ViewPane {
|
|||
|
||||
try {
|
||||
if (reveal) {
|
||||
if (item.isDisposed) {
|
||||
return this.onSelectResource(resource, reveal, retry + 1);
|
||||
}
|
||||
|
||||
// Don't scroll to the item if it's already visible
|
||||
if (this.tree.getRelativeTop(item) === null) {
|
||||
this.tree.reveal(item, 0.5);
|
||||
|
@ -703,12 +689,13 @@ export class ExplorerView extends ViewPane {
|
|||
this.tree.setFocus([item]);
|
||||
this.tree.setSelection([item]);
|
||||
} catch (e) {
|
||||
// Element might not be in the tree, silently fail
|
||||
// Element might not be in the tree, try again and silently fail
|
||||
return this.selectResource(resource, reveal, retry + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private onCopyItems(stats: ExplorerItem[], cut: boolean, previousCut: ExplorerItem[] | undefined): void {
|
||||
itemsCopied(stats: ExplorerItem[], cut: boolean, previousCut: ExplorerItem[] | undefined): void {
|
||||
this.fileCopiedContextKey.set(stats.length > 0);
|
||||
this.resourceCutContextKey.set(cut && stats.length > 0);
|
||||
if (previousCut) {
|
||||
|
|
|
@ -78,7 +78,6 @@ export class ExplorerModel implements IDisposable {
|
|||
|
||||
export class ExplorerItem {
|
||||
protected _isDirectoryResolved: boolean;
|
||||
private _isDisposed: boolean;
|
||||
public isError = false;
|
||||
private _isExcluded = false;
|
||||
|
||||
|
@ -93,7 +92,6 @@ export class ExplorerItem {
|
|||
private _unknown = false
|
||||
) {
|
||||
this._isDirectoryResolved = false;
|
||||
this._isDisposed = false;
|
||||
}
|
||||
|
||||
get isExcluded(): boolean {
|
||||
|
@ -111,10 +109,6 @@ export class ExplorerItem {
|
|||
this._isExcluded = value;
|
||||
}
|
||||
|
||||
get isDisposed(): boolean {
|
||||
return this._isDisposed;
|
||||
}
|
||||
|
||||
get isDirectoryResolved(): boolean {
|
||||
return this._isDirectoryResolved;
|
||||
}
|
||||
|
@ -257,10 +251,6 @@ export class ExplorerItem {
|
|||
local.addChild(diskChild);
|
||||
}
|
||||
});
|
||||
|
||||
for (let child of oldLocalChildren.values()) {
|
||||
child._dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -310,20 +300,10 @@ export class ExplorerItem {
|
|||
}
|
||||
|
||||
forgetChildren(): void {
|
||||
for (let c of this.children.values()) {
|
||||
c._dispose();
|
||||
}
|
||||
this.children.clear();
|
||||
this._isDirectoryResolved = false;
|
||||
}
|
||||
|
||||
private _dispose() {
|
||||
this._isDisposed = true;
|
||||
for (let child of this.children.values()) {
|
||||
child._dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private getPlatformAwareName(name: string): string {
|
||||
return this.fileService.hasCapability(this.resource, FileSystemProviderCapabilities.PathCaseSensitive) ? name : name.toLowerCase();
|
||||
}
|
||||
|
|
|
@ -3,13 +3,13 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { IExplorerService, IFilesConfiguration, SortOrder, IContextProvider } from 'vs/workbench/contrib/files/common/files';
|
||||
import { IExplorerService, IFilesConfiguration, SortOrder, IExplorerView } from 'vs/workbench/contrib/files/common/files';
|
||||
import { ExplorerItem, ExplorerModel } from 'vs/workbench/contrib/files/common/explorerModel';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { FileOperationEvent, FileOperation, IFileStat, IFileService, FileChangesEvent, FILES_EXCLUDE_CONFIG, FileChangeType, IResolveFileOptions } from 'vs/platform/files/common/files';
|
||||
import { FileOperationEvent, FileOperation, IFileService, FileChangesEvent, FILES_EXCLUDE_CONFIG, FileChangeType, IResolveFileOptions } from 'vs/platform/files/common/files';
|
||||
import { dirname } from 'vs/base/common/resources';
|
||||
import { memoize } from 'vs/base/common/decorators';
|
||||
import { ResourceGlobMatcher } from 'vs/workbench/common/resources';
|
||||
|
@ -32,16 +32,11 @@ export class ExplorerService implements IExplorerService {
|
|||
|
||||
private static readonly EXPLORER_FILE_CHANGES_REACT_DELAY = 500; // delay in ms to react to file changes to give our internal events a chance to react first
|
||||
|
||||
private readonly _onDidChangeRoots = new Emitter<void>();
|
||||
private readonly _onDidChangeItem = new Emitter<{ item?: ExplorerItem, recursive: boolean }>();
|
||||
private readonly _onDidChangeEditable = new Emitter<ExplorerItem>();
|
||||
private readonly _onDidSelectResource = new Emitter<{ resource?: URI, reveal?: boolean }>();
|
||||
private readonly _onDidCopyItems = new Emitter<{ items: ExplorerItem[], cut: boolean, previouslyCutItems: ExplorerItem[] | undefined }>();
|
||||
private readonly disposables = new DisposableStore();
|
||||
private editable: { stat: ExplorerItem, data: IEditableData } | undefined;
|
||||
private _sortOrder: SortOrder;
|
||||
private cutItems: ExplorerItem[] | undefined;
|
||||
private contextProvider: IContextProvider | undefined;
|
||||
private view: IExplorerView | undefined;
|
||||
private model: ExplorerModel;
|
||||
|
||||
constructor(
|
||||
|
@ -59,7 +54,7 @@ export class ExplorerService implements IExplorerService {
|
|||
this.disposables.add(this.fileService.onDidRunOperation(e => this.onDidRunOperation(e)));
|
||||
this.disposables.add(this.fileService.onDidFilesChange(e => this.onDidFilesChange(e)));
|
||||
this.disposables.add(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(this.configurationService.getValue<IFilesConfiguration>())));
|
||||
this.disposables.add(Event.any<{ scheme: string }>(this.fileService.onDidChangeFileSystemProviderRegistrations, this.fileService.onDidChangeFileSystemProviderCapabilities)(e => {
|
||||
this.disposables.add(Event.any<{ scheme: string }>(this.fileService.onDidChangeFileSystemProviderRegistrations, this.fileService.onDidChangeFileSystemProviderCapabilities)(async e => {
|
||||
let affected = false;
|
||||
this.model.roots.forEach(r => {
|
||||
if (r.resource.scheme === e.scheme) {
|
||||
|
@ -68,50 +63,35 @@ export class ExplorerService implements IExplorerService {
|
|||
}
|
||||
});
|
||||
if (affected) {
|
||||
this._onDidChangeItem.fire({ recursive: true });
|
||||
if (this.view) {
|
||||
await this.view.refresh(true);
|
||||
}
|
||||
}
|
||||
}));
|
||||
this.disposables.add(this.model.onDidChangeRoots(() => {
|
||||
if (this.view) {
|
||||
this.view.setTreeInput();
|
||||
}
|
||||
}));
|
||||
this.disposables.add(this.model.onDidChangeRoots(() => this._onDidChangeRoots.fire()));
|
||||
}
|
||||
|
||||
get roots(): ExplorerItem[] {
|
||||
return this.model.roots;
|
||||
}
|
||||
|
||||
get onDidChangeRoots(): Event<void> {
|
||||
return this._onDidChangeRoots.event;
|
||||
}
|
||||
|
||||
get onDidChangeItem(): Event<{ item?: ExplorerItem, recursive: boolean }> {
|
||||
return this._onDidChangeItem.event;
|
||||
}
|
||||
|
||||
get onDidChangeEditable(): Event<ExplorerItem> {
|
||||
return this._onDidChangeEditable.event;
|
||||
}
|
||||
|
||||
get onDidSelectResource(): Event<{ resource?: URI, reveal?: boolean }> {
|
||||
return this._onDidSelectResource.event;
|
||||
}
|
||||
|
||||
get onDidCopyItems(): Event<{ items: ExplorerItem[], cut: boolean, previouslyCutItems: ExplorerItem[] | undefined }> {
|
||||
return this._onDidCopyItems.event;
|
||||
}
|
||||
|
||||
get sortOrder(): SortOrder {
|
||||
return this._sortOrder;
|
||||
}
|
||||
|
||||
registerContextProvider(contextProvider: IContextProvider): void {
|
||||
this.contextProvider = contextProvider;
|
||||
registerView(contextProvider: IExplorerView): void {
|
||||
this.view = contextProvider;
|
||||
}
|
||||
|
||||
getContext(respectMultiSelection: boolean): ExplorerItem[] {
|
||||
if (!this.contextProvider) {
|
||||
if (!this.view) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return this.contextProvider.getContext(respectMultiSelection);
|
||||
return this.view.getContext(respectMultiSelection);
|
||||
}
|
||||
|
||||
// Memoized locals
|
||||
|
@ -132,13 +112,18 @@ export class ExplorerService implements IExplorerService {
|
|||
return this.model.findClosest(resource);
|
||||
}
|
||||
|
||||
setEditable(stat: ExplorerItem, data: IEditableData | null): void {
|
||||
async setEditable(stat: ExplorerItem, data: IEditableData | null): Promise<void> {
|
||||
if (!this.view) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
this.editable = undefined;
|
||||
} else {
|
||||
this.editable = { stat, data };
|
||||
}
|
||||
this._onDidChangeEditable.fire(stat);
|
||||
const isEditing = this.isEditable(stat);
|
||||
await this.view.setEditable(stat, isEditing);
|
||||
}
|
||||
|
||||
setToCopy(items: ExplorerItem[], cut: boolean): void {
|
||||
|
@ -146,7 +131,7 @@ export class ExplorerService implements IExplorerService {
|
|||
this.cutItems = cut ? items : undefined;
|
||||
this.clipboardService.writeResources(items.map(s => s.resource));
|
||||
|
||||
this._onDidCopyItems.fire({ items, cut, previouslyCutItems });
|
||||
this.view?.itemsCopied(items, cut, previouslyCutItems);
|
||||
}
|
||||
|
||||
isCut(item: ExplorerItem): boolean {
|
||||
|
@ -166,9 +151,12 @@ export class ExplorerService implements IExplorerService {
|
|||
}
|
||||
|
||||
async select(resource: URI, reveal?: boolean): Promise<void> {
|
||||
if (!this.view) {
|
||||
return;
|
||||
}
|
||||
const fileStat = this.findClosest(resource);
|
||||
if (fileStat) {
|
||||
this._onDidSelectResource.fire({ resource: fileStat.resource, reveal });
|
||||
await this.view.selectResource(fileStat.resource, reveal);
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
|
@ -190,31 +178,33 @@ export class ExplorerService implements IExplorerService {
|
|||
// Update Input with disk Stat
|
||||
ExplorerItem.mergeLocalWithDisk(modelStat, root);
|
||||
const item = root.find(resource);
|
||||
this._onDidChangeItem.fire({ item: root, recursive: true });
|
||||
await this.view.refresh(true, root);
|
||||
|
||||
// Select and Reveal
|
||||
this._onDidSelectResource.fire({ resource: item ? item.resource : undefined, reveal });
|
||||
await this.view.selectResource(item ? item.resource : undefined, reveal);
|
||||
} catch (error) {
|
||||
root.isError = true;
|
||||
this._onDidChangeItem.fire({ item: root, recursive: false });
|
||||
await this.view.refresh(false, root);
|
||||
}
|
||||
}
|
||||
|
||||
refresh(): void {
|
||||
async refresh(reveal = true): Promise<void> {
|
||||
this.model.roots.forEach(r => r.forgetChildren());
|
||||
this._onDidChangeItem.fire({ recursive: true });
|
||||
const resource = this.editorService.activeEditor ? this.editorService.activeEditor.resource : undefined;
|
||||
const autoReveal = this.configurationService.getValue<IFilesConfiguration>().explorer.autoReveal;
|
||||
if (this.view) {
|
||||
await this.view.refresh(true);
|
||||
const resource = this.editorService.activeEditor ? this.editorService.activeEditor.resource : undefined;
|
||||
const autoReveal = this.configurationService.getValue<IFilesConfiguration>().explorer.autoReveal;
|
||||
|
||||
if (resource && autoReveal) {
|
||||
// We did a top level refresh, reveal the active file #67118
|
||||
this.select(resource, true);
|
||||
if (reveal && resource && autoReveal) {
|
||||
// We did a top level refresh, reveal the active file #67118
|
||||
this.select(resource, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// File events
|
||||
|
||||
private onDidRunOperation(e: FileOperationEvent): void {
|
||||
private async onDidRunOperation(e: FileOperationEvent): Promise<void> {
|
||||
// Add
|
||||
if (e.isOperation(FileOperation.CREATE) || e.isOperation(FileOperation.COPY)) {
|
||||
const addedElement = e.target;
|
||||
|
@ -224,23 +214,23 @@ export class ExplorerService implements IExplorerService {
|
|||
if (parents.length) {
|
||||
|
||||
// Add the new file to its parent (Model)
|
||||
parents.forEach(p => {
|
||||
parents.forEach(async p => {
|
||||
// We have to check if the parent is resolved #29177
|
||||
const resolveMetadata = this.sortOrder === `modified`;
|
||||
const thenable: Promise<IFileStat | undefined> = p.isDirectoryResolved ? Promise.resolve(undefined) : this.fileService.resolve(p.resource, { resolveMetadata });
|
||||
thenable.then(stat => {
|
||||
if (!p.isDirectoryResolved) {
|
||||
const stat = await this.fileService.resolve(p.resource, { resolveMetadata });
|
||||
if (stat) {
|
||||
const modelStat = ExplorerItem.create(this.fileService, stat, p.parent);
|
||||
ExplorerItem.mergeLocalWithDisk(modelStat, p);
|
||||
}
|
||||
}
|
||||
|
||||
const childElement = ExplorerItem.create(this.fileService, addedElement, p.parent);
|
||||
// Make sure to remove any previous version of the file if any
|
||||
p.removeChild(childElement);
|
||||
p.addChild(childElement);
|
||||
// Refresh the Parent (View)
|
||||
this._onDidChangeItem.fire({ item: p, recursive: false });
|
||||
});
|
||||
const childElement = ExplorerItem.create(this.fileService, addedElement, p.parent);
|
||||
// Make sure to remove any previous version of the file if any
|
||||
p.removeChild(childElement);
|
||||
p.addChild(childElement);
|
||||
// Refresh the Parent (View)
|
||||
await this.view?.refresh(false, p);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -255,10 +245,10 @@ export class ExplorerService implements IExplorerService {
|
|||
// Handle Rename
|
||||
if (oldParentResource.toString() === newParentResource.toString()) {
|
||||
const modelElements = this.model.findAll(oldResource);
|
||||
modelElements.forEach(modelElement => {
|
||||
modelElements.forEach(async modelElement => {
|
||||
// Rename File (Model)
|
||||
modelElement.rename(newElement);
|
||||
this._onDidChangeItem.fire({ item: modelElement.parent, recursive: false });
|
||||
await this.view?.refresh(false, modelElement.parent);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -269,11 +259,11 @@ export class ExplorerService implements IExplorerService {
|
|||
|
||||
if (newParents.length && modelElements.length) {
|
||||
// Move in Model
|
||||
modelElements.forEach((modelElement, index) => {
|
||||
modelElements.forEach(async (modelElement, index) => {
|
||||
const oldParent = modelElement.parent;
|
||||
modelElement.move(newParents[index]);
|
||||
this._onDidChangeItem.fire({ item: oldParent, recursive: false });
|
||||
this._onDidChangeItem.fire({ item: newParents[index], recursive: false });
|
||||
await this.view?.refresh(false, oldParent);
|
||||
await this.view?.refresh(false, newParents[index]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -282,13 +272,13 @@ export class ExplorerService implements IExplorerService {
|
|||
// Delete
|
||||
else if (e.isOperation(FileOperation.DELETE)) {
|
||||
const modelElements = this.model.findAll(e.resource);
|
||||
modelElements.forEach(element => {
|
||||
modelElements.forEach(async element => {
|
||||
if (element.parent) {
|
||||
const parent = element.parent;
|
||||
// Remove Element from Parent (Model)
|
||||
parent.removeChild(element);
|
||||
// Refresh Parent (View)
|
||||
this._onDidChangeItem.fire({ item: parent, recursive: false });
|
||||
await this.view?.refresh(false, parent);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -298,7 +288,7 @@ export class ExplorerService implements IExplorerService {
|
|||
// Check if an explorer refresh is necessary (delayed to give internal events a chance to react first)
|
||||
// Note: there is no guarantee when the internal events are fired vs real ones. Code has to deal with the fact that one might
|
||||
// be fired first over the other or not at all.
|
||||
setTimeout(() => {
|
||||
setTimeout(async () => {
|
||||
// Filter to the ones we care
|
||||
const shouldRefresh = () => {
|
||||
e = this.filterToViewRelevantEvents(e);
|
||||
|
@ -365,8 +355,7 @@ export class ExplorerService implements IExplorerService {
|
|||
};
|
||||
|
||||
if (shouldRefresh()) {
|
||||
this.roots.forEach(r => r.forgetChildren());
|
||||
this._onDidChangeItem.fire({ recursive: true });
|
||||
await this.refresh(false);
|
||||
}
|
||||
}, ExplorerService.EXPLORER_FILE_CHANGES_REACT_DELAY);
|
||||
}
|
||||
|
@ -389,13 +378,13 @@ export class ExplorerService implements IExplorerService {
|
|||
}));
|
||||
}
|
||||
|
||||
private onConfigurationUpdated(configuration: IFilesConfiguration, event?: IConfigurationChangeEvent): void {
|
||||
private async onConfigurationUpdated(configuration: IFilesConfiguration, event?: IConfigurationChangeEvent): Promise<void> {
|
||||
const configSortOrder = configuration?.explorer?.sortOrder || 'default';
|
||||
if (this._sortOrder !== configSortOrder) {
|
||||
const shouldRefresh = this._sortOrder !== undefined;
|
||||
this._sortOrder = configSortOrder;
|
||||
if (shouldRefresh) {
|
||||
this.refresh();
|
||||
await this.refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/con
|
|||
import { ITextModelContentProvider } from 'vs/editor/common/services/resolverService';
|
||||
import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { IModeService, ILanguageSelection } from 'vs/editor/common/services/modeService';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
|
@ -38,20 +37,15 @@ export interface IExplorerService {
|
|||
_serviceBrand: undefined;
|
||||
readonly roots: ExplorerItem[];
|
||||
readonly sortOrder: SortOrder;
|
||||
readonly onDidChangeRoots: Event<void>;
|
||||
readonly onDidChangeItem: Event<{ item?: ExplorerItem, recursive: boolean }>;
|
||||
readonly onDidChangeEditable: Event<ExplorerItem>;
|
||||
readonly onDidSelectResource: Event<{ resource?: URI, reveal?: boolean }>;
|
||||
readonly onDidCopyItems: Event<{ items: ExplorerItem[], cut: boolean, previouslyCutItems: ExplorerItem[] | undefined }>;
|
||||
|
||||
getContext(respectMultiSelection: boolean): ExplorerItem[];
|
||||
setEditable(stat: ExplorerItem, data: IEditableData | null): void;
|
||||
setEditable(stat: ExplorerItem, data: IEditableData | null): Promise<void>;
|
||||
getEditable(): { stat: ExplorerItem, data: IEditableData } | undefined;
|
||||
getEditableData(stat: ExplorerItem): IEditableData | undefined;
|
||||
// If undefined is passed checks if any element is currently being edited.
|
||||
isEditable(stat: ExplorerItem | undefined): boolean;
|
||||
findClosest(resource: URI): ExplorerItem | null;
|
||||
refresh(): void;
|
||||
refresh(): Promise<void>;
|
||||
setToCopy(stats: ExplorerItem[], cut: boolean): void;
|
||||
isCut(stat: ExplorerItem): boolean;
|
||||
|
||||
|
@ -61,11 +55,16 @@ export interface IExplorerService {
|
|||
*/
|
||||
select(resource: URI, reveal?: boolean): Promise<void>;
|
||||
|
||||
registerContextProvider(contextProvider: IContextProvider): void;
|
||||
registerView(contextAndRefreshProvider: IExplorerView): void;
|
||||
}
|
||||
|
||||
export interface IContextProvider {
|
||||
export interface IExplorerView {
|
||||
getContext(respectMultiSelection: boolean): ExplorerItem[];
|
||||
refresh(recursive: boolean, item?: ExplorerItem): Promise<void>;
|
||||
selectResource(resource: URI | undefined, reveal?: boolean): Promise<void>;
|
||||
setTreeInput(): Promise<void>;
|
||||
itemsCopied(tats: ExplorerItem[], cut: boolean, previousCut: ExplorerItem[] | undefined): void;
|
||||
setEditable(stat: ExplorerItem, isEditing: boolean): Promise<void>;
|
||||
}
|
||||
|
||||
export const IExplorerService = createDecorator<IExplorerService>('explorerService');
|
||||
|
|
|
@ -693,7 +693,7 @@ namespace LabelTunnelAction {
|
|||
if (context instanceof TunnelItem) {
|
||||
const remoteExplorerService = accessor.get(IRemoteExplorerService);
|
||||
remoteExplorerService.setEditable(context, {
|
||||
onFinish: (value, success) => {
|
||||
onFinish: async (value, success) => {
|
||||
if (success) {
|
||||
remoteExplorerService.tunnelModel.name(context.remoteHost, context.remotePort, value);
|
||||
}
|
||||
|
@ -752,7 +752,7 @@ namespace ForwardPortAction {
|
|||
remoteExplorerService.forward({ host: arg.remoteHost, port: arg.remotePort }).then(tunnel => error(notificationService, tunnel, arg.remoteHost, arg.remotePort));
|
||||
} else {
|
||||
remoteExplorerService.setEditable(undefined, {
|
||||
onFinish: (value, success) => {
|
||||
onFinish: async (value, success) => {
|
||||
let parsed: { host: string, port: number } | undefined;
|
||||
if (success && (parsed = parseInput(value))) {
|
||||
remoteExplorerService.forward({ host: parsed.host, port: parsed.port }).then(tunnel => error(notificationService, tunnel, parsed!.host, parsed!.port));
|
||||
|
|
Loading…
Reference in a new issue