diff --git a/packages/notebook/src/browser/contributions/cell-operations.ts b/packages/notebook/src/browser/contributions/cell-operations.ts index c77c0665f4d00..6ed1b4b5d7a7d 100644 --- a/packages/notebook/src/browser/contributions/cell-operations.ts +++ b/packages/notebook/src/browser/contributions/cell-operations.ts @@ -22,7 +22,7 @@ import { NotebookModel } from '../view-model/notebook-model'; * a collection of different reusable notbook cell operations */ -export function changeCellType(notebookModel: NotebookModel, cell: NotebookCellModel, type: CellKind): void { +export function changeCellType(notebookModel: NotebookModel, cell: NotebookCellModel, type: CellKind, language?: string): void { if (cell.cellKind === type) { return; } @@ -32,7 +32,8 @@ export function changeCellType(notebookModel: NotebookModel, cell: NotebookCellM count: 1, cells: [{ ...cell.getData(), - cellKind: type + cellKind: type, + language: language ?? cell.language }] }], true); } diff --git a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts index cafeddddde6e1..2775a4afab51c 100644 --- a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts @@ -370,18 +370,25 @@ export class NotebookCellActionContribution implements MenuContribution, Command return; } const language = await this.languageQuickPickService.pickEditorLanguage(selectedCell.language); - if (!language?.value || language.value === 'autoDetect') { + if (!language?.value || language.value === 'autoDetect' || language.value.id === selectedCell.language) { return; } - if (language.value.id === 'markdown') { - selectedCell.language = 'markdown'; - changeCellType(activeNotebook, selectedCell, CellKind.Markup); + const isMarkdownCell = selectedCell.cellKind === CellKind.Markup; + const isMarkdownLanguage = language.value.id === 'markdown'; + if (isMarkdownLanguage) { + if (!isMarkdownCell) { + changeCellType(activeNotebook, selectedCell, CellKind.Markup, language.value.id); + } } else { - this.notebookEditorWidgetService.focusedEditor?.model?.applyEdits([{ - editType: CellEditType.CellLanguage, - index: activeNotebook.cells.indexOf(selectedCell), - language: language.value.id - }], true); + if (isMarkdownCell) { + changeCellType(activeNotebook, selectedCell, CellKind.Code, language.value.id); + } else { + this.notebookEditorWidgetService.focusedEditor?.model?.applyEdits([{ + editType: CellEditType.CellLanguage, + index: activeNotebook.cells.indexOf(selectedCell), + language: language.value.id + }], true); + } } } }); diff --git a/packages/notebook/src/browser/style/index.css b/packages/notebook/src/browser/style/index.css index 12eb18739c08f..f2d45fdfd4d68 100644 --- a/packages/notebook/src/browser/style/index.css +++ b/packages/notebook/src/browser/style/index.css @@ -79,14 +79,14 @@ } /* Markdown cell edit mode */ -.theia-notebook-cell-content:has(> .theia-notebook-cell-editor) { +.theia-notebook-cell-content:has(.theia-notebook-markdown-editor-container>.theia-notebook-cell-editor) { margin-left: 37px; margin-right: var(--theia-notebook-cell-editor-margin-right); outline: 1px solid var(--theia-notebook-cellBorderColor); } /* Markdown cell edit mode focused */ -.theia-notebook-cell.focused .theia-notebook-cell-content:has(> .theia-notebook-cell-editor) { +.theia-notebook-cell.focused .theia-notebook-cell-content:has(.theia-notebook-markdown-editor-container>.theia-notebook-cell-editor) { outline-color: var(--theia-notebook-focusedEditorBorder); } diff --git a/packages/notebook/src/browser/view/notebook-code-cell-view.tsx b/packages/notebook/src/browser/view/notebook-code-cell-view.tsx index e0fa2fb41e8ca..27764fac9f7af 100644 --- a/packages/notebook/src/browser/view/notebook-code-cell-view.tsx +++ b/packages/notebook/src/browser/view/notebook-code-cell-view.tsx @@ -150,7 +150,7 @@ export interface NotebookCodeCellStatusProps { notebook: NotebookModel; cell: NotebookCellModel; commandRegistry: CommandRegistry; - executionStateService: NotebookExecutionStateService; + executionStateService?: NotebookExecutionStateService; onClick: () => void; } @@ -171,22 +171,24 @@ export class NotebookCodeCellStatus extends React.Component { - if (event.affectsCell(this.props.cell.uri)) { - this.setState({ currentExecution: event.changed, executionTime: 0 }); - clearInterval(currentInterval); - if (event.changed?.state === NotebookCellExecutionState.Executing) { - const startTime = Date.now(); - // The resolution of the time display is only a single digit after the decimal point. - // Therefore, we only need to update the display every 100ms. - currentInterval = setInterval(() => { - this.setState({ - executionTime: Date.now() - startTime - }); - }, 100); + if (props.executionStateService) { + this.toDispose.push(props.executionStateService.onDidChangeExecution(event => { + if (event.affectsCell(this.props.cell.uri)) { + this.setState({ currentExecution: event.changed, executionTime: 0 }); + clearInterval(currentInterval); + if (event.changed?.state === NotebookCellExecutionState.Executing) { + const startTime = Date.now(); + // The resolution of the time display is only a single digit after the decimal point. + // Therefore, we only need to update the display every 100ms. + currentInterval = setInterval(() => { + this.setState({ + executionTime: Date.now() - startTime + }); + }, 100); + } } - } - })); + })); + } this.toDispose.push(props.cell.onDidChangeLanguage(() => { this.forceUpdate(); @@ -200,7 +202,7 @@ export class NotebookCodeCellStatus extends React.Component this.props.onClick()}>
- {this.renderExecutionState()} + {this.props.executionStateService && this.renderExecutionState()}
{ diff --git a/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx b/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx index ac01e094460e3..65c96082f9325 100644 --- a/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx +++ b/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx @@ -23,8 +23,10 @@ import { NotebookCellModel } from '../view-model/notebook-cell-model'; import { CellEditor } from './notebook-cell-editor'; import { inject, injectable } from '@theia/core/shared/inversify'; import { MonacoEditorServices } from '@theia/monaco/lib/browser/monaco-editor'; -import { nls } from '@theia/core'; +import { CommandRegistry, nls } from '@theia/core'; import { NotebookContextManager } from '../service/notebook-context-manager'; +import { NotebookOptionsService } from '../service/notebook-options'; +import { NotebookCodeCellStatus } from './notebook-code-cell-view'; import { NotebookEditorFindMatch, NotebookEditorFindMatchOptions } from './notebook-find-widget'; import * as mark from 'advanced-mark.js'; @@ -39,9 +41,21 @@ export class NotebookMarkdownCellRenderer implements CellRenderer { @inject(NotebookContextManager) protected readonly notebookContextManager: NotebookContextManager; + @inject(CommandRegistry) + protected readonly commandRegistry: CommandRegistry; + + @inject(NotebookOptionsService) + protected readonly notebookOptionsService: NotebookOptionsService; + render(notebookModel: NotebookModel, cell: NotebookCellModel): React.ReactNode { - return ; + return ; } renderDragImage(cell: NotebookCellModel): HTMLElement { @@ -55,15 +69,19 @@ export class NotebookMarkdownCellRenderer implements CellRenderer { } interface MarkdownCellProps { - markdownRenderer: MarkdownRenderer - monacoServices: MonacoEditorServices - - cell: NotebookCellModel - notebookModel: NotebookModel - notebookContextManager: NotebookContextManager + markdownRenderer: MarkdownRenderer; + monacoServices: MonacoEditorServices; + + commandRegistry: CommandRegistry; + cell: NotebookCellModel; + notebookModel: NotebookModel; + notebookContextManager: NotebookContextManager; + notebookOptionsService: NotebookOptionsService; } -function MarkdownCell({ markdownRenderer, monacoServices, cell, notebookModel, notebookContextManager }: MarkdownCellProps): React.JSX.Element { +function MarkdownCell({ + markdownRenderer, monacoServices, cell, notebookModel, notebookContextManager, notebookOptionsService, commandRegistry +}: MarkdownCellProps): React.JSX.Element { const [editMode, setEditMode] = React.useState(cell.editing); let empty = false; @@ -111,11 +129,19 @@ function MarkdownCell({ markdownRenderer, monacoServices, cell, notebookModel, n } return editMode ? - : -
+ + cell.requestFocusEditor()} /> +
) : + (
cell.requestEdit()} ref={node => node?.replaceChildren(markdownContent)} - />; + />); } function searchInMarkdown(instance: mark, options: NotebookEditorFindMatchOptions): NotebookEditorFindMatch[] {