UNPKG

@theia/filesystem

Version:
372 lines • 16.9 kB
"use strict"; // ***************************************************************************** // Copyright (C) 2018 TypeFox and others. // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License v. 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0. // // This Source Code may also be made available under the following Secondary // Licenses when the conditions for such availability set forth in the Eclipse // Public License v. 2.0 are satisfied: GNU General Public License, version 2 // with the GNU Classpath Exception which is available at // https://www.gnu.org/software/classpath/license.html. // // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** Object.defineProperty(exports, "__esModule", { value: true }); exports.FileSystemFrontendContribution = exports.FileSystemCommands = void 0; const tslib_1 = require("tslib"); const core_1 = require("@theia/core"); const browser_1 = require("@theia/core/lib/browser"); const mime_service_1 = require("@theia/core/lib/browser/mime-service"); const tree_widget_selection_1 = require("@theia/core/lib/browser/tree/tree-widget-selection"); const common_1 = require("@theia/core/lib/common"); const command_1 = require("@theia/core/lib/common/command"); const promise_util_1 = require("@theia/core/lib/common/promise-util"); const uri_1 = require("@theia/core/lib/common/uri"); const environment_1 = require("@theia/core/shared/@theia/application-package/lib/environment"); const inversify_1 = require("@theia/core/shared/inversify"); const user_working_directory_provider_1 = require("@theia/core/lib/browser/user-working-directory-provider"); const file_dialog_1 = require("./file-dialog"); const file_selection_1 = require("./file-selection"); const file_service_1 = require("./file-service"); const filesystem_preferences_1 = require("../common/filesystem-preferences"); const file_upload_1 = require("../common/upload/file-upload"); var FileSystemCommands; (function (FileSystemCommands) { FileSystemCommands.UPLOAD = command_1.Command.toLocalizedCommand({ id: 'file.upload', category: browser_1.CommonCommands.FILE_CATEGORY, label: 'Upload Files...' }, 'theia/filesystem/uploadFiles', browser_1.CommonCommands.FILE_CATEGORY_KEY); })(FileSystemCommands || (exports.FileSystemCommands = FileSystemCommands = {})); let FileSystemFrontendContribution = class FileSystemFrontendContribution { constructor() { this.onDidChangeEditorFileEmitter = new common_1.Emitter(); this.onDidChangeEditorFile = this.onDidChangeEditorFileEmitter.event; this.userOperations = new Map(); this.pendingOperation = Promise.resolve(); this.moveSnapshots = new Map(); this.deletedSuffix = `(${core_1.nls.localizeByDefault('Deleted')})`; } queueUserOperation(event) { const moveOperation = new promise_util_1.Deferred(); this.userOperations.set(event.correlationId, moveOperation); this.run(() => moveOperation.promise); } resolveUserOperation(event) { const operation = this.userOperations.get(event.correlationId); if (operation) { this.userOperations.delete(event.correlationId); operation.resolve(); } } initialize() { this.fileService.onDidFilesChange(event => this.run(() => this.updateWidgets(event))); this.fileService.onWillRunUserOperation(event => { this.queueUserOperation(event); event.waitUntil(this.runEach((uri, widget) => this.pushMove(uri, widget, event))); }); this.fileService.onDidFailUserOperation(event => event.waitUntil((async () => { await this.runEach((uri, widget) => this.revertMove(uri, widget, event)); this.resolveUserOperation(event); })())); this.fileService.onDidRunUserOperation(event => event.waitUntil((async () => { await this.runEach((uri, widget) => this.applyMove(uri, widget, event)); this.resolveUserOperation(event); })())); this.uploadService.onDidUpload(files => { this.doHandleUpload(files); }); } onStart(app) { this.updateAssociations(); this.preferences.onPreferenceChanged(e => { if (e.preferenceName === 'files.associations') { this.updateAssociations(); } }); } registerCommands(commands) { commands.registerCommand(FileSystemCommands.UPLOAD, { isEnabled: (...args) => { const selection = this.getSelection(...args); return !!selection && !environment_1.environment.electron.is(); }, isVisible: () => !environment_1.environment.electron.is(), execute: (...args) => { const selection = this.getSelection(...args); if (selection) { return this.upload(selection); } } }); commands.registerCommand(browser_1.CommonCommands.NEW_FILE, { execute: (...args) => { this.handleNewFileCommand(args); } }); } async upload(selection) { try { const source = tree_widget_selection_1.TreeWidgetSelection.getSource(this.selectionService.selection); const fileUploadResult = await this.uploadService.upload(selection.fileStat.isDirectory ? selection.fileStat.resource : selection.fileStat.resource.parent); if (browser_1.ExpandableTreeNode.is(selection) && source) { await source.model.expandNode(selection); } return fileUploadResult; } catch (e) { if (!(0, common_1.isCancelled)(e)) { console.error(e); } } } async doHandleUpload(uploads) { // Only handle single file uploads if (uploads.length === 1) { const uri = new uri_1.default(uploads[0]); // Close all existing widgets for this URI const widgets = this.shell.widgets.filter(widget => { var _a; return (_a = browser_1.NavigatableWidget.getUri(widget)) === null || _a === void 0 ? void 0 : _a.isEqual(uri); }); await this.shell.closeMany(widgets, { // Don't ask to save the file if it's dirty // The user has already confirmed the file overwrite save: false }); // Open a new editor for this URI (0, browser_1.open)(this.openerService, uri); } } /** * Opens a save dialog to create a new file. * * @param args The first argument is the name of the new file. The second argument is the parent directory URI. */ async handleNewFileCommand(args) { const fileName = (args !== undefined && typeof args[0] === 'string') ? args[0] : undefined; const title = core_1.nls.localizeByDefault('Create File'); const props = { title, saveLabel: title, inputValue: fileName }; const dirUri = (args[1] instanceof uri_1.default) ? args[1] : await this.workingDirectory.getUserWorkingDir(); const directory = await this.fileService.resolve(dirUri); const filePath = await this.fileDialogService.showSaveDialog(props, directory.isDirectory ? directory : undefined); if (filePath) { const file = await this.fileService.createFile(filePath); (0, browser_1.open)(this.openerService, file.resource); } } getSelection(...args) { var _a; const { selection } = this.selectionService; return (_a = this.toSelection(args[0])) !== null && _a !== void 0 ? _a : (Array.isArray(selection) ? selection.find(file_selection_1.FileSelection.is) : this.toSelection(selection)); } ; toSelection(arg) { return file_selection_1.FileSelection.is(arg) ? arg : undefined; } run(operation) { return this.pendingOperation = this.pendingOperation.then(async () => { try { await operation(); } catch (e) { console.error(e); } }); } async runEach(participant) { const promises = []; for (const [resourceUri, widget] of browser_1.NavigatableWidget.get(this.shell.widgets)) { promises.push(participant(resourceUri, widget)); } await Promise.all(promises); } popMoveSnapshot(resourceUri) { const snapshotKey = resourceUri.toString(); const snapshot = this.moveSnapshots.get(snapshotKey); if (snapshot) { this.moveSnapshots.delete(snapshotKey); } return snapshot; } applyMoveSnapshot(widget, snapshot) { if (!snapshot) { return undefined; } if (snapshot.dirty) { const saveable = browser_1.Saveable.get(widget); if (saveable && saveable.applySnapshot) { saveable.applySnapshot(snapshot.dirty); } } if (snapshot.view && browser_1.StatefulWidget.is(widget)) { widget.restoreState(snapshot.view); } } async pushMove(resourceUri, widget, event) { const newResourceUri = this.createMoveToUri(resourceUri, widget, event); if (!newResourceUri) { return; } const snapshot = {}; const saveable = browser_1.Saveable.get(widget); if (browser_1.StatefulWidget.is(widget)) { snapshot.view = widget.storeState(); } if (saveable && saveable.dirty) { if (saveable.createSnapshot) { snapshot.dirty = saveable.createSnapshot(); } if (saveable.revert) { await saveable.revert({ soft: true }); } } this.moveSnapshots.set(newResourceUri.toString(), snapshot); } async revertMove(resourceUri, widget, event) { const newResourceUri = this.createMoveToUri(resourceUri, widget, event); if (!newResourceUri) { return; } const snapshot = this.popMoveSnapshot(newResourceUri); this.applyMoveSnapshot(widget, snapshot); } async applyMove(resourceUri, widget, event) { const newResourceUri = this.createMoveToUri(resourceUri, widget, event); if (!newResourceUri) { return; } const snapshot = this.popMoveSnapshot(newResourceUri); const description = this.widgetManager.getDescription(widget); if (!description) { return; } const { factoryId, options } = description; if (!browser_1.NavigatableWidgetOptions.is(options)) { return; } const newWidget = await this.widgetManager.getOrCreateWidget(factoryId, { ...options, uri: newResourceUri.toString() }); this.applyMoveSnapshot(newWidget, snapshot); const area = this.shell.getAreaFor(widget) || 'main'; // eslint-disable-next-line @typescript-eslint/no-explicit-any const pending = [this.shell.addWidget(newWidget, { area, ref: widget })]; if (this.shell.activeWidget === widget) { pending.push(this.shell.activateWidget(newWidget.id)); } else if (widget.isVisible) { pending.push(this.shell.revealWidget(newWidget.id)); } pending.push(this.shell.closeWidget(widget.id, { save: false })); await Promise.all(pending); } createMoveToUri(resourceUri, widget, event) { var _a; if (event.operation !== 2 /* FileOperation.MOVE */) { return undefined; } const path = (_a = event.source) === null || _a === void 0 ? void 0 : _a.relative(resourceUri); const targetUri = path && event.target.resolve(path); return targetUri && widget.createMoveToUri(targetUri); } async updateWidgets(event) { if (!event.gotDeleted() && !event.gotAdded()) { return; } const dirty = new Set(); const toClose = new Map(); for (const [uri, widget] of browser_1.NavigatableWidget.get(this.shell.widgets)) { this.updateWidget(uri, widget, event, { dirty, toClose: toClose }); } if (this.corePreferences['workbench.editor.closeOnFileDelete']) { const doClose = []; for (const [uri, widgets] of toClose.entries()) { if (!dirty.has(uri)) { doClose.push(...widgets); } } await this.shell.closeMany(doClose); } } updateWidget(uri, widget, event, { dirty, toClose }) { const label = widget.title.label; const deleted = label.endsWith(this.deletedSuffix); if (event.contains(uri, 2 /* FileChangeType.DELETED */)) { const uriString = uri.toString(); if (browser_1.Saveable.isDirty(widget)) { dirty.add(uriString); } if (!deleted) { widget.title.label += this.deletedSuffix; this.onDidChangeEditorFileEmitter.fire({ editor: widget, type: 2 /* FileChangeType.DELETED */ }); } const widgets = toClose.get(uriString) || []; widgets.push(widget); toClose.set(uriString, widgets); } else if (event.contains(uri, 1 /* FileChangeType.ADDED */)) { if (deleted) { widget.title.label = widget.title.label.substring(0, label.length - this.deletedSuffix.length); this.onDidChangeEditorFileEmitter.fire({ editor: widget, type: 1 /* FileChangeType.ADDED */ }); } } } updateAssociations() { const fileAssociations = this.preferences['files.associations']; const mimeAssociations = Object.keys(fileAssociations).map(filepattern => ({ id: fileAssociations[filepattern], filepattern })); this.mimeService.setAssociations(mimeAssociations); } }; exports.FileSystemFrontendContribution = FileSystemFrontendContribution; tslib_1.__decorate([ (0, inversify_1.inject)(browser_1.ApplicationShell), tslib_1.__metadata("design:type", browser_1.ApplicationShell) ], FileSystemFrontendContribution.prototype, "shell", void 0); tslib_1.__decorate([ (0, inversify_1.inject)(browser_1.WidgetManager), tslib_1.__metadata("design:type", browser_1.WidgetManager) ], FileSystemFrontendContribution.prototype, "widgetManager", void 0); tslib_1.__decorate([ (0, inversify_1.inject)(mime_service_1.MimeService), tslib_1.__metadata("design:type", mime_service_1.MimeService) ], FileSystemFrontendContribution.prototype, "mimeService", void 0); tslib_1.__decorate([ (0, inversify_1.inject)(filesystem_preferences_1.FileSystemPreferences), tslib_1.__metadata("design:type", Object) ], FileSystemFrontendContribution.prototype, "preferences", void 0); tslib_1.__decorate([ (0, inversify_1.inject)(core_1.CorePreferences), tslib_1.__metadata("design:type", Object) ], FileSystemFrontendContribution.prototype, "corePreferences", void 0); tslib_1.__decorate([ (0, inversify_1.inject)(common_1.SelectionService), tslib_1.__metadata("design:type", common_1.SelectionService) ], FileSystemFrontendContribution.prototype, "selectionService", void 0); tslib_1.__decorate([ (0, inversify_1.inject)(file_upload_1.FileUploadService), tslib_1.__metadata("design:type", Object) ], FileSystemFrontendContribution.prototype, "uploadService", void 0); tslib_1.__decorate([ (0, inversify_1.inject)(file_service_1.FileService), tslib_1.__metadata("design:type", file_service_1.FileService) ], FileSystemFrontendContribution.prototype, "fileService", void 0); tslib_1.__decorate([ (0, inversify_1.inject)(file_dialog_1.FileDialogService), tslib_1.__metadata("design:type", Object) ], FileSystemFrontendContribution.prototype, "fileDialogService", void 0); tslib_1.__decorate([ (0, inversify_1.inject)(browser_1.OpenerService), tslib_1.__metadata("design:type", Object) ], FileSystemFrontendContribution.prototype, "openerService", void 0); tslib_1.__decorate([ (0, inversify_1.inject)(user_working_directory_provider_1.UserWorkingDirectoryProvider), tslib_1.__metadata("design:type", user_working_directory_provider_1.UserWorkingDirectoryProvider) ], FileSystemFrontendContribution.prototype, "workingDirectory", void 0); exports.FileSystemFrontendContribution = FileSystemFrontendContribution = tslib_1.__decorate([ (0, inversify_1.injectable)() ], FileSystemFrontendContribution); //# sourceMappingURL=filesystem-frontend-contribution.js.map