Notebook Cells re-render upon changes to metadata (#156917)

* dataflow support for updated metadata

* update cellAttachmentRenderer.ts to reflect metadata being a getter() inside MarkupCell

* update condition to re-render cells, now includes metadata changes

* notebook cells now re-render upon metadata changes

* fix missing metadata update

Co-authored-by: Peng Lyu <penn.lv@gmail.com>
This commit is contained in:
Michael Lively 2022-08-03 09:46:12 -07:00 committed by GitHub
parent 57599a9ecd
commit 61e8687fa3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 40 additions and 32 deletions

View file

@ -22,7 +22,7 @@ export async function activate(ctx: RendererContext<void>) {
md.renderer.rules.image = (tokens: MarkdownItToken[], idx: number, options, env, self) => {
const token = tokens[idx];
const src = token.attrGet('src');
const attachments: Record<string, Record<string, string>> = env.outputItem.metadata?.custom?.attachments; // this stores attachment entries for every image in the cell
const attachments: Record<string, Record<string, string>> = env.outputItem.metadata().custom?.attachments;
if (attachments && src) {
const imageAttachment = attachments[src.replace('attachment:', '')];
if (imageAttachment) {

View file

@ -44,7 +44,7 @@ export interface CellOutputMetadata {
/**
* Metadata we store in VS Code cells.
* This contains the original metadata from the Jupyuter cells.
* This contains the original metadata from the Jupyter cells.
*/
export interface CellMetadata {
/**

View file

@ -1025,31 +1025,33 @@ var requirejs = (function() {
});
}
async showMarkupPreview(initialization: IMarkupCellInitialization) {
async showMarkupPreview(newContent: IMarkupCellInitialization) {
if (this._disposed) {
return;
}
const entry = this.markupPreviewMapping.get(initialization.cellId);
const entry = this.markupPreviewMapping.get(newContent.cellId);
if (!entry) {
return this.createMarkupPreview(initialization);
return this.createMarkupPreview(newContent);
}
const sameContent = initialization.content === entry.content;
if (!sameContent || !entry.visible) {
const sameContent = newContent.content === entry.content;
const sameMetadata = newContent.metadata === entry.metadata;
if (!sameContent || !sameMetadata || !entry.visible) {
this._sendMessageToWebview({
type: 'showMarkupCell',
id: initialization.cellId,
handle: initialization.cellHandle,
id: newContent.cellId,
handle: newContent.cellHandle,
// If the content has not changed, we still want to make sure the
// preview is visible but don't need to send anything over
content: sameContent ? undefined : initialization.content,
top: initialization.offset
content: sameContent ? undefined : newContent.content,
top: newContent.offset,
metadata: sameMetadata ? undefined : newContent.metadata
});
}
entry.content = initialization.content;
entry.offset = initialization.offset;
entry.metadata = newContent.metadata;
entry.content = newContent.content;
entry.offset = newContent.offset;
entry.visible = true;
}

View file

@ -316,6 +316,7 @@ export interface IShowMarkupCellMessage {
readonly handle: number;
readonly content: string | undefined;
readonly top: number;
readonly metadata: NotebookCellMetadata | undefined;
}
export interface IUpdateSelectedMarkupCellsMessage {

View file

@ -1035,7 +1035,7 @@ async function webviewPreloads(ctx: PreloadContext) {
break;
case 'showMarkupCell':
viewModel.showMarkupCell(event.data.id, event.data.top, event.data.content);
viewModel.showMarkupCell(event.data.id, event.data.top, event.data.content, event.data.metadata);
break;
case 'hideMarkupCells':
@ -1448,7 +1448,7 @@ async function webviewPreloads(ctx: PreloadContext) {
let cell = this._markupCells.get(info.cellId);
if (cell) {
cell.element.style.visibility = info.visible ? 'visible' : 'hidden';
await cell.updateContentAndRender(info.content);
await cell.updateContentAndRender(info.content, info.metadata);
} else {
cell = await this.createMarkupCell(info, info.offset, info.visible);
}
@ -1462,14 +1462,14 @@ async function webviewPreloads(ctx: PreloadContext) {
}
}
public async updateMarkupContent(id: string, newContent: string): Promise<void> {
public async updateMarkupContent(id: string, newContent: string, metadata: NotebookCellMetadata): Promise<void> {
const cell = this.getExpectedMarkupCell(id);
await cell?.updateContentAndRender(newContent);
await cell?.updateContentAndRender(newContent, metadata);
}
public showMarkupCell(id: string, top: number, newContent: string | undefined): void {
public showMarkupCell(id: string, top: number, newContent: string | undefined, metadata: NotebookCellMetadata | undefined): void {
const cell = this.getExpectedMarkupCell(id);
cell?.show(top, newContent);
cell?.show(top, newContent, metadata);
}
public hideMarkupCell(id: string): void {
@ -1633,11 +1633,11 @@ async function webviewPreloads(ctx: PreloadContext) {
private readonly outputItem: rendererApi.OutputItem;
/// Internal field that holds text content
private _content: { readonly value: string; readonly version: number };
private _content: { readonly value: string; readonly version: number; readonly metadata: NotebookCellMetadata };
constructor(id: string, mime: string, content: string, top: number, metadata: NotebookCellMetadata) {
this.id = id;
this._content = { value: content, version: 0 };
this._content = { value: content, version: 0, metadata: metadata };
let resolveReady: () => void;
this.ready = new Promise<void>(r => resolveReady = r);
@ -1646,7 +1646,10 @@ async function webviewPreloads(ctx: PreloadContext) {
this.outputItem = Object.freeze(<rendererApi.OutputItem>{
id,
mime,
metadata,
metadata: (): NotebookCellMetadata => {
return this._content.metadata;
},
text: (): string => {
return this._content.value;
@ -1688,7 +1691,7 @@ async function webviewPreloads(ctx: PreloadContext) {
this.addEventListeners();
this.updateContentAndRender(this._content.value).then(() => {
this.updateContentAndRender(this._content.value, this._content.metadata).then(() => {
resizeObserver.observe(this.element, this.id, false, this.id);
resolveReady();
});
@ -1738,8 +1741,8 @@ async function webviewPreloads(ctx: PreloadContext) {
});
}
public async updateContentAndRender(newContent: string): Promise<void> {
this._content = { value: newContent, version: this._content.version + 1 };
public async updateContentAndRender(newContent: string, metadata: NotebookCellMetadata): Promise<void> {
this._content = { value: newContent, version: this._content.version + 1, metadata };
await renderers.render(this.outputItem, this.element);
@ -1772,11 +1775,11 @@ async function webviewPreloads(ctx: PreloadContext) {
});
}
public show(top: number, newContent: string | undefined): void {
public show(top: number, newContent: string | undefined, metadata: NotebookCellMetadata | undefined): void {
this.element.style.visibility = 'visible';
this.element.style.top = `${top}px`;
if (typeof newContent === 'string') {
this.updateContentAndRender(newContent);
if (typeof newContent === 'string' || metadata) {
this.updateContentAndRender(newContent ?? this._content.value, metadata ?? this._content.metadata);
} else {
this.updateMarkupDimensions();
}
@ -1792,7 +1795,7 @@ async function webviewPreloads(ctx: PreloadContext) {
}
public rerender() {
this.updateContentAndRender(this._content.value);
this.updateContentAndRender(this._content.value, this._content.metadata);
}
public remove() {

View file

@ -29,8 +29,10 @@ export class MarkupCellViewModel extends BaseCellViewModel implements ICellViewM
public get renderedHtml(): string | undefined { return this._renderedHtml; }
public set renderedHtml(value: string | undefined) {
this._renderedHtml = value;
this._onDidChangeState.fire({ contentChanged: true });
if (this._renderedHtml !== value) {
this._renderedHtml = value;
this._onDidChangeState.fire({ contentChanged: true });
}
}
get layoutInfo() {