Skip to content

Commit

Permalink
preferences: Support 'Close On File Delete' preference
Browse files Browse the repository at this point in the history
+ Add new preference `workbench.editor.closeOnFileDelete` to control
whether the editor should be closed if another process deleted or renamed the file.
+ Change deletedSuffix to ` (deleted)`.

Signed-off-by: Duc Nguyen <[email protected]>
  • Loading branch information
DucNgn committed Nov 18, 2020
1 parent 56c2274 commit e768066
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 8 deletions.
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

0 comments on commit e768066

Please sign in to comment.