Skip to content

Commit

Permalink
Backport PR jupyterlab#11525: Sync dirty property between clients
Browse files Browse the repository at this point in the history
  • Loading branch information
hbcarlos authored and meeseeksmachine committed Nov 30, 2021
1 parent 471c93c commit 2821b43
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 28 deletions.
3 changes: 1 addition & 2 deletions packages/docregistry/src/context.ts
Expand Up @@ -676,7 +676,6 @@ export class Context<
if (this.isDisposed) {
return;
}
const dirty = false;
if (contents.format === 'json') {
model.fromJSON(contents.content);
if (initializeModel) {
Expand All @@ -701,7 +700,7 @@ export class Context<
}
}
this._updateContentsModel(contents);
model.dirty = dirty;
model.dirty = false;
if (!this._isPopulated) {
return this._populate();
}
Expand Down
30 changes: 23 additions & 7 deletions packages/docregistry/src/default.ts
Expand Up @@ -29,6 +29,9 @@ export class DocumentModel
const filemodel = new models.YFile() as models.ISharedFile;
this.switchSharedModel(filemodel, true);
this.value.changed.connect(this.triggerContentChange, this);

(this.sharedModel as models.YFile).dirty = false;
this.sharedModel.changed.connect(this._onStateChanged, this);
}

/**
Expand All @@ -49,15 +52,13 @@ export class DocumentModel
* The dirty state of the document.
*/
get dirty(): boolean {
return this._dirty;
return this.sharedModel.dirty;
}
set dirty(newValue: boolean) {
if (newValue === this._dirty) {
if (newValue === this.dirty) {
return;
}
const oldValue = this._dirty;
this._dirty = newValue;
this.triggerStateChange({ name: 'dirty', oldValue, newValue });
(this.sharedModel as models.YFile).dirty = newValue;
}

/**
Expand Down Expand Up @@ -151,12 +152,24 @@ export class DocumentModel
this.dirty = true;
}

private _onStateChanged(
sender: models.ISharedFile,
changes: models.NotebookChange
): void {
if (changes.stateChange) {
changes.stateChange.forEach(value => {
if (value.name !== 'dirty' || value.oldValue !== value.newValue) {
this.triggerStateChange(value);
}
});
}
}

/**
* The shared notebook model.
*/
readonly sharedModel: models.ISharedFile;
private _defaultLang = '';
private _dirty = false;
private _readOnly = false;
private _contentChanged = new Signal<this, void>(this);
private _stateChanged = new Signal<this, IChangedArgs<any>>(this);
Expand Down Expand Up @@ -547,7 +560,10 @@ export class DocumentWidget<
* Handle the dirty state of the context model.
*/
private _handleDirtyState(): void {
if (this.context.model.dirty) {
if (
this.context.model.dirty &&
!this.title.className.includes(DIRTY_CLASS)
) {
this.title.className += ` ${DIRTY_CLASS}`;
} else {
this.title.className = this.title.className.replace(DIRTY_CLASS, '');
Expand Down
8 changes: 8 additions & 0 deletions packages/docregistry/test/default.spec.ts
Expand Up @@ -581,6 +581,14 @@ describe('docregistry/default', () => {
expect(widget.title.className).toContain('jp-mod-dirty');
});

it('should remove the dirty class', () => {
context.model.dirty = true;
context.model.dirty = true;
expect(widget.title.className).toContain('jp-mod-dirty');
context.model.dirty = false;
expect(widget.title.className).not.toContain('jp-mod-dirty');
});

it('should store the context', () => {
expect(widget.context).toBe(context);
});
Expand Down
15 changes: 6 additions & 9 deletions packages/notebook/src/model.ts
Expand Up @@ -114,6 +114,7 @@ export class NotebookModel implements INotebookModel {
metadata.changed.connect(this._onMetadataChanged, this);
this._deletedCells = [];

(this.sharedModel as models.YNotebook).dirty = false;
this.sharedModel.changed.connect(this._onStateChanged, this);
}
/**
Expand All @@ -134,15 +135,13 @@ export class NotebookModel implements INotebookModel {
* The dirty state of the document.
*/
get dirty(): boolean {
return this._dirty;
return this.sharedModel.dirty;
}
set dirty(newValue: boolean) {
if (newValue === this._dirty) {
if (newValue === this.dirty) {
return;
}
const oldValue = this._dirty;
this._dirty = newValue;
this.triggerStateChange({ name: 'dirty', oldValue, newValue });
(this.sharedModel as models.YNotebook).dirty = newValue;
}

/**
Expand Down Expand Up @@ -428,10 +427,9 @@ close the notebook without saving it.`,
if (value.name === 'nbformatMinor') {
this._nbformatMinor = value.newValue;
}
if (value.name === 'dirty') {
this._dirty = value.newValue;
if (value.name !== 'dirty' || value.oldValue !== value.newValue) {
this.triggerStateChange(value);
}
this.triggerStateChange(value);
});
}

Expand Down Expand Up @@ -513,7 +511,6 @@ close the notebook without saving it.`,
*/
readonly modelDB: IModelDB;

private _dirty = false;
private _readOnly = false;
private _contentChanged = new Signal<this, void>(this);
private _stateChanged = new Signal<this, IChangedArgs<any>>(this);
Expand Down
5 changes: 5 additions & 0 deletions packages/shared-models/src/api.ts
Expand Up @@ -58,6 +58,11 @@ export interface ISharedBase extends IDisposable {
* This is used by, for example, docregistry to share the file-path of the edited content.
*/
export interface ISharedDocument extends ISharedBase {
/**
* Whether the document is saved to disk or not.
*/
readonly dirty: boolean;

/**
* The changed signal.
*/
Expand Down
34 changes: 24 additions & 10 deletions packages/shared-models/src/ymodels.ts
Expand Up @@ -26,6 +26,16 @@ export interface IYText extends models.ISharedText {
export type YCellType = YRawCell | YCodeCell | YMarkdownCell;

export class YDocument<T> implements models.ISharedDocument {
get dirty(): boolean {
return this.ystate.get('dirty');
}

set dirty(value: boolean) {
this.transact(() => {
this.ystate.set('dirty', value);
}, false);
}

/**
* Perform a transaction. While the function f is called, all changes to the shared
* document are bundled into a single event.
Expand Down Expand Up @@ -128,11 +138,13 @@ export class YFile

event.keysChanged.forEach(key => {
const change = event.changes.keys.get(key);
stateChange.push({
name: key,
oldValue: change?.oldValue ? change!.oldValue : 0,
newValue: this.ystate.get(key)
});
if (change) {
stateChange.push({
name: key,
oldValue: change.oldValue,
newValue: this.ystate.get(key)
});
}
});

this._changed.emit({ stateChange });
Expand Down Expand Up @@ -444,11 +456,13 @@ export class YNotebook
const stateChange: any = [];
event.keysChanged.forEach(key => {
const change = event.changes.keys.get(key);
stateChange.push({
name: key,
oldValue: change?.oldValue ? change!.oldValue : 0,
newValue: this.ystate.get(key)
});
if (change) {
stateChange.push({
name: key,
oldValue: change.oldValue,
newValue: this.ystate.get(key)
});
}
});

this._changed.emit({ stateChange });
Expand Down

0 comments on commit 2821b43

Please sign in to comment.