Skip to content

Commit

Permalink
Merge pull request #6304 from ian-r-rose/carriage-return-perf
Browse files Browse the repository at this point in the history
Carriage return perf
  • Loading branch information
jasongrout committed May 4, 2019
2 parents 84d6eca + e7774e9 commit 88e20b0
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 100 deletions.
6 changes: 1 addition & 5 deletions packages/cells/src/model.ts
Expand Up @@ -469,11 +469,7 @@ export class CodeCellModel extends CellModel implements ICodeCellModel {
}
executionCount.changed.connect(this._onExecutionCountChanged, this);

this._outputs = factory.createOutputArea({
trusted,
values: outputs,
modelDB: this.modelDB
});
this._outputs = factory.createOutputArea({ trusted, values: outputs });
this._outputs.changed.connect(this.onGenericChange, this);

// We keep `collapsed` and `jupyter.outputs_hidden` metadata in sync, since
Expand Down
156 changes: 61 additions & 95 deletions packages/outputarea/src/model.ts
Expand Up @@ -9,13 +9,7 @@ import { ISignal, Signal } from '@phosphor/signaling';

import { nbformat } from '@jupyterlab/coreutils';

import {
IObservableList,
ObservableList,
IObservableValue,
ObservableValue,
IModelDB
} from '@jupyterlab/observables';
import { IObservableList, ObservableList } from '@jupyterlab/observables';

import { IOutputModel, OutputModel } from '@jupyterlab/rendermime';

Expand Down Expand Up @@ -112,11 +106,6 @@ export namespace IOutputAreaModel {
* If not given, a default factory will be used.
*/
contentFactory?: IContentFactory;

/**
* An optional IModelDB to store the output area model.
*/
modelDB?: IModelDB;
}

