Skip to content

Commit c568315

Browse files
committed
added cell tag support for notebooks
Signed-off-by: Jonah Iden <[email protected]>
1 parent d37db57 commit c568315

File tree

6 files changed

+80
-23
lines changed

6 files changed

+80
-23
lines changed

packages/notebook/src/browser/service/notebook-cell-status-bar-service.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export interface NotebookCellStatusBarItem {
3232
readonly color?: string | ThemeColor;
3333
readonly backgroundColor?: string | ThemeColor;
3434
readonly tooltip?: string | MarkdownString;
35-
readonly command?: string | Command;
35+
readonly command?: string | (Command & { arguments?: unknown[] });
3636
readonly accessibilityInformation?: AccessibilityInformation;
3737
readonly opacity?: string;
3838
readonly onlyShowWhenActive?: boolean;
@@ -75,11 +75,11 @@ export class NotebookCellStatusBarService implements Disposable {
7575
});
7676
}
7777

78-
async getStatusBarItemsForCell(docUri: URI, cellIndex: number, viewType: string, token: CancellationToken): Promise<NotebookCellStatusBarItemList[]> {
78+
async getStatusBarItemsForCell(notebookUri: URI, cellIndex: number, viewType: string, token: CancellationToken): Promise<NotebookCellStatusBarItemList[]> {
7979
const providers = this.providers.filter(p => p.viewType === viewType || p.viewType === '*');
8080
return Promise.all(providers.map(async p => {
8181
try {
82-
return await p.provideCellStatusBarItems(docUri, cellIndex, token) ?? { items: [] };
82+
return await p.provideCellStatusBarItems(notebookUri, cellIndex, token) ?? { items: [] };
8383
} catch (e) {
8484
console.error(e);
8585
return { items: [] };

packages/notebook/src/browser/style/index.css

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,3 +508,22 @@ mark.theia-find-match.theia-find-match-selected {
508508
color: var(--theia-editor-findMatchForeground);
509509
background-color: var(--theia-editor-findMatchBackground);
510510
}
511+
512+
.cell-status-bar-item {
513+
align-items: center;
514+
display: flex;
515+
height: 16px;
516+
margin: 0 3px;
517+
overflow: hidden;
518+
padding: 0 3px;
519+
text-overflow: clip;
520+
white-space: pre;
521+
}
522+
523+
.cell-status-item-has-command {
524+
cursor: pointer;
525+
}
526+
527+
.cell-status-item-has-command:hover {
528+
background-color: var(--theia-toolbar-hoverBackground);
529+
}

packages/notebook/src/browser/view/notebook-code-cell-view.tsx

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ import { MarkdownRenderer } from '@theia/core/lib/browser/markdown-rendering/mar
3636
import { MarkdownString } from '@theia/monaco-editor-core/esm/vs/base/common/htmlContent';
3737
import { NotebookCellEditorService } from '../service/notebook-cell-editor-service';
3838
import { CellOutputWebview } from '../renderers/cell-output-webview';
39-
import { NotebookCellStatusBarItemList, NotebookCellStatusBarService } from '../service/notebook-cell-status-bar-service';
39+
import { NotebookCellStatusBarItem, NotebookCellStatusBarItemList, NotebookCellStatusBarService } from '../service/notebook-cell-status-bar-service';
40+
import { LabelParser } from '@theia/core/lib/browser/label-parser';
4041

4142
@injectable()
4243
export class NotebookCodeCellRenderer implements CellRenderer {
@@ -79,6 +80,9 @@ export class NotebookCodeCellRenderer implements CellRenderer {
7980
@inject(NotebookCellStatusBarService)
8081
protected readonly notebookCellStatusBarService: NotebookCellStatusBarService;
8182

83+
@inject(LabelParser)
84+
protected readonly labelParser: LabelParser;
85+
8286
render(notebookModel: NotebookModel, cell: NotebookCellModel, handle: number): React.ReactNode {
8387
return <div className='theia-notebook-cell-with-sidebar' ref={ref => observeCellHeight(ref, cell)}>
8488
<div className='theia-notebook-cell-editor-container'>
@@ -92,6 +96,7 @@ export class NotebookCodeCellRenderer implements CellRenderer {
9296
commandRegistry={this.commandRegistry}
9397
executionStateService={this.executionStateService}
9498
cellStatusBarService={this.notebookCellStatusBarService}
99+
labelParser={this.labelParser}
95100
onClick={() => cell.requestFocusEditor()} />
96101
</div >
97102
</div >;
@@ -189,6 +194,7 @@ export interface NotebookCodeCellStatusProps {
189194
commandRegistry: CommandRegistry;
190195
cellStatusBarService: NotebookCellStatusBarService;
191196
executionStateService?: NotebookExecutionStateService;
197+
labelParser: LabelParser;
192198
onClick: () => void;
193199
}
194200

@@ -234,13 +240,14 @@ export class NotebookCodeCellStatus extends React.Component<NotebookCodeCellStat
234240
this.forceUpdate();
235241
}));
236242

237-
this.getStatusBarItems();
238-
this.props.cellStatusBarService.onDidChangeItems(() => this.getStatusBarItems());
243+
this.updateStatusBarItems();
244+
this.props.cellStatusBarService.onDidChangeItems(() => this.updateStatusBarItems());
245+
this.props.notebook.onContentChanged(() => this.updateStatusBarItems());
239246
}
240247

241-
async getStatusBarItems(): Promise<void> {
248+
async updateStatusBarItems(): Promise<void> {
242249
this.statusBarItems = await this.props.cellStatusBarService.getStatusBarItemsForCell(
243-
this.props.cell.uri,
250+
this.props.notebook.uri,
244251
this.props.notebook.cells.indexOf(this.props.cell),
245252
this.props.notebook.viewType,
246253
CancellationToken.None);
@@ -255,7 +262,7 @@ export class NotebookCodeCellStatus extends React.Component<NotebookCodeCellStat
255262
return <div className='notebook-cell-status' onClick={() => this.props.onClick()}>
256263
<div className='notebook-cell-status-left'>
257264
{this.props.executionStateService && this.renderExecutionState()}
258-
{this.statusBarItems?.length > 0 && this.renderStatusBarItems()}
265+
{this.statusBarItems?.length && this.renderStatusBarItems()}
259266
</div>
260267
<div className='notebook-cell-status-right'>
261268
<span className='notebook-cell-language-label' onClick={() => {
@@ -265,7 +272,7 @@ export class NotebookCodeCellStatus extends React.Component<NotebookCodeCellStat
265272
</div>;
266273
}
267274

268-
private renderExecutionState(): React.ReactNode {
275+
protected renderExecutionState(): React.ReactNode {
269276
const state = this.state.currentExecution?.state;
270277
const { lastRunSuccess } = this.props.cell.internalMetadata;
271278

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

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

303-
private renderTime(ms: number): string {
310+
protected renderTime(ms: number): string {
304311
return `${(ms / 1000).toLocaleString(undefined, { maximumFractionDigits: 1, minimumFractionDigits: 1 })}s`;
305312
}
306313

307-
private renderStatusBarItems(): React.ReactNode {
314+
protected renderStatusBarItems(): React.ReactNode {
308315
return <>
309316
{
310-
this.statusBarItems.map((itemList, listIndex) =>
311-
<>{itemList.items.map((item, index) =>
312-
<span key={`${listIndex}-${index}`}>{item.text}</span>
313-
)}</>
317+
this.statusBarItems.flatMap((itemList, listIndex) =>
318+
itemList.items.map((item, index) => this.renderStatusBarItem(item, `${listIndex}-${index}`)
319+
)
314320
)
315321
}
316322
</>;
317323
}
324+
325+
protected renderStatusBarItem(item: NotebookCellStatusBarItem, key: string): React.ReactNode {
326+
const content = this.props.labelParser.parse(item.text).map(part => {
327+
if (typeof part === 'string') {
328+
return part;
329+
} else {
330+
return <span key={part.name} className={`codicon codicon-${part.name}`}></span>;
331+
}
332+
});
333+
return <div key={key} className={`cell-status-bar-item ${item.command ? 'cell-status-item-has-command' : ''}`} onClick={async () => {
334+
if (item.command) {
335+
if (typeof item.command === 'string') {
336+
this.props.commandRegistry.executeCommand(item.command);
337+
} else {
338+
this.props.commandRegistry.executeCommand(item.command.id, ...(item.command.arguments ?? []));
339+
}
340+
}
341+
}}>
342+
{content}
343+
</div>;
344+
}
345+
318346
}
319347

320348
interface NotebookCellOutputProps {

packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import { NotebookEditorFindMatch, NotebookEditorFindMatchOptions } from './noteb
3131
import * as mark from 'advanced-mark.js';
3232
import { NotebookCellEditorService } from '../service/notebook-cell-editor-service';
3333
import { NotebookCellStatusBarService } from '../service/notebook-cell-status-bar-service';
34+
import { LabelParser } from '@theia/core/lib/browser/label-parser';
3435

3536
@injectable()
3637
export class NotebookMarkdownCellRenderer implements CellRenderer {
@@ -55,6 +56,9 @@ export class NotebookMarkdownCellRenderer implements CellRenderer {
5556
@inject(NotebookCellStatusBarService)
5657
protected readonly notebookCellStatusBarService: NotebookCellStatusBarService;
5758

59+
@inject(LabelParser)
60+
protected readonly labelParser: LabelParser;
61+
5862
render(notebookModel: NotebookModel, cell: NotebookCellModel): React.ReactNode {
5963
return <MarkdownCell
6064
markdownRenderer={this.markdownRenderer}
@@ -66,6 +70,7 @@ export class NotebookMarkdownCellRenderer implements CellRenderer {
6670
notebookContextManager={this.notebookContextManager}
6771
notebookCellEditorService={this.notebookCellEditorService}
6872
notebookCellStatusBarService={this.notebookCellStatusBarService}
73+
labelParser={this.labelParser}
6974
/>;
7075
}
7176

@@ -94,10 +99,13 @@ interface MarkdownCellProps {
9499
notebookOptionsService: NotebookOptionsService;
95100
notebookCellEditorService: NotebookCellEditorService;
96101
notebookCellStatusBarService: NotebookCellStatusBarService;
102+
labelParser: LabelParser;
97103
}
98104

99105
function MarkdownCell({
100-
markdownRenderer, monacoServices, cell, notebookModel, notebookContextManager, notebookOptionsService, commandRegistry, notebookCellEditorService, notebookCellStatusBarService
106+
markdownRenderer, monacoServices, cell, notebookModel, notebookContextManager,
107+
notebookOptionsService, commandRegistry, notebookCellEditorService, notebookCellStatusBarService,
108+
labelParser
101109
}: MarkdownCellProps): React.JSX.Element {
102110
const [editMode, setEditMode] = React.useState(cell.editing);
103111
let empty = false;
@@ -155,6 +163,7 @@ function MarkdownCell({
155163
<NotebookCodeCellStatus cell={cell} notebook={notebookModel}
156164
commandRegistry={commandRegistry}
157165
cellStatusBarService={notebookCellStatusBarService}
166+
labelParser={labelParser}
158167
onClick={() => cell.requestFocusEditor()} />
159168
</div >) :
160169
(<div className='theia-notebook-markdown-content' key="markdown"

packages/plugin-ext/src/main/browser/notebooks/notebooks-main.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,8 @@ export class NotebooksMainImpl implements NotebooksMain {
106106
async $registerNotebookCellStatusBarItemProvider(handle: number, eventHandle: number | undefined, viewType: string): Promise<void> {
107107
const that = this;
108108
const provider: NotebookCellStatusBarItemProvider = {
109-
async provideCellStatusBarItems(uri: URI, index: number, token: CancellationToken): Promise<NotebookCellStatusBarItemList | undefined> {
110-
const result = await that.proxy.$provideNotebookCellStatusBarItems(handle, uri.toComponents(), index, token);
109+
async provideCellStatusBarItems(notebookUri: URI, index: number, token: CancellationToken): Promise<NotebookCellStatusBarItemList | undefined> {
110+
const result = await that.proxy.$provideNotebookCellStatusBarItems(handle, notebookUri.toComponents(), index, token);
111111
return {
112112
items: result?.items ?? [],
113113
dispose(): void {

packages/plugin-ext/src/plugin/notebook/notebooks.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,14 @@ import { CancellationToken, Disposable, DisposableCollection, Emitter, Event, UR
2222
import { URI as TheiaURI } from '../types-impl';
2323
import * as theia from '@theia/plugin';
2424
import {
25-
CommandRegistryExt, NotebookCellStatusBarListDto, NotebookDataDto,
25+
NotebookCellStatusBarListDto, NotebookDataDto,
2626
NotebookDocumentsAndEditorsDelta, NotebookDocumentShowOptions, NotebookDocumentsMain, NotebookEditorAddData, NotebookEditorsMain, NotebooksExt, NotebooksMain, Plugin,
2727
PLUGIN_RPC_CONTEXT
2828
} from '../../common';
2929
import { Cache } from '../../common/cache';
3030
import { RPCProtocol } from '../../common/rpc-protocol';
3131
import { UriComponents } from '../../common/uri-components';
32-
import { CommandsConverter } from '../command-registry';
32+
import { CommandRegistryImpl, CommandsConverter } from '../command-registry';
3333
import * as typeConverters from '../type-converters';
3434
import { BinaryBuffer } from '@theia/core/lib/common/buffer';
3535
import { Cell, NotebookDocument } from './notebook-document';
@@ -74,10 +74,11 @@ export class NotebooksExtImpl implements NotebooksExt {
7474

7575
constructor(
7676
rpc: RPCProtocol,
77-
commands: CommandRegistryExt,
77+
commands: CommandRegistryImpl,
7878
private textDocumentsAndEditors: EditorsAndDocumentsExtImpl,
7979
private textDocuments: DocumentsExtImpl,
8080
) {
81+
this.commandsConverter = commands.converter;
8182
this.notebookProxy = rpc.getProxy(PLUGIN_RPC_CONTEXT.NOTEBOOKS_MAIN);
8283
this.notebookDocumentsProxy = rpc.getProxy(PLUGIN_RPC_CONTEXT.NOTEBOOK_DOCUMENTS_MAIN);
8384
this.notebookEditors = rpc.getProxy(PLUGIN_RPC_CONTEXT.NOTEBOOK_EDITORS_MAIN);

0 commit comments

Comments
 (0)