Skip to content

Commit

Permalink
馃┕ Fixed autosaving on cell focus change
Browse files Browse the repository at this point in the history
What started as a bug now is a feature to activate via the setting 'saveOnCellFocusChange'.
  • Loading branch information
s-weigand committed May 4, 2021
1 parent 11d8a7e commit 488c600
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 17 deletions.
6 changes: 6 additions & 0 deletions schema/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@
"title": "Files to exclude from save on focus change.",
"description": "List of glob patterns to exclude.\nThe root will be relative to where you started jupyterlab from",
"default": []
},
"saveOnCellFocusChange": {
"type": "boolean",
"title": "Autosave notebook on cell focus change",
"description": "Whether to autosave notebook on cell focus change.",
"default": false
}
}
}
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ const extension: JupyterFrontEndPlugin<void> = {
docManager,
notebookTracker,
editorTracker,
// debug: true,
debug: true,
});

const settings = new FocusChangeAutoSaveSettings({
Expand Down
42 changes: 35 additions & 7 deletions src/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,18 @@ import { FocusChangeAutoSaveTracker } from './tracker';
import { create_debug_printer } from './utils';
import { PLUGIN_ID, TOGGLE_ACTIVE_COMMAND_ID } from './consts';

/**
* Settings used by FocusChangeAutoSaveTracker.updateSettings
*/
export interface IFocusChangeAutoSaveSettings {
/** Whether or not the extension is activated. */
active: boolean;
/** Glob patterns to exclude files from autosaving. */
exclude: string[];
/** Whether or not to save when cell focus changed. */
saveOnCellFocusChange: boolean;
}

/**
* Arguments to initialize FocusSaveTracker.
*/
Expand Down Expand Up @@ -52,19 +64,35 @@ export class FocusChangeAutoSaveSettings {
}

/**
* Callback for changed settings
* Parse setting and extract values.
*
* @param setting loaded setting instance
* @param setting Read settings
* @returns Parsed settings to be used with FocusChangeAutoSaveTracker.updateSettings
*/
loadSetting(setting: ISettingRegistry.ISettings): void {
parseSetting(
setting: ISettingRegistry.ISettings,
): IFocusChangeAutoSaveSettings {
// Read the settings and convert to the correct type
this._active = setting.get('active').composite as boolean;
const exclude = setting.get('exclude').composite as string[];
this._saveTracker.updateSettings(this._active, exclude);
const saveOnCellFocusChange = setting.get('saveOnCellFocusChange')
.composite as boolean;
return { active: this._active, exclude, saveOnCellFocusChange };
}

this._debug_printer('FocusChangeAutoSaveSettings.loadSetting:', {
active: this._active,
});
/**
* Callback for changed settings
*
* @param setting loaded setting instance
*/
loadSetting(setting: ISettingRegistry.ISettings): void {
const trackerSettings = this.parseSetting(setting);
this._saveTracker.updateSettings(trackerSettings);

this._debug_printer(
'FocusChangeAutoSaveSettings.loadSetting:',
trackerSettings,
);
}

/**
Expand Down
59 changes: 50 additions & 9 deletions src/tracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { toArray } from '@lumino/algorithm';
import { Minimatch, IMinimatch } from 'minimatch';

import { debug_printer, create_debug_printer } from './utils';
import { IFocusChangeAutoSaveSettings } from './settings';

/**
* Arguments to initialize FocusSaveTracker.
Expand Down Expand Up @@ -47,6 +48,8 @@ export class FocusChangeAutoSaveTracker {
private _nodes = new Map<HTMLElement, Widget>();
/** Glob pattern matcher to check if a document is excluded. */
private _excludeMatcher: IMinimatch;
/** Save cells of focus change auto save tracker */
private _saveOnCellFocusChange: boolean;
private _debug_printer: (...args: any[]) => void;

/**
Expand Down Expand Up @@ -127,13 +130,46 @@ export class FocusChangeAutoSaveTracker {
* See: https://stackoverflow.com/a/58149336/3990615
*/
handleEvent(event: Event): void {
const widget = this._nodes.get(event.currentTarget as HTMLElement);
let widget: Widget | undefined;
switch (event.type) {
case 'focusout':
this.saveDocumentWidget(widget);
widget = this.getWidgetFromEvent(event as FocusEvent);
this._debug_printer('FocusEvent: ', event);
if (widget !== undefined) {
this.saveDocumentWidget(widget);
}
break;
}
}

/**
* Get Widget depending what triggered the event.
* This allows filtering of bubbled up focusout events from changing
* the focussed cell.
*
* @param event Focus event triggered by an editor or child widget
* @returns Document Widget or undefined
*/
getWidgetFromEvent(event: FocusEvent): Widget | undefined {
if (this._saveOnCellFocusChange !== true) {
const relatedTarget = event.relatedTarget as HTMLElement;
const target = event.target as HTMLElement;
if (
relatedTarget === null ||
relatedTarget.contains(target) || // cell is in focused widget
relatedTarget.nodeName !== 'DIV' // newly created cell (TEXTAREA)
) {
return undefined;
}
this._debug_printer(
'target in relatedTarget: ',
relatedTarget.contains(target),
);
}
const targetNode = event.currentTarget as HTMLElement;
return this._nodes.get(targetNode);
}

/**
* Save Widget if it is a document Widget.
*
Expand All @@ -157,19 +193,24 @@ export class FocusChangeAutoSaveTracker {
}

/**
* Activate or deactivate the tracking.
* Activate or deactivate the tracking, with new settings.
*
* @param active Setting if the Extension is active or not.
*/
updateSettings(active: boolean, exclude: string[]): void {
this._excludeMatcher = new Minimatch(`{${exclude.join(',')},}`, {
nocomment: true,
});
// updateSettings(active: boolean, exclude: string[]): void {
updateSettings(trackerSetting: IFocusChangeAutoSaveSettings): void {
this._excludeMatcher = new Minimatch(
`{${trackerSetting.exclude.join(',')},}`,
{
nocomment: true,
},
);
this._saveOnCellFocusChange = trackerSetting.saveOnCellFocusChange;
this._debug_printer('_excludeMatcher: ', this._excludeMatcher);
debug_printer(true, 'Setting active state to: ', active);
debug_printer(true, 'Setting active state to: ', trackerSetting.active);
this.saveAllDocumentWidgets();

if (active === true) {
if (trackerSetting.active === true) {
this.trackWidgets();
this._notebookTracker.widgetAdded.connect(this.trackWidgets, this);
this._editorTracker.widgetAdded.connect(this.trackWidgets, this);
Expand Down

0 comments on commit 488c600

Please sign in to comment.