Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

preferences: Support 'Close On File Delete' preference #8731

Merged
merged 1 commit into from
Dec 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions examples/api-tests/src/saveable.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ describe('Saveable', function () {

beforeEach(async () => {
await preferences.set('editor.autoSave', 'off', undefined, rootUri.toString());
await preferences.set('editor.closeOnFileDelete', true);
await editorManager.closeAll({ save: false });
await fileService.create(fileUri, 'foo', { fromUserGesture: false, overwrite: true });
widget = /** @type {EditorWidget & SaveableWidget} */
Expand Down Expand Up @@ -227,6 +228,22 @@ describe('Saveable', function () {
assert.equal(state.value, 'foo', 'fs should NOT be updated after rejected close');
});

it('delete file for saved with editor.CloseOnFileDelete off', async () => {
await preferences.set('editor.closeOnFileDelete', false);
assert.isFalse(Saveable.isDirty(widget), 'should NOT be dirty before delete');
assert.isTrue(editor.document.valid, 'should be valid before delete');
const waitForInvalid = new Deferred();
const listener = editor.document.onDidChangeValid(() => waitForInvalid.resolve());
try {
await fileService.delete(fileUri);
await waitForInvalid.promise;
assert.isFalse(editor.document.valid, 'should be INVALID after delete');
assert.isFalse(widget.isDisposed, 'model should NOT be disposed after delete');
} finally {
listener.dispose();
}
});

it('accept save on close and reject it', async () => {
let outOfSync = false;
toTearDown.push(setShouldOverwrite(async () => {
Expand Down Expand Up @@ -284,7 +301,7 @@ describe('Saveable', function () {
try {
await fileService.delete(fileUri);
await waitForDidChangeTitle.promise;
assert.isTrue(widget.title.label.endsWith('(deleted from disk)'), 'should be marked as deleted');
assert.isTrue(widget.title.label.endsWith('(deleted)'), 'should be marked as deleted');
assert.isTrue(Saveable.isDirty(widget), 'should be dirty after delete');
assert.isFalse(widget.isDisposed, 'model should NOT be disposed after delete');
} finally {
Expand All @@ -296,7 +313,7 @@ describe('Saveable', function () {
try {
await fileService.create(fileUri, 'foo');
await waitForDidChangeTitle.promise;
assert.isFalse(widget.title.label.endsWith('(deleted from disk)'), 'should NOT be marked as deleted');
assert.isFalse(widget.title.label.endsWith('(deleted)'), 'should NOT be marked as deleted');
assert.isTrue(Saveable.isDirty(widget), 'should be dirty after added again');
assert.isFalse(widget.isDisposed, 'model should NOT be disposed after added again');
} finally {
Expand Down
7 changes: 7 additions & 0 deletions packages/core/src/browser/core-preferences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ export const corePreferenceSchema: PreferenceSchema = {
'description': 'Controls whether a top border is drawn on modified (dirty) editor tabs or not.',
'default': false
},
'workbench.editor.closeOnFileDelete': {
'type': 'boolean',
// eslint-disable-next-line max-len
'description': 'Controls whether editors showing a file that was opened during the session should close automatically when getting deleted or renamed by some other process. Disabling this will keep the editor open on such an event. Note that deleting from within the application will always close the editor and that dirty files will never close to preserve your data.',
'default': true
},
'application.confirmExit': {
type: 'string',
enum: [
Expand Down Expand Up @@ -100,6 +106,7 @@ export interface CoreConfiguration {
'workbench.list.openMode': 'singleClick' | 'doubleClick';
'workbench.commandPalette.history': number;
'workbench.editor.highlightModifiedTabs': boolean;
'workbench.editor.closeOnFileDelete': boolean;
'workbench.colorTheme': string;
'workbench.iconTheme': string | null;
'workbench.silentNotifications': boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ import { Command, CommandContribution, CommandRegistry } from '@theia/core/lib/c
import {
FrontendApplicationContribution, ApplicationShell,
NavigatableWidget, NavigatableWidgetOptions,
Saveable, WidgetManager, StatefulWidget, FrontendApplication, ExpandableTreeNode, waitForClosed
Saveable, WidgetManager, StatefulWidget, FrontendApplication, ExpandableTreeNode, waitForClosed,
CorePreferences
} from '@theia/core/lib/browser';
import { MimeService } from '@theia/core/lib/browser/mime-service';
import { TreeWidgetSelection } from '@theia/core/lib/browser/tree/tree-widget-selection';
Expand Down Expand Up @@ -63,6 +64,9 @@ export class FileSystemFrontendContribution implements FrontendApplicationContri
@inject(FileSystemPreferences)
protected readonly preferences: FileSystemPreferences;

@inject(CorePreferences)
protected readonly corePreferences: CorePreferences;

@inject(SelectionService)
protected readonly selectionService: SelectionService;

Expand Down Expand Up @@ -258,7 +262,7 @@ export class FileSystemFrontendContribution implements FrontendApplicationContri
return targetUri && widget.createMoveToUri(targetUri);
}

protected readonly deletedSuffix = ' (deleted from disk)';
protected readonly deletedSuffix = ' (deleted)';
protected async updateWidgets(event: FileChangesEvent): Promise<void> {
if (!event.gotDeleted() && !event.gotAdded()) {
return;
Expand All @@ -272,7 +276,7 @@ export class FileSystemFrontendContribution implements FrontendApplicationContri
this.updateWidget(uri, widget, event, { dirty, toClose });
}
for (const [uriString, widgets] of toClose.entries()) {
if (!dirty.has(uriString)) {
if (!dirty.has(uriString) && this.corePreferences['workbench.editor.closeOnFileDelete']) {
for (const widget of widgets) {
widget.close();
pending.push(waitForClosed(widget));
Expand All @@ -291,11 +295,11 @@ export class FileSystemFrontendContribution implements FrontendApplicationContri
if (event.contains(uri, FileChangeType.DELETED)) {
const uriString = uri.toString();
if (Saveable.isDirty(widget)) {
if (!deleted) {
widget.title.label += this.deletedSuffix;
}
dirty.add(uriString);
}
if (!deleted) {
widget.title.label += this.deletedSuffix;
}
const widgets = toClose.get(uriString) || [];
widgets.push(widget);
toClose.set(uriString, widgets);
Expand Down