/**
Expand Down Expand Up @@ -153,19 +142,6 @@ export class OutputAreaModel implements IOutputAreaModel {
});
}
this.list.changed.connect(this._onListChanged, this);

// If we are given a IModelDB, keep an up-to-date
// serialized copy of the OutputAreaModel in it.
if (options.modelDB) {
this._modelDB = options.modelDB;
this._serialized = this._modelDB.createValue('outputs');
if (this._serialized.get()) {
this.fromJSON(this._serialized.get() as nbformat.IOutput[]);
} else {
this._serialized.set(this.toJSON());
}
this._serialized.changed.connect(this._onSerializedChanged, this);
}
}

/**
Expand Down Expand Up @@ -252,7 +228,7 @@ export class OutputAreaModel implements IOutputAreaModel {
*/
set(index: number, value: nbformat.IOutput): void {
// Normalize stream data.
this._normalize(value);
Private.normalize(value);
let item = this._createItem({ value, trusted: this._trusted });
this.list.set(index, item);
}
Expand Down Expand Up @@ -318,7 +294,7 @@ export class OutputAreaModel implements IOutputAreaModel {
let trusted = this._trusted;

// Normalize the value.
this._normalize(value);
Private.normalize(value);

// Consolidate outputs if they are stream outputs of the same kind.
if (
Expand All @@ -330,8 +306,8 @@ export class OutputAreaModel implements IOutputAreaModel {
// text to the current item and replace the previous item.
// This also replaces the metadata of the last item.
this._lastStream += value.text as string;
this._lastStream = Private.removeOverwrittenChars(this._lastStream);
value.text = this._lastStream;
this._removeOverwrittenChars(value);
let item = this._createItem({ value, trusted });
let index = this.length - 1;
let prev = this.list.get(index);
Expand All @@ -341,7 +317,7 @@ export class OutputAreaModel implements IOutputAreaModel {
}

if (nbformat.isStream(value)) {
this._removeOverwrittenChars(value);
value.text = Private.removeOverwrittenChars(value.text as string);
}

// Create the new item.
Expand All @@ -360,53 +336,15 @@ export class OutputAreaModel implements IOutputAreaModel {
}

/**
* Normalize an output.
*/
private _normalize(value: nbformat.IOutput): void {
if (nbformat.isStream(value)) {
if (Array.isArray(value.text)) {
value.text = (value.text as string[]).join('\n');
}
}
}

/**
* Remove characters that are overridden by backspace characters.
* A flag that is set when we want to clear the output area
* *after* the next addition to it.
*/
private _fixBackspace(txt: string): string {
let tmp = txt;
do {
txt = tmp;
// Cancel out anything-but-newline followed by backspace
tmp = txt.replace(/[^\n]\x08/gm, '');
} while (tmp.length < txt.length);
return txt;
}
protected clearNext = false;

/**
* Remove chunks that should be overridden by the effect of
* carriage return characters.
*/
private _fixCarriageReturn(txt: string): string {
txt = txt.replace(/\r+\n/gm, '\n'); // \r followed by \n --> newline
while (txt.search(/\r[^$]/g) > -1) {
const base = txt.match(/^(.*)\r+/m)[1];
let insert = txt.match(/\r+(.*)$/m)[1];
insert = insert + base.slice(insert.length, base.length);
txt = txt.replace(/\r+.*$/m, '\r').replace(/^.*\r/m, insert);
}
return txt;
}

/*
* Remove characters overridden by backspaces and carriage returns
* An observable list containing the output models
* for this output area.
*/
private _removeOverwrittenChars(value: nbformat.IOutput): void {
let tmp = value.text as string;
value.text = this._fixCarriageReturn(this._fixBackspace(tmp));
}

protected clearNext = false;
protected list: IObservableList<IOutputModel> = null;

/**
Expand All @@ -426,29 +364,9 @@ export class OutputAreaModel implements IOutputAreaModel {
sender: IObservableList<IOutputModel>,
args: IObservableList.IChangedArgs<IOutputModel>
) {
if (this._serialized && !this._changeGuard) {
this._changeGuard = true;
this._serialized.set(this.toJSON());
this._changeGuard = false;
}
this._changed.emit(args);
}

/**
* If the serialized version of the outputs have changed due to a remote
* action, then update the model accordingly.
*/
private _onSerializedChanged(
sender: IObservableValue,
args: ObservableValue.IChangedArgs
) {
if (!this._changeGuard) {
this._changeGuard = true;
this.fromJSON(args.newValue as nbformat.IOutput[]);
this._changeGuard = false;
}
}

/**
* Handle a change to an item.
*/
Expand All @@ -462,9 +380,6 @@ export class OutputAreaModel implements IOutputAreaModel {
private _isDisposed = false;
private _stateChanged = new Signal<IOutputAreaModel, void>(this);
private _changed = new Signal<this, IOutputAreaModel.ChangedArgs>(this);
private _modelDB: IModelDB = null;
private _serialized: IObservableValue = null;
private _changeGuard = false;
}

/**
Expand All @@ -488,3 +403,54 @@ export namespace OutputAreaModel {
*/
export const defaultContentFactory = new ContentFactory();
}

/**
* A namespace for module-private functionality.
*/
namespace Private {
/**
* Normalize an output.
*/
export function normalize(value: nbformat.IOutput): void {
if (nbformat.isStream(value)) {
if (Array.isArray(value.text)) {
value.text = (value.text as string[]).join('\n');
}
}
}

/**
* Remove characters that are overridden by backspace characters.
*/
function fixBackspace(txt: string): string {
let tmp = txt;
do {
txt = tmp;
// Cancel out anything-but-newline followed by backspace
tmp = txt.replace(/[^\n]\x08/gm, '');
} while (tmp.length < txt.length);
return txt;
}

/**
* Remove chunks that should be overridden by the effect of
* carriage return characters.
*/
function fixCarriageReturn(txt: string): string {
txt = txt.replace(/\r+\n/gm, '\n'); // \r followed by \n --> newline
while (txt.search(/\r[^$]/g) > -1) {
const base = txt.match(/^(.*)\r+/m)[1];
let insert = txt.match(/\r+(.*)$/m)[1];
insert = insert + base.slice(insert.length, base.length);
txt = txt.replace(/\r+.*$/m, '\r').replace(/^.*\r/m, insert);
}
return txt;
}

/*
* Remove characters overridden by backspaces and carriage returns
*/
export function removeOverwrittenChars(text: string): string {
return fixCarriageReturn(fixBackspace(text));
}
}

0 comments on commit 88e20b0

Please sign in to comment.