зеркало из https://github.com/microsoft/gather.git
History: Disable slicing across kernel session
This commit is contained in:
Родитель
9598ec9606
Коммит
e33d788317
|
@ -79,8 +79,7 @@ export interface ILocation {
|
|||
|
||||
export interface ILocatable {
|
||||
location: ILocation;
|
||||
cellPersistentId?: string;
|
||||
executionCount?: number;
|
||||
cellExecutionEventId?: string;
|
||||
}
|
||||
|
||||
export const MODULE = 'module';
|
||||
|
|
|
@ -15,13 +15,12 @@ export class DataflowAnalyzer {
|
|||
}
|
||||
|
||||
private _statementLocationKey(statement: ast.ISyntaxNode) {
|
||||
if (statement.cellPersistentId != undefined && statement.executionCount != undefined) {
|
||||
if (statement.cellExecutionEventId != undefined) {
|
||||
return statement.location.first_line + "," +
|
||||
statement.location.first_column + "," +
|
||||
statement.location.last_line + "," +
|
||||
statement.location.last_column + "," +
|
||||
statement.cellPersistentId + "," +
|
||||
statement.executionCount;
|
||||
statement.cellExecutionEventId;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -37,18 +37,17 @@ export class SlicedExecution {
|
|||
) { }
|
||||
|
||||
merge(...slicedExecutions: SlicedExecution[]): SlicedExecution {
|
||||
let cellSlices: { [ cellPersistentId: string ]: { [ executionCount: number ]: CellSlice }} = {};
|
||||
let cellSlices: { [ cellExecutionEventId: string ]: CellSlice } = {};
|
||||
let mergedCellSlices = [];
|
||||
for (let slicedExecution of slicedExecutions.concat(this)) {
|
||||
for (let cellSlice of slicedExecution.cellSlices) {
|
||||
let cell = cellSlice.cell;
|
||||
if (!cellSlices.hasOwnProperty(cell.persistentId)) cellSlices[cell.persistentId] = {};
|
||||
if (!cellSlices[cell.persistentId].hasOwnProperty(cell.executionCount)) {
|
||||
if (!cellSlices[cell.executionEventId]) {
|
||||
let newCellSlice = new CellSlice(cell.deepCopy(), new LocationSet(), cellSlice.executionTime);
|
||||
cellSlices[cell.persistentId][cell.executionCount] = newCellSlice;
|
||||
cellSlices[cell.executionEventId] = newCellSlice;
|
||||
mergedCellSlices.push(newCellSlice);
|
||||
}
|
||||
let mergedCellSlice = cellSlices[cell.persistentId][cell.executionCount];
|
||||
let mergedCellSlice = cellSlices[cell.executionEventId];
|
||||
mergedCellSlice.slice = mergedCellSlice.slice.union(cellSlice.slice);
|
||||
}
|
||||
}
|
||||
|
@ -126,19 +125,18 @@ export class ExecutionLogSlicer {
|
|||
public sliceAllExecutions(cell: ICell, pSeedLocations?: LocationSet): SlicedExecution[] {
|
||||
|
||||
// Make a map from cells to their execution times.
|
||||
let cellExecutionTimes: { [cellPersistentId: string]: { [executionCount: number]: Date } } = {};
|
||||
let cellExecutionTimes: { [cellExecutionEventId: string]: Date } = {};
|
||||
for (let execution of this._executionLog) {
|
||||
if (!cellExecutionTimes[execution.cell.persistentId]) cellExecutionTimes[execution.cell.persistentId] = {};
|
||||
cellExecutionTimes[execution.cell.persistentId][execution.cell.executionCount] = execution.executionTime;
|
||||
cellExecutionTimes[execution.cell.executionEventId] = execution.executionTime;
|
||||
}
|
||||
|
||||
return this._executionLog
|
||||
.filter(execution => execution.cell.persistentId == cell.persistentId)
|
||||
.filter(execution => execution.cell.executionEventId == cell.executionEventId)
|
||||
.filter(execution => execution.cell.executionCount != undefined)
|
||||
.map(execution => {
|
||||
|
||||
// Build the program up to that cell.
|
||||
let program = this._programBuilder.buildTo(execution.cell.persistentId, execution.cell.kernelId, execution.cell.executionCount);
|
||||
let program = this._programBuilder.buildTo(execution.cell.executionEventId);
|
||||
if (program == null) return null;
|
||||
|
||||
// Set the seed locations for the slice.
|
||||
|
@ -154,7 +152,7 @@ export class ExecutionLogSlicer {
|
|||
}
|
||||
|
||||
// Set seed locations were specified relative to the last cell's position in program.
|
||||
let lastCellLines = program.cellToLineMap[execution.cell.persistentId][execution.cell.executionCount];
|
||||
let lastCellLines = program.cellToLineMap[execution.cell.executionEventId];
|
||||
let lastCellStart = Math.min(...lastCellLines.items);
|
||||
seedLocations = new LocationSet(
|
||||
...seedLocations.items.map(loc => {
|
||||
|
@ -172,11 +170,11 @@ export class ExecutionLogSlicer {
|
|||
.sort((loc1, loc2) => loc1.first_line - loc2.first_line);
|
||||
|
||||
// Get the relative offsets of slice lines in each cell.
|
||||
let cellSliceLocations: { [cellId: string]: { [executionCount: number]: LocationSet } } = {};
|
||||
let cellSliceLocations: { [ executionEventId: string ]: LocationSet } = {};
|
||||
let cellOrder = new Array<ICell>();
|
||||
sliceLocations.forEach(location => {
|
||||
let sliceCell = program.lineToCellMap[location.first_line];
|
||||
let sliceCellLines = program.cellToLineMap[sliceCell.persistentId][sliceCell.executionCount];
|
||||
let sliceCellLines = program.cellToLineMap[sliceCell.executionEventId];
|
||||
let sliceCellStart = Math.min(...sliceCellLines.items);
|
||||
if (cellOrder.indexOf(sliceCell) == -1) {
|
||||
cellOrder.push(sliceCell);
|
||||
|
@ -187,20 +185,19 @@ export class ExecutionLogSlicer {
|
|||
last_line: location.last_line - sliceCellStart + 1,
|
||||
last_column: location.last_column
|
||||
};
|
||||
if (!cellSliceLocations[sliceCell.persistentId]) cellSliceLocations[sliceCell.persistentId] = {};
|
||||
if (!cellSliceLocations[sliceCell.persistentId][sliceCell.executionCount]) {
|
||||
cellSliceLocations[sliceCell.persistentId][sliceCell.executionCount] = new LocationSet();
|
||||
}
|
||||
cellSliceLocations[sliceCell.persistentId][sliceCell.executionCount].add(adjustedLocation);
|
||||
if (!cellSliceLocations[sliceCell.executionEventId]) {
|
||||
cellSliceLocations[sliceCell.executionEventId] = new LocationSet();
|
||||
}
|
||||
cellSliceLocations[sliceCell.executionEventId].add(adjustedLocation);
|
||||
});
|
||||
|
||||
let cellSlices = cellOrder.map((sliceCell): CellSlice => {
|
||||
let executionTime = undefined;
|
||||
if (cellExecutionTimes[sliceCell.persistentId] && cellExecutionTimes[sliceCell.persistentId][sliceCell.executionCount]) {
|
||||
executionTime = cellExecutionTimes[sliceCell.persistentId][sliceCell.executionCount];
|
||||
if (cellExecutionTimes[sliceCell.executionEventId]) {
|
||||
executionTime = cellExecutionTimes[sliceCell.executionEventId];
|
||||
}
|
||||
return new CellSlice(sliceCell,
|
||||
cellSliceLocations[sliceCell.persistentId][sliceCell.executionCount],
|
||||
cellSliceLocations[sliceCell.executionEventId],
|
||||
executionTime);
|
||||
});
|
||||
return new SlicedExecution(execution.executionTime, cellSlices);
|
||||
|
|
|
@ -7,8 +7,8 @@ import { NumberSet } from "./set";
|
|||
/**
|
||||
* Maps to find out what line numbers over a program correspond to what cells.
|
||||
*/
|
||||
export type CellToLineMap = { [cellId: string]: { [executionCount: number]: NumberSet } };
|
||||
export type LineToCellMap = { [line: number]: ICell };
|
||||
export type CellToLineMap = { [ cellExecutionEventId: string ]: NumberSet };
|
||||
export type LineToCellMap = { [ line: number ]: ICell };
|
||||
|
||||
/**
|
||||
* A program built from cells.
|
||||
|
@ -84,8 +84,7 @@ export class ProgramBuilder {
|
|||
for (let node of ast.walk(tree)) {
|
||||
// Sanity check that this is actually a node.
|
||||
if (node.hasOwnProperty("type")) {
|
||||
node.executionCount = cell.executionCount;
|
||||
node.cellPersistentId = cell.persistentId;
|
||||
node.cellExecutionEventId = cell.executionEventId;
|
||||
}
|
||||
}
|
||||
// By querying for defs and uses right when a cell is added to the log, we
|
||||
|
@ -120,23 +119,34 @@ export class ProgramBuilder {
|
|||
|
||||
/**
|
||||
* Build a program from the list of cells. Program will include the cells' contents in
|
||||
* execution order. It will omit cells that raised errors (syntax or runtime).
|
||||
* the order they were added to the log. It will omit cells that raised errors (syntax or
|
||||
* runtime, except for the last cell).
|
||||
*/
|
||||
buildTo(cellPersistentId: string, kernelId: string, executionCount: number): Program {
|
||||
buildTo(cellExecutionEventId: string): Program {
|
||||
|
||||
let cellVersions = this._cellPrograms
|
||||
.filter(cp => cp.cell.persistentId == cellPersistentId)
|
||||
.map(cp => cp.cell);
|
||||
let lastCell = cellVersions
|
||||
.filter(cell => cell.executionCount == executionCount)
|
||||
.filter(cell => cell.kernelId !== undefined && cell.kernelId === kernelId)[0];
|
||||
if (!lastCell) return null;
|
||||
let addingPrograms = false;
|
||||
let lastExecutionCountSeen;
|
||||
let cellPrograms = new Array<CellProgram>();
|
||||
|
||||
let sortedCellPrograms = this._cellPrograms
|
||||
.filter(cp => cp.cell.kernelId !== undefined && cp.cell.kernelId === kernelId)
|
||||
.filter(cp => cp.cell == lastCell || !cp.hasError) // can have error only if it's the last cell
|
||||
.filter(cp => cp.cell.executionCount != null && cp.cell.executionCount <= lastCell.executionCount)
|
||||
.sort((cp1, cp2) => cp1.cell.executionCount - cp2.cell.executionCount);
|
||||
for (let i = this._cellPrograms.length - 1; i >= 0; i--) {
|
||||
let cellProgram = this._cellPrograms[i];
|
||||
let cell = cellProgram.cell;
|
||||
if (!addingPrograms && cell.executionEventId === cellExecutionEventId) {
|
||||
addingPrograms = true;
|
||||
lastExecutionCountSeen = cell.executionCount;
|
||||
cellPrograms.unshift(cellProgram);
|
||||
continue;
|
||||
}
|
||||
if (addingPrograms) {
|
||||
if (cell.executionCount >= lastExecutionCountSeen) {
|
||||
break;
|
||||
}
|
||||
if (!cellProgram.hasError) {
|
||||
cellPrograms.unshift(cellProgram);
|
||||
}
|
||||
lastExecutionCountSeen = cell.executionCount;
|
||||
}
|
||||
}
|
||||
|
||||
let code = "";
|
||||
let currentLine = 1;
|
||||
|
@ -150,7 +160,7 @@ export class ProgramBuilder {
|
|||
location: undefined
|
||||
};
|
||||
|
||||
sortedCellPrograms.forEach(cp => {
|
||||
cellPrograms.forEach(cp => {
|
||||
|
||||
let cell = cp.cell;
|
||||
let cellCode = cell.text;
|
||||
|
@ -162,11 +172,8 @@ export class ProgramBuilder {
|
|||
for (let l = 0; l < cellLength; l++) { cellLines.push(currentLine + l); }
|
||||
cellLines.forEach(l => {
|
||||
lineToCellMap[l] = cell;
|
||||
if (!cellToLineMap[cell.persistentId]) cellToLineMap[cell.persistentId] = {};
|
||||
if (!cellToLineMap[cell.persistentId][cell.executionCount]) {
|
||||
cellToLineMap[cell.persistentId][cell.executionCount] = new NumberSet();
|
||||
}
|
||||
cellToLineMap[cell.persistentId][cell.executionCount].add(l);
|
||||
if (!cellToLineMap[cell.executionEventId]) cellToLineMap[cell.executionEventId] = new NumberSet();
|
||||
cellToLineMap[cell.executionEventId].add(l);
|
||||
});
|
||||
|
||||
// Accumulate the code text.
|
||||
|
@ -199,11 +206,10 @@ export class ProgramBuilder {
|
|||
return new Program(code, tree, cellToLineMap, lineToCellMap);
|
||||
}
|
||||
|
||||
|
||||
getCellProgram(cell: ICell): CellProgram {
|
||||
let matchingPrograms = this._cellPrograms.filter(
|
||||
(cp) => cp.cell.persistentId == cell.persistentId &&
|
||||
cp.cell.executionCount == cell.executionCount &&
|
||||
cp.cell.kernelId !== undefined && cp.cell.kernelId === cell.kernelId);
|
||||
(cp) => cp.cell.executionEventId == cell.executionEventId);
|
||||
if (matchingPrograms.length >= 1) return matchingPrograms.pop();
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -158,7 +158,7 @@ function getCellsJsonForSlice(slice: SlicedExecution, outputSelections?: OutputS
|
|||
if (originalOutputs) {
|
||||
for (let i = 0; i < originalOutputs.length; i++) {
|
||||
let output = originalOutputs[i];
|
||||
if (outputSelections.some(s => s.cell.persistentId == slicedCell.persistentId && s.outputIndex == i)) {
|
||||
if (outputSelections.some(s => s.cell.executionEventId == slicedCell.executionEventId && s.outputIndex == i)) {
|
||||
cellJson.outputs.push(output);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,12 +13,6 @@ export interface ICell {
|
|||
* due to the implementation of Jupyter Lab.
|
||||
*/
|
||||
readonly id: string;
|
||||
|
||||
/**
|
||||
* A persistent ID for this cell, that will stay the same even as the notebook is closed and
|
||||
* re-opened. In general, all gathering functionality should refer to cells using this ID.
|
||||
*/
|
||||
readonly persistentId: string;
|
||||
|
||||
/**
|
||||
* Whether this cell was created by gathering code.
|
||||
|
@ -36,14 +30,16 @@ export interface ICell {
|
|||
*/
|
||||
text: string;
|
||||
|
||||
/**
|
||||
* ID of the session that was last used to execute this cell. Note that unfortunately these
|
||||
* session IDs reset whenever someone opens a new notebook---let's keep an eye out for ways
|
||||
* to tie together sessions ID that should be merged.
|
||||
*/
|
||||
kernelId: string;
|
||||
|
||||
executionCount: number;
|
||||
|
||||
/**
|
||||
* A unique ID generated each time a cell is executed. This lets us disambiguate between two
|
||||
* runs of a cell that have the same ID *and* execution count, if the kernel was restarted.
|
||||
* This ID should also be programmed to be *persistent*, so that even after a notebook is
|
||||
* reloaded, the cell in the same position will still have this ID.
|
||||
*/
|
||||
readonly executionEventId: string;
|
||||
|
||||
outputs: nbformat.IOutput[];
|
||||
|
||||
/**
|
||||
|
@ -85,9 +81,8 @@ export abstract class AbstractCell implements ICell {
|
|||
|
||||
abstract is_cell: boolean;
|
||||
abstract id: string;
|
||||
abstract persistentId: string;
|
||||
abstract executionCount: number;
|
||||
abstract kernelId: string;
|
||||
abstract executionEventId: string;
|
||||
abstract hasError: boolean;
|
||||
abstract isCode: boolean;
|
||||
abstract text: string;
|
||||
|
@ -112,7 +107,6 @@ export abstract class AbstractCell implements ICell {
|
|||
toJSON(): any {
|
||||
return {
|
||||
id: this.id,
|
||||
persistentId: this.persistentId,
|
||||
executionCount: this.executionCount,
|
||||
lineCount: this.text.split("\n").length,
|
||||
isCode: this.isCode,
|
||||
|
@ -145,7 +139,7 @@ export abstract class AbstractCell implements ICell {
|
|||
outputs: this.outputs,
|
||||
metadata: {
|
||||
gathered: this.gathered,
|
||||
persistent_id: this.persistentId,
|
||||
execution_event_id: this.executionEventId,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -157,15 +151,14 @@ export abstract class AbstractCell implements ICell {
|
|||
export class LogCell extends AbstractCell {
|
||||
|
||||
constructor(data: {
|
||||
id?: string, persistentId?: string, executionCount?: number, kernelId?: string,
|
||||
id?: string, executionCount?: number, executionEventId?: string,
|
||||
hasError?: boolean, text?: string, outputs?: nbformat.IOutput[]
|
||||
}) {
|
||||
super();
|
||||
this.is_cell = true;
|
||||
this.id = data.id || UUID.uuid4();
|
||||
this.persistentId = data.persistentId || UUID.uuid4();
|
||||
this.executionCount = data.executionCount || undefined;
|
||||
this.kernelId = data.kernelId;
|
||||
this.executionEventId = data.executionEventId || UUID.uuid4();
|
||||
this.hasError = data.hasError || false;
|
||||
this.text = data.text || "";
|
||||
this.lastExecutedText = this.text;
|
||||
|
@ -179,9 +172,8 @@ export class LogCell extends AbstractCell {
|
|||
|
||||
readonly is_cell: boolean;
|
||||
readonly id: string;
|
||||
readonly persistentId: string;
|
||||
readonly executionCount: number;
|
||||
readonly kernelId: string;
|
||||
readonly executionEventId: string;
|
||||
readonly hasError: boolean;
|
||||
readonly isCode: boolean;
|
||||
readonly text: string;
|
||||
|
@ -209,11 +201,12 @@ export class LabCell extends AbstractCell {
|
|||
return this._model.id;
|
||||
}
|
||||
|
||||
get persistentId(): string {
|
||||
if (!this._model.metadata.has("persistent_id")) {
|
||||
this._model.metadata.set("persistent_id", UUID.uuid4());
|
||||
}
|
||||
return this._model.metadata.get("persistent_id") as string;
|
||||
get executionEventId(): string {
|
||||
return this._model.metadata.get("execution_event_id") as string;
|
||||
}
|
||||
|
||||
set executionEventId(id: string) {
|
||||
this._model.metadata.set('execution_event_id', id);
|
||||
}
|
||||
|
||||
get text(): string {
|
||||
|
@ -232,14 +225,6 @@ export class LabCell extends AbstractCell {
|
|||
this._model.metadata.set('last_executed_text', text);
|
||||
}
|
||||
|
||||
get kernelId(): string {
|
||||
return this._model.metadata.get('kernel_id') as string;
|
||||
}
|
||||
|
||||
set kernelId(kernelId: string) {
|
||||
this._model.metadata.set('kernel_id', kernelId);
|
||||
}
|
||||
|
||||
get executionCount(): number {
|
||||
return this._model.executionCount;
|
||||
}
|
||||
|
|
|
@ -163,10 +163,10 @@ export class GatherModel {
|
|||
/**
|
||||
* Remove the editor def from the list of editor definitions.
|
||||
*/
|
||||
removeEditorDefsForCell(cellPersistentId: string) {
|
||||
removeEditorDefsForCell(cellExecutionEventId: string) {
|
||||
for (let i = this._editorDefs.length - 1; i >= 0; i--) {
|
||||
let editorDef = this._editorDefs[i];
|
||||
if (editorDef.cell.persistentId == cellPersistentId) {
|
||||
if (editorDef.cell.executionEventId == cellExecutionEventId) {
|
||||
this._editorDefs.splice(i, 1);
|
||||
this.notifyObservers(GatherModelEvent.EDITOR_DEF_REMOVED, editorDef);
|
||||
}
|
||||
|
@ -353,10 +353,10 @@ export class GatherModel {
|
|||
/**
|
||||
* Deselect all outputs.
|
||||
*/
|
||||
deselectOutputsForCell(cellPersistentId: string) {
|
||||
deselectOutputsForCell(cellExecutionEventId: string) {
|
||||
for (let i = this._selectedOutputs.length - 1; i >= 0; i--) {
|
||||
let output = this._selectedOutputs[i];
|
||||
if (output.cell.persistentId == cellPersistentId) {
|
||||
if (output.cell.executionEventId == cellExecutionEventId) {
|
||||
this._selectedOutputs.splice(i, 1);
|
||||
this.notifyObservers(GatherModelEvent.OUTPUT_DESELECTED, output);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import { NotebookPanel } from "@jupyterlab/notebook";
|
|||
import { IObservableList } from "@jupyterlab/observables";
|
||||
import { GatherModel } from "../model";
|
||||
import { LabCell } from "../model/cell";
|
||||
import { UUID } from "@phosphor/coreutils";
|
||||
|
||||
/**
|
||||
* Listens to cell executions and edits.
|
||||
|
@ -13,7 +14,6 @@ export class CellChangeListener {
|
|||
|
||||
constructor(gatherModel: GatherModel, notebook: NotebookPanel) {
|
||||
this._gatherModel = gatherModel;
|
||||
this._notebook = notebook;
|
||||
this._registerCurrentCells(notebook);
|
||||
notebook.content.model.cells.changed.connect((_, change) => this._registerAddedCells(change), this);
|
||||
}
|
||||
|
@ -29,10 +29,7 @@ export class CellChangeListener {
|
|||
*/
|
||||
private _annotateCellWithExecutionInformation(cell: LabCell) {
|
||||
cell.lastExecutedText = cell.text;
|
||||
cell.kernelId = this._notebook.session.kernel.model.id;
|
||||
this._notebook.session.kernel.requestHistory({ output: false, raw: true, hist_access_type: 'tail' }).then((res) => {
|
||||
console.log("Gotta reply");
|
||||
});
|
||||
cell.executionEventId = UUID.uuid4();
|
||||
}
|
||||
|
||||
private _registerCell(cell: ICellModel) {
|
||||
|
@ -71,6 +68,4 @@ export class CellChangeListener {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _notebook: NotebookPanel;
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
import { Cell, CodeCell, isCodeCellModel } from "@jupyterlab/cells";
|
||||
import { ICell, LabCell } from "../model/cell";
|
||||
import { Cell, isCodeCellModel } from "@jupyterlab/cells";
|
||||
import { CodeMirrorEditor } from "@jupyterlab/codemirror";
|
||||
import { NotebookPanel } from "@jupyterlab/notebook";
|
||||
import CodeMirror from "codemirror";
|
||||
import { ICell, LabCell } from "../model/cell";
|
||||
|
||||
/**
|
||||
* Finds the HTML elements in a notebook corresponding to a cell. Useful for looking up HTML
|
||||
|
@ -14,41 +14,28 @@ export class NotebookElementFinder {
|
|||
this._notebook = notebook;
|
||||
}
|
||||
|
||||
getCellWithPersistentId(persistentId: string): Cell | null {
|
||||
for (let cell of this._notebook.content.widgets) {
|
||||
if (isCodeCellModel(cell.model)) {
|
||||
let labCell = new LabCell(cell.model);
|
||||
if (labCell.persistentId == persistentId) {
|
||||
return cell;
|
||||
/**
|
||||
* Look up cells in the notebook using the ID of the execution event that executed it last.
|
||||
* This is the only way to make sure we get the right cell if a cell has been executed
|
||||
* with the same exeuction count twice in two separate notebook sessions.
|
||||
*/
|
||||
getCellWidget(cell: ICell): Cell | null {
|
||||
for (let cellWidget of this._notebook.content.widgets) {
|
||||
if (isCodeCellModel(cellWidget.model)) {
|
||||
let labCell = new LabCell(cellWidget.model);
|
||||
if (labCell.executionEventId == cell.executionEventId) {
|
||||
return cellWidget;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a cell from the notebook.
|
||||
* (Don't call this right after a cell execution event, as it takes a while for the
|
||||
* execution count to update in an executed cell).
|
||||
*/
|
||||
getCell(persistentId: string, executionCount?: number): Cell | null {
|
||||
let cell = this.getCellWithPersistentId(persistentId);
|
||||
if (cell != null && (cell as CodeCell).model.executionCount == executionCount) {
|
||||
return cell;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the element for the code editor for a cell.
|
||||
*/
|
||||
getEditor(cell: ICell): CodeMirror.Editor | null {
|
||||
let widget = this.getCellWithPersistentId(cell.persistentId);
|
||||
return this._getEditor(widget);
|
||||
}
|
||||
|
||||
getEditorWithExecutionCount(cell: ICell): CodeMirror.Editor | null {
|
||||
let widget = this.getCell(cell.persistentId, cell.executionCount);
|
||||
let widget = this.getCellWidget(cell);
|
||||
return this._getEditor(widget);
|
||||
}
|
||||
|
||||
|
@ -63,7 +50,7 @@ export class NotebookElementFinder {
|
|||
* Finds HTML elements for cell outputs in a notebook.
|
||||
*/
|
||||
getOutputs(cell: ICell): HTMLElement[] {
|
||||
let cellWidget = this.getCell(cell.persistentId, cell.executionCount);
|
||||
let cellWidget = this.getCellWidget(cell);
|
||||
let outputElements: HTMLElement[] = [];
|
||||
if (cellWidget == null) {
|
||||
return outputElements;
|
||||
|
|
|
@ -177,7 +177,7 @@ export class MarkerManager implements IGatherObserver {
|
|||
let defSelection = eventData as DefSelection;
|
||||
this._defMarkers.filter(marker => {
|
||||
return defSelection.editorDef.def.location == marker.location &&
|
||||
defSelection.cell.persistentId == marker.cell.persistentId;
|
||||
defSelection.cell.executionEventId == marker.cell.executionEventId;
|
||||
}).forEach(marker => marker.deselect());
|
||||
|
||||
let editorDef = defSelection.editorDef;
|
||||
|
@ -195,7 +195,7 @@ export class MarkerManager implements IGatherObserver {
|
|||
let outputSelection = eventData as OutputSelection;
|
||||
this._outputMarkers.filter(marker => {
|
||||
return marker.outputIndex == outputSelection.outputIndex &&
|
||||
marker.cell.persistentId == outputSelection.cell.persistentId;
|
||||
marker.cell.executionEventId == outputSelection.cell.executionEventId;
|
||||
}).forEach(marker => marker.deselect());
|
||||
}
|
||||
|
||||
|
@ -261,8 +261,8 @@ export class MarkerManager implements IGatherObserver {
|
|||
* Clear all def markers that belong to this editor.
|
||||
*/
|
||||
clearSelectablesForCell(cell: ICell) {
|
||||
this._model.removeEditorDefsForCell(cell.persistentId);
|
||||
this._model.deselectOutputsForCell(cell.persistentId);
|
||||
this._model.removeEditorDefsForCell(cell.executionEventId);
|
||||
this._model.deselectOutputsForCell(cell.executionEventId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -304,8 +304,8 @@ export class MarkerManager implements IGatherObserver {
|
|||
slice.cellSlices.forEach(cellSlice => {
|
||||
let loggedCell = cellSlice.cell;
|
||||
let sliceLocations = cellSlice.slice;
|
||||
let liveCellWidget = this._elementFinder.getCell(loggedCell.persistentId, loggedCell.executionCount);
|
||||
let editor = this._elementFinder.getEditorWithExecutionCount(loggedCell);
|
||||
let liveCellWidget = this._elementFinder.getCellWidget(loggedCell);
|
||||
let editor = this._elementFinder.getEditor(loggedCell);
|
||||
|
||||
if (liveCellWidget && editor) {
|
||||
let liveCell = new LabCell(liveCellWidget.model as ICodeCellModel);
|
||||
|
@ -333,8 +333,8 @@ export class MarkerManager implements IGatherObserver {
|
|||
}
|
||||
|
||||
private _updateDependenceHighlightsForCell(cell: ICell) {
|
||||
let editor = this._elementFinder.getEditorWithExecutionCount(cell);
|
||||
let liveCellWidget = this._elementFinder.getCell(cell.persistentId, cell.executionCount);
|
||||
let editor = this._elementFinder.getEditor(cell);
|
||||
let liveCellWidget = this._elementFinder.getCellWidget(cell);
|
||||
let liveCell = new LabCell(liveCellWidget.model as ICodeCellModel);
|
||||
this._dependencyLineMarkers
|
||||
.filter((marker) => marker.editor == editor)
|
||||
|
|
|
@ -61,24 +61,24 @@ export class RevisionBrowser extends Widget {
|
|||
let defSelections = model.selectedDefs;
|
||||
let outputSelections = model.selectedOutputs;
|
||||
let slices;
|
||||
let cellPersistentId;
|
||||
let cellExecutionEventId;
|
||||
if (defSelections.length > 0) {
|
||||
slices = model.getSelectedDefSlices(defSelections[0]);
|
||||
cellPersistentId = defSelections[0].cell.persistentId;
|
||||
cellExecutionEventId = defSelections[0].cell.executionEventId;
|
||||
} else if (outputSelections.length > 0) {
|
||||
slices = model.getSelectedOutputSlices(outputSelections[0]);
|
||||
cellPersistentId = outputSelections[0].cell.persistentId;
|
||||
cellExecutionEventId = outputSelections[0].cell.executionEventId;
|
||||
}
|
||||
log("Bringing up the revision browser for selection", {
|
||||
cellPersistendId: cellPersistentId, slices,
|
||||
cellPersistendId: cellExecutionEventId, slices,
|
||||
selectedDefs: model.selectedDefs,
|
||||
selectedOutputs: model.selectedOutputs
|
||||
});
|
||||
if (slices && cellPersistentId) {
|
||||
if (slices && cellExecutionEventId) {
|
||||
// Only show output if the selection was output.
|
||||
let includeOutput = model.selectedOutputs.length >= 1;
|
||||
let historyModel = buildHistoryModel(
|
||||
model, cellPersistentId, slices, includeOutput);
|
||||
model, cellExecutionEventId, slices, includeOutput);
|
||||
let historyViewer = new HistoryViewer({
|
||||
model: historyModel,
|
||||
outputRenderer: this._outputRenderer
|
||||
|
|
|
@ -89,9 +89,8 @@ function _loadExecutionFromJson(executionJson: JSONObject): CellExecution {
|
|||
let cellJson = executionJson['cell'] as JSONObject;
|
||||
|
||||
let id = _getString(cellJson, 'id');
|
||||
let persistentId = _getString(cellJson, 'persistentId');
|
||||
let executionCount = _getNumber(cellJson, 'executionCount');
|
||||
let kernelId = _getString(cellJson, 'kernelId');
|
||||
let executionEventId = _getString(cellJson, 'executionEventId');
|
||||
let hasError = _getBoolean(cellJson, 'hasError');
|
||||
let text = _getString(cellJson, 'text');
|
||||
|
||||
|
@ -99,7 +98,7 @@ function _loadExecutionFromJson(executionJson: JSONObject): CellExecution {
|
|||
let executionTime = new Date(executionTimeString);
|
||||
|
||||
if (id == null || executionCount == null || hasError == null ||
|
||||
kernelId == null || text == null || executionTime == null) {
|
||||
executionEventId == null || text == null || executionTime == null) {
|
||||
log("Cell could not be loaded, as it's missing a critical field.");
|
||||
return null;
|
||||
}
|
||||
|
@ -107,6 +106,6 @@ function _loadExecutionFromJson(executionJson: JSONObject): CellExecution {
|
|||
/**
|
||||
* TODO(andrewhead): Update with Kunal's code for serializing and deserializing outputs.
|
||||
*/
|
||||
let cell = new LogCell({ id, executionCount, hasError, text, persistentId });
|
||||
let cell = new LogCell({ id, executionCount, hasError, text, executionEventId });
|
||||
return new CellExecution(cell, executionTime);
|
||||
}
|
|
@ -10,9 +10,8 @@ interface CellExecutionJson extends JSONObject {
|
|||
|
||||
interface CellJson extends JSONObject {
|
||||
id: string;
|
||||
persistentId: string;
|
||||
executionEventId: string;
|
||||
executionCount: number;
|
||||
kernelId: string;
|
||||
hasError: boolean;
|
||||
isCode: boolean;
|
||||
text: string;
|
||||
|
@ -32,9 +31,8 @@ export function storeHistory(notebookModel: INotebookModel, executionLog: Execut
|
|||
let cell = cellExecution.cell;
|
||||
let cellJson = new Object(null) as CellJson;
|
||||
cellJson.id = cell.id;
|
||||
cellJson.persistentId = cell.persistentId;
|
||||
cellJson.executionEventId = cell.executionEventId;
|
||||
cellJson.executionCount = cell.executionCount;
|
||||
cellJson.kernelId = cell.kernelId;
|
||||
cellJson.hasError = cell.hasError;
|
||||
cellJson.text = cell.text;
|
||||
|
||||
|
|
|
@ -6,8 +6,9 @@ import { CellSlice } from "../model/cellslice";
|
|||
|
||||
describe('SlicedExecution', () => {
|
||||
|
||||
function cell(persistentId: string, executionCount: number, ...codeLines: string[]): ICell {
|
||||
return new LogCell({ executionCount, text: codeLines.join("\n"), persistentId });
|
||||
function cell(executionEventId: string, text: string, executionCount?: number, id?: string): ICell {
|
||||
if (executionCount === undefined) executionCount = 1;
|
||||
return new LogCell({ executionCount, text, executionEventId, id });
|
||||
}
|
||||
|
||||
function cellSlice(cell: ICell, slice: LocationSet): CellSlice {
|
||||
|
@ -35,28 +36,28 @@ describe('SlicedExecution', () => {
|
|||
it('unions slices with different cells', () => {
|
||||
let slice1 = slicedExecution(
|
||||
cellSlice(
|
||||
cell("1", 1, "a = 1"),
|
||||
cell("1", "a = 1", 1),
|
||||
new LocationSet(location(1, 0, 1, 5))
|
||||
));
|
||||
let slice2 = slicedExecution(
|
||||
cellSlice(
|
||||
cell("2", 2, "b = 2"),
|
||||
cell("2", "b = 2", 2),
|
||||
new LocationSet(location(1, 0, 1, 5))
|
||||
));
|
||||
let merged = slice1.merge(slice2);
|
||||
expect(merged.cellSlices[0].cell.persistentId).to.equal("1");
|
||||
expect(merged.cellSlices[1].cell.persistentId).to.equal("2");
|
||||
expect(merged.cellSlices[0].cell.executionEventId).to.equal("1");
|
||||
expect(merged.cellSlices[1].cell.executionEventId).to.equal("2");
|
||||
});
|
||||
|
||||
it('will not include the same locations from the same cell twice', () => {
|
||||
let slice1 = slicedExecution(
|
||||
cellSlice(
|
||||
cell("1", 1, "a = 1"),
|
||||
cell("1", "a = 1"),
|
||||
new LocationSet(location(1, 0, 1, 5))
|
||||
));
|
||||
let slice2 = slicedExecution(
|
||||
cellSlice(
|
||||
cell("1", 1, "a = 1"),
|
||||
cell("1", "a = 1"),
|
||||
new LocationSet(location(1, 0, 1, 5))
|
||||
));
|
||||
let merged = slice1.merge(slice2);
|
||||
|
@ -64,15 +65,16 @@ describe('SlicedExecution', () => {
|
|||
expect(merged.cellSlices[0].slice.size).to.equal(1);
|
||||
});
|
||||
|
||||
it('considers the same cell with different execution counts to be different', () => {
|
||||
it('considers two cells sharing ID and execution count but differing in execution event ' +
|
||||
'ID to be different', () => {
|
||||
let slice1 = slicedExecution(
|
||||
cellSlice(
|
||||
cell("1", 1, "a = 1"),
|
||||
cell("1", "a = 1", 1, "id1"),
|
||||
new LocationSet(location(1, 0, 1, 5))
|
||||
));
|
||||
let slice2 = slicedExecution(
|
||||
cellSlice(
|
||||
cell("1", 2, "a = 1"),
|
||||
cell("2", "a = 1", 1, "id1"),
|
||||
new LocationSet(location(1, 0, 1, 5))
|
||||
));
|
||||
let merged = slice1.merge(slice2);
|
||||
|
@ -82,12 +84,12 @@ describe('SlicedExecution', () => {
|
|||
it('will include complementary ranges from two slices of the same cell', () => {
|
||||
let slice1 = slicedExecution(
|
||||
cellSlice(
|
||||
cell("1", 1, "a = 1"),
|
||||
cell("1", "a = 1"),
|
||||
new LocationSet(location(1, 0, 1, 5))
|
||||
));
|
||||
let slice2 = slicedExecution(
|
||||
cellSlice(
|
||||
cell("1", 1, "a = 1"),
|
||||
cell("1", "a = 1"),
|
||||
new LocationSet(location(1, 0, 1, 4))
|
||||
));
|
||||
let merged = slice1.merge(slice2);
|
||||
|
@ -100,12 +102,12 @@ describe('SlicedExecution', () => {
|
|||
it('reorders the cells in execution order', () => {
|
||||
let slice1 = slicedExecution(
|
||||
cellSlice(
|
||||
cell("2", 2, "a = 1"),
|
||||
cell("2", "a = 1", 2),
|
||||
new LocationSet(location(1, 0, 1, 5))
|
||||
));
|
||||
let slice2 = slicedExecution(
|
||||
cellSlice(
|
||||
cell("1", 1, "a = 1"),
|
||||
cell("1", "a = 1", 1),
|
||||
new LocationSet(location(1, 0, 1, 4))
|
||||
));
|
||||
let merged = slice1.merge(slice2);
|
||||
|
@ -115,16 +117,16 @@ describe('SlicedExecution', () => {
|
|||
|
||||
it('can do an n-way merge with a bunch of cells', () => {
|
||||
let slice1 = slicedExecution(
|
||||
cellSlice(cell("1", 1, "a = 1"), new LocationSet(location(1, 0, 1, 5))),
|
||||
cellSlice(cell("2", 2, "b = 1"), new LocationSet(location(1, 0, 1, 5)))
|
||||
cellSlice(cell("1", "a = 1"), new LocationSet(location(1, 0, 1, 5))),
|
||||
cellSlice(cell("2", "b = 1"), new LocationSet(location(1, 0, 1, 5)))
|
||||
);
|
||||
let slice2 = slicedExecution(
|
||||
cellSlice(cell("3", 3, "c = 1"), new LocationSet(location(1, 0, 1, 5))),
|
||||
cellSlice(cell("4", 4, "d = 1"), new LocationSet(location(1, 0, 1, 5)))
|
||||
cellSlice(cell("3", "c = 1"), new LocationSet(location(1, 0, 1, 5))),
|
||||
cellSlice(cell("4", "d = 1"), new LocationSet(location(1, 0, 1, 5)))
|
||||
);
|
||||
let slice3 = slicedExecution(
|
||||
cellSlice(cell("5", 5, "e = 1"), new LocationSet(location(1, 0, 1, 5))),
|
||||
cellSlice(cell("6", 6, "f = 1"), new LocationSet(location(1, 0, 1, 5)))
|
||||
cellSlice(cell("5", "e = 1"), new LocationSet(location(1, 0, 1, 5))),
|
||||
cellSlice(cell("6", "f = 1"), new LocationSet(location(1, 0, 1, 5)))
|
||||
);
|
||||
let merged = slice1.merge(slice2, slice3);
|
||||
expect(merged.cellSlices.length).to.equal(6);
|
||||
|
|
|
@ -4,17 +4,9 @@ import { ICell, LogCell } from '../model/cell';
|
|||
|
||||
|
||||
describe('program builder', () => {
|
||||
|
||||
const TEST_KERNEL_ID = "kernel-1";
|
||||
|
||||
function createCellWithKernelId(persistentId: string, kernelId: string, executionCount: number,
|
||||
...codeLines: string[]): ICell {
|
||||
let text = codeLines.join("\n");
|
||||
return new LogCell({ executionCount, persistentId, kernelId, text });
|
||||
}
|
||||
|
||||
function createCell(persistentId: string, executionCount: number, ...codeLines: string[]): ICell {
|
||||
return(createCellWithKernelId(persistentId, TEST_KERNEL_ID, executionCount, ...codeLines));
|
||||
function createCell(executionEventId: string, text: string, executionCount?: number): ICell {
|
||||
return new LogCell({ executionEventId, text, executionCount });
|
||||
}
|
||||
|
||||
let programBuilder: ProgramBuilder;
|
||||
|
@ -22,125 +14,125 @@ describe('program builder', () => {
|
|||
programBuilder = new ProgramBuilder();
|
||||
});
|
||||
|
||||
it('appends cell contents in execution order', () => {
|
||||
it('appends cell contents in order', () => {
|
||||
programBuilder.add(
|
||||
createCell("id1", 2, "print(1)"),
|
||||
createCell("id2", 1, "print(2)")
|
||||
createCell("id1", "print(1)"),
|
||||
createCell("id2", "print(2)")
|
||||
)
|
||||
let code = programBuilder.buildTo("id1", TEST_KERNEL_ID, 2).text;
|
||||
expect(code).to.equal(["print(2)", "print(1)", ""].join("\n"))
|
||||
let code = programBuilder.buildTo("id2").text;
|
||||
expect(code).to.equal(["print(1)", "print(2)", ""].join("\n"))
|
||||
});
|
||||
|
||||
it('builds a map from lines to cells', () => {
|
||||
let cell1 = createCell("id1", 1, "print(1)");
|
||||
let cell2 = createCell("id2", 2, "print(2)");
|
||||
let cell1 = createCell("id1", "print(1)");
|
||||
let cell2 = createCell("id2", "print(2)");
|
||||
programBuilder.add(cell1, cell2);
|
||||
let lineToCellMap = programBuilder.buildTo("id2", TEST_KERNEL_ID, 2).lineToCellMap;
|
||||
let lineToCellMap = programBuilder.buildTo("id2").lineToCellMap;
|
||||
expect(lineToCellMap[1]).to.equal(cell1);
|
||||
expect(lineToCellMap[2]).to.equal(cell2);
|
||||
});
|
||||
|
||||
it('builds a map from cells to lines', () => {
|
||||
let cell1 = createCell("id1", 1, "print(1)");
|
||||
let cell2 = createCell("id2", 2, "print(2)");
|
||||
let cell1 = createCell("id1", "print(1)");
|
||||
let cell2 = createCell("id2", "print(2)");
|
||||
programBuilder.add(cell1, cell2);
|
||||
let cellToLineMap = programBuilder.buildTo("id2", TEST_KERNEL_ID, 2).cellToLineMap;
|
||||
expect(cellToLineMap["id1"][1].items).to.deep.equal([1]);
|
||||
expect(cellToLineMap["id2"][2].items).to.deep.equal([2]);
|
||||
let cellToLineMap = programBuilder.buildTo("id2").cellToLineMap;
|
||||
expect(cellToLineMap["id1"].items).to.deep.equal([1]);
|
||||
expect(cellToLineMap["id2"].items).to.deep.equal([2]);
|
||||
});
|
||||
|
||||
it('stops after the specified cell', () => {
|
||||
programBuilder.add(
|
||||
createCell("id1", 2, "print(1)"),
|
||||
createCell("id2", 1, "print(2)")
|
||||
createCell("id1", "print(1)"),
|
||||
createCell("id2", "print(2)")
|
||||
);
|
||||
let code = programBuilder.buildTo("id2", TEST_KERNEL_ID, 1).text;
|
||||
expect(code).to.equal("print(2)\n");
|
||||
let code = programBuilder.buildTo("id1").text;
|
||||
expect(code).to.equal("print(1)\n");
|
||||
});
|
||||
|
||||
/* We might want the program builder to include code that was executed before a runtime
|
||||
* error, though this will probably require us to rewrite the code. */
|
||||
it('skips cells with errors', () => {
|
||||
let badCell = createCell("idE", 2, "print(2)");
|
||||
let badCell = createCell("idE", "print(2)");
|
||||
badCell.hasError = true;
|
||||
programBuilder.add(
|
||||
createCell("id1", 1, "print(1)"),
|
||||
createCell("id1", "print(1)"),
|
||||
badCell,
|
||||
createCell("id3", 3, "print(3)")
|
||||
createCell("id3", "print(3)")
|
||||
);
|
||||
let code = programBuilder.buildTo("id3", TEST_KERNEL_ID, 3).text;
|
||||
let code = programBuilder.buildTo("id3").text;
|
||||
expect(code).to.equal(["print(1)", "print(3)", ""].join("\n"));
|
||||
});
|
||||
|
||||
it('includes cells that end with errors', () => {
|
||||
let badCell = createCell("idE", 3, "print(bad_name)");
|
||||
let badCell = createCell("idE", "print(bad_name)");
|
||||
badCell.hasError = true;
|
||||
programBuilder.add(
|
||||
createCell("id1", 1, "print(1)"),
|
||||
createCell("id2", 2, "print(2)"),
|
||||
createCell("id1", "print(1)"),
|
||||
createCell("id2", "print(2)"),
|
||||
badCell,
|
||||
);
|
||||
let code = programBuilder.buildTo("idE", TEST_KERNEL_ID, 3).text;
|
||||
let code = programBuilder.buildTo("idE").text;
|
||||
expect(code).to.equal(["print(1)", "print(2)", "print(bad_name)", ""].join("\n"));
|
||||
});
|
||||
|
||||
/* Sometimes, a cell might not throw an error, but our parser might choke. This shouldn't
|
||||
* crash the entire program---just skip it if it can't parse. */
|
||||
it('skips cells that fail to parse', () => {
|
||||
let badCell = createCell("idE", 2, "causes_syntax_error(");
|
||||
let badCell = createCell("idE", "causes_syntax_error(");
|
||||
|
||||
// Hide console output from parse errors.
|
||||
let oldConsoleLog = console.log;
|
||||
console.log = () => {};
|
||||
|
||||
programBuilder.add(
|
||||
createCell("id1", 1, "print(1)"),
|
||||
createCell("id1", "print(1)"),
|
||||
badCell,
|
||||
createCell("id3", 3, "print(3)")
|
||||
createCell("id3", "print(3)")
|
||||
);
|
||||
|
||||
// Restore console output.
|
||||
console.log = oldConsoleLog;
|
||||
|
||||
let code = programBuilder.buildTo("id3", TEST_KERNEL_ID, 3).text;
|
||||
let code = programBuilder.buildTo("id3").text;
|
||||
expect(code).to.equal(["print(1)", "print(3)", ""].join("\n"));
|
||||
});
|
||||
|
||||
it('skips cells that were executed with different kernels', () => {
|
||||
it('skips cells that were executed in prior kernels', () => {
|
||||
programBuilder.add(
|
||||
createCellWithKernelId("id1", "kernel-1", 1, "print(1)"),
|
||||
createCellWithKernelId("id2", "kernel-2", 2, "print(2)"),
|
||||
createCellWithKernelId("id3", "kernel-1", 3, "print(3)")
|
||||
createCell("id1", "print(1)", 1),
|
||||
createCell("id2", "print(2)", 1),
|
||||
createCell("id3", "print(3)", 2),
|
||||
createCell("id3", "print(4)", 1)
|
||||
);
|
||||
let code = programBuilder.buildTo("id3", "kernel-1", 3).text;
|
||||
expect(code).to.equals(["print(1)", "print(3)", ""].join("\n"));
|
||||
let code = programBuilder.buildTo("id3").text;
|
||||
expect(code).to.equals(["print(4)", ""].join("\n"));
|
||||
});
|
||||
|
||||
it('constructs a tree for the program', () => {
|
||||
programBuilder.add(
|
||||
createCell("id1", 1, "print(1)"),
|
||||
createCell("id2", 2, "print(2)")
|
||||
createCell("id1", "print(1)"),
|
||||
createCell("id2", "print(2)")
|
||||
)
|
||||
let tree = programBuilder.buildTo("id2", TEST_KERNEL_ID, 2).tree;
|
||||
let tree = programBuilder.buildTo("id2").tree;
|
||||
expect(tree.code.length).to.equal(2);
|
||||
});
|
||||
|
||||
it('adjusts the node locations', () => {
|
||||
programBuilder.add(
|
||||
createCell("id1", 1, "print(1)"),
|
||||
createCell("id2", 2, "print(2)")
|
||||
createCell("id1", "print(1)"),
|
||||
createCell("id2", "print(2)")
|
||||
)
|
||||
let tree = programBuilder.buildTo("id2", TEST_KERNEL_ID, 2).tree;
|
||||
let tree = programBuilder.buildTo("id2").tree;
|
||||
expect(tree.code[0].location.first_line).to.equal(1);
|
||||
expect(tree.code[1].location.first_line).to.equal(2);
|
||||
});
|
||||
|
||||
it('annotates tree nodes with cell ID info', () => {
|
||||
programBuilder.add(
|
||||
createCell("id1", 2, "print(1)")
|
||||
createCell("id1", "print(1)")
|
||||
);
|
||||
let tree = programBuilder.buildTo("id1", TEST_KERNEL_ID, 2).tree;
|
||||
expect(tree.code[0].cellPersistentId).to.equal("id1");
|
||||
expect(tree.code[0].executionCount).to.equal(2);
|
||||
let tree = programBuilder.buildTo("id1").tree;
|
||||
expect(tree.code[0].cellExecutionEventId).to.equal("id1");
|
||||
});
|
||||
});
|
|
@ -15,7 +15,7 @@ import { HistoryModel } from './model';
|
|||
*/
|
||||
export function buildHistoryModel(
|
||||
gatherModel: GatherModel,
|
||||
selectedCellPersistentId: string,
|
||||
selectedCellExecutionEventId: string,
|
||||
executionVersions: SlicedExecution[],
|
||||
includeOutput?: boolean
|
||||
): HistoryModel {
|
||||
|
@ -24,9 +24,9 @@ export function buildHistoryModel(
|
|||
// recent version, save a mapping from cells' IDs to their content, so we can look them up to
|
||||
// make comparisons between versions of cells.
|
||||
let lastestVersion = executionVersions[executionVersions.length - 1];
|
||||
let latestCellVersions: { [cellPersistentId: string]: CellSlice } = {};
|
||||
let latestCellVersions: { [cellExecutionEvent: string]: CellSlice } = {};
|
||||
lastestVersion.cellSlices.forEach(cellSlice => {
|
||||
latestCellVersions[cellSlice.cell.persistentId] = cellSlice;
|
||||
latestCellVersions[cellSlice.cell.executionEventId] = cellSlice;
|
||||
});
|
||||
|
||||
// Compute diffs between each of the previous revisions and the current revision.
|
||||
|
@ -40,7 +40,7 @@ export function buildHistoryModel(
|
|||
executionVersion.cellSlices.forEach(function (cellSlice) {
|
||||
|
||||
let cell = cellSlice.cell;
|
||||
let recentCellVersion = latestCellVersions[cell.persistentId];
|
||||
let recentCellVersion = latestCellVersions[cell.executionEventId];
|
||||
let latestText: string = "";
|
||||
if (recentCellVersion) {
|
||||
latestText = recentCellVersion.textSliceLines;
|
||||
|
@ -50,7 +50,7 @@ export function buildHistoryModel(
|
|||
let diff = computeTextDiff(latestText, thisVersionText);
|
||||
|
||||
let slicedCell: SlicedCellModel = new SlicedCellModel({
|
||||
cellPersistentId: cell.persistentId,
|
||||
executionEventId: cell.executionEventId,
|
||||
executionCount: cell.executionCount,
|
||||
sourceCode: diff.text,
|
||||
diff: diff
|
||||
|
@ -62,7 +62,7 @@ export function buildHistoryModel(
|
|||
if (includeOutput) {
|
||||
let selectedCell: ICell = null;
|
||||
executionVersion.cellSlices.map(cs => cs.cell).forEach(function (cellModel) {
|
||||
if (cellModel.persistentId == selectedCellPersistentId) {
|
||||
if (cellModel.executionEventId == selectedCellExecutionEventId) {
|
||||
selectedCell = cellModel;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -7,9 +7,9 @@ import { Diff } from '../history/diff';
|
|||
*/
|
||||
export interface ISlicedCellModel extends CodeEditor.IModel {
|
||||
/**
|
||||
* A unique ID for a cell.
|
||||
* A unique ID for a logged cell, computed at the moment of execution.
|
||||
*/
|
||||
readonly cellPersistentId: string;
|
||||
readonly cellExecutionEventId: string;
|
||||
|
||||
/**
|
||||
* The execution count for the cell.
|
||||
|
@ -38,7 +38,7 @@ export class SlicedCellModel extends CodeEditor.Model implements ISlicedCellMode
|
|||
constructor(options: SlicedCellModel.IOptions) {
|
||||
super({ modelDB: options.modelDB });
|
||||
|
||||
this._cellPersistentId = options.cellPersistentId;
|
||||
this._cellExecutionEventId = options.executionEventId;
|
||||
this._executionCount = options.executionCount;
|
||||
this._sourceCode = options.sourceCode;
|
||||
this._diff = options.diff;
|
||||
|
@ -50,8 +50,8 @@ export class SlicedCellModel extends CodeEditor.Model implements ISlicedCellMode
|
|||
/**
|
||||
* Get the cell ID.
|
||||
*/
|
||||
get cellPersistentId(): string {
|
||||
return this._cellPersistentId;
|
||||
get cellExecutionEventId(): string {
|
||||
return this._cellExecutionEventId;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -76,7 +76,7 @@ export class SlicedCellModel extends CodeEditor.Model implements ISlicedCellMode
|
|||
return this._diff;
|
||||
}
|
||||
|
||||
private _cellPersistentId: string;
|
||||
private _cellExecutionEventId: string;
|
||||
private _executionCount: number;
|
||||
private _sourceCode: string;
|
||||
private _diff:Diff;
|
||||
|
@ -93,7 +93,7 @@ export namespace SlicedCellModel {
|
|||
/**
|
||||
* A unique ID for a cell.
|
||||
*/
|
||||
cellPersistentId: string;
|
||||
executionEventId: string;
|
||||
|
||||
/**
|
||||
* The execution count for the cell.
|
||||
|
|
Загрузка…
Ссылка в новой задаче