Skip to content

Commit

Permalink
added cell tag support for notebooks
Browse files Browse the repository at this point in the history
Signed-off-by: Jonah Iden <jonah.iden@typefox.io>
  • Loading branch information
jonah-iden committed Oct 10, 2024
1 parent d37db57 commit c568315
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export interface NotebookCellStatusBarItem {
readonly color?: string | ThemeColor;
readonly backgroundColor?: string | ThemeColor;
readonly tooltip?: string | MarkdownString;
readonly command?: string | Command;
readonly command?: string | (Command & { arguments?: unknown[] });
readonly accessibilityInformation?: AccessibilityInformation;
readonly opacity?: string;
readonly onlyShowWhenActive?: boolean;
Expand Down Expand Up @@ -75,11 +75,11 @@ export class NotebookCellStatusBarService implements Disposable {
});
}

async getStatusBarItemsForCell(docUri: URI, cellIndex: number, viewType: string, token: CancellationToken): Promise<NotebookCellStatusBarItemList[]> {
async getStatusBarItemsForCell(notebookUri: URI, cellIndex: number, viewType: string, token: CancellationToken): Promise<NotebookCellStatusBarItemList[]> {
const providers = this.providers.filter(p => p.viewType === viewType || p.viewType === '*');
return Promise.all(providers.map(async p => {
try {
return await p.provideCellStatusBarItems(docUri, cellIndex, token) ?? { items: [] };
return await p.provideCellStatusBarItems(notebookUri, cellIndex, token) ?? { items: [] };
} catch (e) {
console.error(e);
return { items: [] };
Expand Down
19 changes: 19 additions & 0 deletions packages/notebook/src/browser/style/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -508,3 +508,22 @@ mark.theia-find-match.theia-find-match-selected {
color: var(--theia-editor-findMatchForeground);
background-color: var(--theia-editor-findMatchBackground);
}

.cell-status-bar-item {
align-items: center;
display: flex;
height: 16px;
margin: 0 3px;
overflow: hidden;
padding: 0 3px;
text-overflow: clip;
white-space: pre;
}

.cell-status-item-has-command {
cursor: pointer;
}

.cell-status-item-has-command:hover {
background-color: var(--theia-toolbar-hoverBackground);
}
56 changes: 42 additions & 14 deletions packages/notebook/src/browser/view/notebook-code-cell-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ import { MarkdownRenderer } from '@theia/core/lib/browser/markdown-rendering/mar
import { MarkdownString } from '@theia/monaco-editor-core/esm/vs/base/common/htmlContent';
import { NotebookCellEditorService } from '../service/notebook-cell-editor-service';
import { CellOutputWebview } from '../renderers/cell-output-webview';
import { NotebookCellStatusBarItemList, NotebookCellStatusBarService } from '../service/notebook-cell-status-bar-service';
import { NotebookCellStatusBarItem, NotebookCellStatusBarItemList, NotebookCellStatusBarService } from '../service/notebook-cell-status-bar-service';
import { LabelParser } from '@theia/core/lib/browser/label-parser';

@injectable()
export class NotebookCodeCellRenderer implements CellRenderer {
Expand Down Expand Up @@ -79,6 +80,9 @@ export class NotebookCodeCellRenderer implements CellRenderer {
@inject(NotebookCellStatusBarService)
protected readonly notebookCellStatusBarService: NotebookCellStatusBarService;

@inject(LabelParser)
protected readonly labelParser: LabelParser;

render(notebookModel: NotebookModel, cell: NotebookCellModel, handle: number): React.ReactNode {
return <div className='theia-notebook-cell-with-sidebar' ref={ref => observeCellHeight(ref, cell)}>
<div className='theia-notebook-cell-editor-container'>
Expand All @@ -92,6 +96,7 @@ export class NotebookCodeCellRenderer implements CellRenderer {
commandRegistry={this.commandRegistry}
executionStateService={this.executionStateService}
cellStatusBarService={this.notebookCellStatusBarService}
labelParser={this.labelParser}
onClick={() => cell.requestFocusEditor()} />
</div >
</div >;
Expand Down Expand Up @@ -189,6 +194,7 @@ export interface NotebookCodeCellStatusProps {
commandRegistry: CommandRegistry;
cellStatusBarService: NotebookCellStatusBarService;
executionStateService?: NotebookExecutionStateService;
labelParser: LabelParser;
onClick: () => void;
}

Expand Down Expand Up @@ -234,13 +240,14 @@ export class NotebookCodeCellStatus extends React.Component<NotebookCodeCellStat
this.forceUpdate();
}));

this.getStatusBarItems();
this.props.cellStatusBarService.onDidChangeItems(() => this.getStatusBarItems());
this.updateStatusBarItems();
this.props.cellStatusBarService.onDidChangeItems(() => this.updateStatusBarItems());
this.props.notebook.onContentChanged(() => this.updateStatusBarItems());
}

async getStatusBarItems(): Promise<void> {
async updateStatusBarItems(): Promise<void> {
this.statusBarItems = await this.props.cellStatusBarService.getStatusBarItemsForCell(
this.props.cell.uri,
this.props.notebook.uri,
this.props.notebook.cells.indexOf(this.props.cell),
this.props.notebook.viewType,
CancellationToken.None);
Expand All @@ -255,7 +262,7 @@ export class NotebookCodeCellStatus extends React.Component<NotebookCodeCellStat
return <div className='notebook-cell-status' onClick={() => this.props.onClick()}>
<div className='notebook-cell-status-left'>
{this.props.executionStateService && this.renderExecutionState()}
{this.statusBarItems?.length > 0 && this.renderStatusBarItems()}
{this.statusBarItems?.length && this.renderStatusBarItems()}
</div>
<div className='notebook-cell-status-right'>
<span className='notebook-cell-language-label' onClick={() => {
Expand All @@ -265,7 +272,7 @@ export class NotebookCodeCellStatus extends React.Component<NotebookCodeCellStat
</div>;
}

private renderExecutionState(): React.ReactNode {
protected renderExecutionState(): React.ReactNode {
const state = this.state.currentExecution?.state;
const { lastRunSuccess } = this.props.cell.internalMetadata;

Expand All @@ -291,7 +298,7 @@ export class NotebookCodeCellStatus extends React.Component<NotebookCodeCellStat
</>;
}

private getExecutionTime(): number {
protected getExecutionTime(): number {
const { runStartTime, runEndTime } = this.props.cell.internalMetadata;
const { executionTime } = this.state;
if (runStartTime !== undefined && runEndTime !== undefined) {
Expand All @@ -300,21 +307,42 @@ export class NotebookCodeCellStatus extends React.Component<NotebookCodeCellStat
return executionTime;
}

private renderTime(ms: number): string {
protected renderTime(ms: number): string {
return `${(ms / 1000).toLocaleString(undefined, { maximumFractionDigits: 1, minimumFractionDigits: 1 })}s`;
}

private renderStatusBarItems(): React.ReactNode {
protected renderStatusBarItems(): React.ReactNode {
return <>
{
this.statusBarItems.map((itemList, listIndex) =>
<>{itemList.items.map((item, index) =>
<span key={`${listIndex}-${index}`}>{item.text}</span>
)}</>
this.statusBarItems.flatMap((itemList, listIndex) =>
itemList.items.map((item, index) => this.renderStatusBarItem(item, `${listIndex}-${index}`)
)
)
}
</>;
}

protected renderStatusBarItem(item: NotebookCellStatusBarItem, key: string): React.ReactNode {
const content = this.props.labelParser.parse(item.text).map(part => {
if (typeof part === 'string') {
return part;
} else {
return <span key={part.name} className={`codicon codicon-${part.name}`}></span>;
}
});
return <div key={key} className={`cell-status-bar-item ${item.command ? 'cell-status-item-has-command' : ''}`} onClick={async () => {
if (item.command) {
if (typeof item.command === 'string') {
this.props.commandRegistry.executeCommand(item.command);
} else {
this.props.commandRegistry.executeCommand(item.command.id, ...(item.command.arguments ?? []));
}
}
}}>
{content}
</div>;
}

}

interface NotebookCellOutputProps {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { NotebookEditorFindMatch, NotebookEditorFindMatchOptions } from './noteb
import * as mark from 'advanced-mark.js';
import { NotebookCellEditorService } from '../service/notebook-cell-editor-service';
import { NotebookCellStatusBarService } from '../service/notebook-cell-status-bar-service';
import { LabelParser } from '@theia/core/lib/browser/label-parser';

@injectable()
export class NotebookMarkdownCellRenderer implements CellRenderer {
Expand All @@ -55,6 +56,9 @@ export class NotebookMarkdownCellRenderer implements CellRenderer {
@inject(NotebookCellStatusBarService)
protected readonly notebookCellStatusBarService: NotebookCellStatusBarService;

@inject(LabelParser)
protected readonly labelParser: LabelParser;

render(notebookModel: NotebookModel, cell: NotebookCellModel): React.ReactNode {
return <MarkdownCell
markdownRenderer={this.markdownRenderer}
Expand All @@ -66,6 +70,7 @@ export class NotebookMarkdownCellRenderer implements CellRenderer {
notebookContextManager={this.notebookContextManager}
notebookCellEditorService={this.notebookCellEditorService}
notebookCellStatusBarService={this.notebookCellStatusBarService}
labelParser={this.labelParser}
/>;
}

Expand Down Expand Up @@ -94,10 +99,13 @@ interface MarkdownCellProps {
notebookOptionsService: NotebookOptionsService;
notebookCellEditorService: NotebookCellEditorService;
notebookCellStatusBarService: NotebookCellStatusBarService;
labelParser: LabelParser;
}

function MarkdownCell({
markdownRenderer, monacoServices, cell, notebookModel, notebookContextManager, notebookOptionsService, commandRegistry, notebookCellEditorService, notebookCellStatusBarService
markdownRenderer, monacoServices, cell, notebookModel, notebookContextManager,
notebookOptionsService, commandRegistry, notebookCellEditorService, notebookCellStatusBarService,
labelParser
}: MarkdownCellProps): React.JSX.Element {
const [editMode, setEditMode] = React.useState(cell.editing);
let empty = false;
Expand Down Expand Up @@ -155,6 +163,7 @@ function MarkdownCell({
<NotebookCodeCellStatus cell={cell} notebook={notebookModel}
commandRegistry={commandRegistry}
cellStatusBarService={notebookCellStatusBarService}
labelParser={labelParser}
onClick={() => cell.requestFocusEditor()} />
</div >) :
(<div className='theia-notebook-markdown-content' key="markdown"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@ export class NotebooksMainImpl implements NotebooksMain {
async $registerNotebookCellStatusBarItemProvider(handle: number, eventHandle: number | undefined, viewType: string): Promise<void> {
const that = this;
const provider: NotebookCellStatusBarItemProvider = {
async provideCellStatusBarItems(uri: URI, index: number, token: CancellationToken): Promise<NotebookCellStatusBarItemList | undefined> {
const result = await that.proxy.$provideNotebookCellStatusBarItems(handle, uri.toComponents(), index, token);
async provideCellStatusBarItems(notebookUri: URI, index: number, token: CancellationToken): Promise<NotebookCellStatusBarItemList | undefined> {
const result = await that.proxy.$provideNotebookCellStatusBarItems(handle, notebookUri.toComponents(), index, token);
return {
items: result?.items ?? [],
dispose(): void {
Expand Down
7 changes: 4 additions & 3 deletions packages/plugin-ext/src/plugin/notebook/notebooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ import { CancellationToken, Disposable, DisposableCollection, Emitter, Event, UR
import { URI as TheiaURI } from '../types-impl';
import * as theia from '@theia/plugin';
import {
CommandRegistryExt, NotebookCellStatusBarListDto, NotebookDataDto,
NotebookCellStatusBarListDto, NotebookDataDto,
NotebookDocumentsAndEditorsDelta, NotebookDocumentShowOptions, NotebookDocumentsMain, NotebookEditorAddData, NotebookEditorsMain, NotebooksExt, NotebooksMain, Plugin,
PLUGIN_RPC_CONTEXT
} from '../../common';
import { Cache } from '../../common/cache';
import { RPCProtocol } from '../../common/rpc-protocol';
import { UriComponents } from '../../common/uri-components';
import { CommandsConverter } from '../command-registry';
import { CommandRegistryImpl, CommandsConverter } from '../command-registry';
import * as typeConverters from '../type-converters';
import { BinaryBuffer } from '@theia/core/lib/common/buffer';
import { Cell, NotebookDocument } from './notebook-document';
Expand Down Expand Up @@ -74,10 +74,11 @@ export class NotebooksExtImpl implements NotebooksExt {

constructor(
rpc: RPCProtocol,
commands: CommandRegistryExt,
commands: CommandRegistryImpl,
private textDocumentsAndEditors: EditorsAndDocumentsExtImpl,
private textDocuments: DocumentsExtImpl,
) {
this.commandsConverter = commands.converter;
this.notebookProxy = rpc.getProxy(PLUGIN_RPC_CONTEXT.NOTEBOOKS_MAIN);
this.notebookDocumentsProxy = rpc.getProxy(PLUGIN_RPC_CONTEXT.NOTEBOOK_DOCUMENTS_MAIN);
this.notebookEditors = rpc.getProxy(PLUGIN_RPC_CONTEXT.NOTEBOOK_EDITORS_MAIN);
Expand Down

0 comments on commit c568315

Please sign in to comment.