Skip to content

Commit

Permalink
Notebooks/basic html output support (#4058)
Browse files Browse the repository at this point in the history
Addresses #4018 

- Add basic support for HTML output, aka things like pandas dataframe
tables.
- Only show a single output element per cell output. 
- A cell output can have multiple data bundles of `mime` + `data`,
previously we would render all of them. Now we just choose one to
render, like other notebook renderers.
- Currently, this is done via a fairly arbitrary ordering of
"interesting" output types, but in the future we may be more structured
about it.

<img width="1199" alt="image"
src="https://github.com/user-attachments/assets/3e9b7e89-855c-48c0-82a3-dcf6ebe4b823">

Also includes a large shuffling of the positron instance code to better
organize it according to how other code in positron is organized with
private and public fields separated.


### QA Notes

Notebooks should continue to work as normal, just simple html outputs
will now actually render. E.g. the above table. Here's the code to
generate one:
```python
import pandas as pd
import numpy as np

# Generate random data
pd.DataFrame({
    'A': np.random.randint(0, 100, size=20),
    'B': np.random.normal(0, 1, size=20),
    'C': np.random.choice(['X', 'Y', 'Z'], size=20),
    'D': np.random.uniform(0, 1, size=20)
})
```
  • Loading branch information
nstrayer authored Jul 18, 2024
1 parent f4b44f9 commit f173405
Show file tree
Hide file tree
Showing 7 changed files with 549 additions and 375 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { ExecutionStatus, IPositronNotebookCodeCell, IPositronNotebookCell, IPositronNotebookMarkdownCell, NotebookCellOutputs } from 'vs/workbench/services/positronNotebook/browser/IPositronNotebookCell';
import { ExecutionStatus, IPositronNotebookCodeCell, IPositronNotebookCell, IPositronNotebookMarkdownCell, NotebookCellOutputs, NotebookCellOutputItem } from 'vs/workbench/services/positronNotebook/browser/IPositronNotebookCell';
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget';
import { CellSelectionType } from 'vs/workbench/services/positronNotebook/browser/selectionMachine';
import { PositronNotebookInstance } from 'vs/workbench/contrib/positronNotebook/browser/PositronNotebookInstance';
Expand All @@ -24,15 +24,12 @@ export abstract class PositronNotebookCellGeneral extends Disposable implements
private _container: HTMLElement | undefined;
private _editor: CodeEditorWidget | undefined;



constructor(
public cellModel: NotebookCellTextModel,
public _instance: PositronNotebookInstance,
@ITextModelService private readonly textModelResolverService: ITextModelService,
) {
super();

}

get uri(): URI {
Expand Down Expand Up @@ -219,4 +216,64 @@ export function createNotebookCell(cell: NotebookCellTextModel, instance: Positr
}


/**
* Get the priority of a mime type for sorting purposes
* @param mime The mime type to get the priority of
* @returns A number representing the priority of the mime type. Lower numbers are higher priority.
*/
function getMimeTypePriority(mime: string): number | null {
if (mime.includes('application')) {
return 1;
}

switch (mime) {
case 'text/html':
return 2;
case 'image/png':
return 3;
case 'text/plain':
return 4;
default:
// Dont know what this is, so mark it as special so we know something went wrong
return null;
}
}

/**
* Pick the output item with the highest priority mime type from a cell output object
* @param outputItems Array of outputs items data from a cell output object
* @returns The output item with the highest priority mime type. If there's a tie, the first one is
* returned. If there's an unknown mime type we defer to ones we do know about.
*/
export function pickPreferredOutputItem(outputItems: NotebookCellOutputItem[], logWarning: (msg: string) => void): NotebookCellOutputItem | undefined {

if (outputItems.length === 0) {
return undefined;
}

let highestPriority: number | null = null;
let preferredOutput = outputItems[0];

for (const item of outputItems) {
const priority = getMimeTypePriority(item.mime);

// If we don't know how to render any of the mime types, we'll return the first one and hope
// for the best!
if (priority === null) {
continue;
}

if (priority < (highestPriority ?? Infinity)) {
preferredOutput = item;
highestPriority = priority;
}
}

if (highestPriority === null) {
logWarning('Could not determine preferred output for notebook cell with mime types' +
outputItems.map(item => item.mime).join(', ')
);
}

return preferredOutput;
}
Loading

0 comments on commit f173405

Please sign in to comment.