diff --git a/packages/nbdime/src/diff/model/cell.ts b/packages/nbdime/src/diff/model/cell.ts index 4dbed206..9ce136d7 100644 --- a/packages/nbdime/src/diff/model/cell.ts +++ b/packages/nbdime/src/diff/model/cell.ts @@ -43,12 +43,14 @@ export class CellDiffModel { metadata: IStringDiffModel, outputs: OutputDiffModel[] | null, executionCount: ImmutableDiffModel | null, - cellType: string) { + cellType: string, + cellId: ImmutableDiffModel) { this.source = source; this.metadata = metadata; this.outputs = outputs; this.executionCount = executionCount; this.cellType = cellType; + this.cellId = cellId; if (outputs === null && cellType === 'code') { throw new NotifyUserError('Invalid code cell, missing outputs!'); } @@ -88,6 +90,10 @@ export class CellDiffModel { */ cellType: string; + /** + * Diff model for the cell identifier. + */ + cellId: ImmutableDiffModel; /** * Whether the cell has remained unchanged @@ -195,7 +201,10 @@ function createPatchedCellDiffModel( // Pass base as remote, which means fall back to unchanged if no diff: executionCount = createImmutableModel(execBase, execBase, execDiff); } - return new CellDiffModel(source, metadata, outputs, executionCount, base.cell_type); + let idBase = base.id as string | undefined; + let idDiff = getDiffEntryByKey(diff, 'id') as IDiffReplace | null; + const idModel = createImmutableModel(idBase, idBase, idDiff); + return new CellDiffModel(source, metadata, outputs, executionCount, base.cell_type, idModel); } export @@ -215,7 +224,9 @@ function createUnchangedCellDiffModel( } else { // markdown or raw cell } - return new CellDiffModel(source, metadata, outputs, executionCount, base.cell_type); + let idBase = base.id as string | undefined; + const idModel = createImmutableModel(idBase, idBase); + return new CellDiffModel(source, metadata, outputs, executionCount, base.cell_type, idModel); } export @@ -231,7 +242,9 @@ function createAddedCellDiffModel( null, remote.outputs); executionCount = createImmutableModel(null, remote.execution_count); } - return new CellDiffModel(source, metadata, outputs, executionCount, remote.cell_type); + let idRemote = remote.id as string | undefined; + const idModel = createImmutableModel(null, idRemote); + return new CellDiffModel(source, metadata, outputs, executionCount, remote.cell_type, idModel); } export @@ -247,5 +260,7 @@ function createDeletedCellDiffModel( let execBase = base.execution_count; executionCount = createImmutableModel(execBase, null); } - return new CellDiffModel(source, metadata, outputs, executionCount, base.cell_type); + let idBase = base.id as string | undefined; + const idModel = createImmutableModel(idBase, null); + return new CellDiffModel(source, metadata, outputs, executionCount, base.cell_type, idModel); } diff --git a/packages/nbdime/src/diff/model/immutable.ts b/packages/nbdime/src/diff/model/immutable.ts index d4db808d..9b712221 100644 --- a/packages/nbdime/src/diff/model/immutable.ts +++ b/packages/nbdime/src/diff/model/immutable.ts @@ -12,7 +12,7 @@ import type { -export type ImmutableValue = number | boolean | null; +export type ImmutableValue = string | number | boolean | null; /** diff --git a/packages/nbdime/src/diff/widget/cell.ts b/packages/nbdime/src/diff/widget/cell.ts index 12a7c0c9..62b4eb35 100644 --- a/packages/nbdime/src/diff/widget/cell.ts +++ b/packages/nbdime/src/diff/widget/cell.ts @@ -53,7 +53,9 @@ const CELLDIFF_CLASS = 'jp-Cell-diff'; export const OUTPUTS_DIFF_CLASS = 'jp-Diff-outputsContainer'; -const EXECUTIONCOUNT_ROW_CLASS = 'jp-Cellrow-executionCount'; +const EXECUTION_COUNT_CLASS = 'jp-Cellrow-header-executionCount'; +const CELL_ID_CLASS = 'jp-Cellrow-header-cellId'; +const HEADER_ROW_CLASS = 'jp-Cellrow-header'; const SOURCE_ROW_CLASS = 'jp-Cellrow-source'; const METADATA_ROW_CLASS = 'jp-Cellrow-metadata'; const OUTPUTS_ROW_CLASS = 'jp-Cellrow-outputs'; @@ -100,10 +102,41 @@ class CellDiffWidget extends Panel { let sourceView = CellDiffWidget.createView( model.source, model, CURR_DIFF_CLASSES, this._rendermime); sourceView.addClass(SOURCE_ROW_CLASS); - if (model.executionCount) { - sourceView.insertWidget(0, CellDiffWidget.createPrompts( - model.executionCount, model)); + + if (model.executionCount || model.cellId) { + const createWidget = (text: string): Widget => { + let w = new Widget(); + w.node.innerText = text; + return w; + } + const header = CellDiffWidget.createHeader(); + FlexPanel.setGrow(header, 1); + sourceView.insertWidget(0, header); + + const prompts = model.executionCount ? CellDiffWidget.createPrompts( + model.executionCount, model) : {base: null, remote: null} + const ids = model.cellId ? CellDiffWidget.createIdentifiers( + model.cellId, model) : {base: null, remote: null} + + const views: ('base' | 'remote')[] = ['base', 'remote']; + for (let side of views) { + const prompt = prompts[side]; + const id = ids[side]; + if (model.executionCount && prompt !== null) { + let w = createWidget(prompt); + w.addClass(PROMPT_CLASS); + w.addClass(EXECUTION_COUNT_CLASS); + header.addWidget(w); + } + if (model.cellId && id !== null) { + let w = createWidget(`Cell ID: ${id}`); + w.addClass(CELL_ID_CLASS); + FlexPanel.setGrow(w, 1); + header.addWidget(w); + } + } } + this.addWidget(sourceView); if (!model.metadata.unchanged) { @@ -157,28 +190,33 @@ class CellDiffWidget extends Panel { } } - static createPrompts(model: ImmutableDiffModel, parent: CellDiffModel): Panel { - let prompts: string[] = []; + static createHeader(): Panel { + let container = new FlexPanel({direction: 'left-to-right'}); + container.addClass(HEADER_ROW_CLASS); + return container; + } + + static createPrompts(model: ImmutableDiffModel, parent: CellDiffModel): Record<'base' | 'remote', string | null> { + const prompts: Record<'base' | 'remote', string | null> = { + base: null, + remote: null + } if (!parent.added) { let base = model.base as number | null; - let baseStr = `In [${base || ' '}]:`; - prompts.push(baseStr); + prompts.base = `In [${base || ' '}]:`; } if (!parent.unchanged && !parent.deleted) { let remote = model.remote as number | null; - let remoteStr = `In [${remote || ' '}]:`; - prompts.push(remoteStr); + prompts.remote = `In [${remote || ' '}]:`; } - let container = new FlexPanel({direction: 'left-to-right'}); - for (let text of prompts) { - let w = new Widget(); - w.node.innerText = text; - w.addClass(PROMPT_CLASS); - container.addWidget(w); - FlexPanel.setGrow(w, 1); - } - container.addClass(EXECUTIONCOUNT_ROW_CLASS); - return container; + return prompts; + } + + static createIdentifiers(model: ImmutableDiffModel, parent: CellDiffModel): Record<'base' | 'remote', string | null> { + return { + base: model.base as string | null, + remote: model.remote as string | null + }; } /** diff --git a/packages/nbdime/src/merge/model/cell.ts b/packages/nbdime/src/merge/model/cell.ts index 5f83b6d7..f34a220a 100644 --- a/packages/nbdime/src/merge/model/cell.ts +++ b/packages/nbdime/src/merge/model/cell.ts @@ -115,7 +115,8 @@ function createPatchedCellDecisionDiffModel( } - return new CellDiffModel(source, metadata, outputs, executionCount, base.cell_type); + let idModel = createImmutableModel(base.id ? (base.id as string) : null, null); + return new CellDiffModel(source, metadata, outputs, executionCount, base.cell_type, idModel); } diff --git a/packages/nbdime/src/styles/diff.css b/packages/nbdime/src/styles/diff.css index 4b31500f..672797d9 100644 --- a/packages/nbdime/src/styles/diff.css +++ b/packages/nbdime/src/styles/diff.css @@ -29,11 +29,20 @@ border: none; } -/* We can float prompt left when unchanged */ -.jp-Notebook-diff .jp-Diff-unchanged .jp-Cellrow-executionCount { +/* When unchanged hide cell ids and float the execution count left */ +.jp-Notebook-diff .jp-Diff-unchanged .jp-Cellrow-header-cellId { + display: none; +} + +.jp-Notebook-diff .jp-Diff-unchanged .jp-Cellrow-header { float: left; } +.jp-Notebook-diff .jp-Cellrow-header-cellId { + align-self: center; + text-align: center; + color: var(--jp-ui-font-color2); +} .jp-Notebook-diff .jp-Metadata-diff { margin-bottom: 20px; @@ -178,8 +187,9 @@ width: 100%; } -.jp-Notebook-diff .jp-Cellrow-source .jp-InputPrompt { +.jp-Notebook-diff .jp-Cellrow-header > .jp-InputPrompt { text-align: left; + flex-basis: min-content; } .jp-CollapsiblePanel-container {