Remove the built-in markdown rendering for notebooks (#128806)
This switches our notebooks to always use contributed markdown rendering instead of our built-in markdown renderer We'd held off on switching over due to accessibility. I've tried to address this using the `aria-describedby` attributed to link the markdown container with a copy of rendered (and sanitized) html from the webview.
This commit is contained in:
parent
cf77875d5c
commit
27858a0faf
|
@ -1392,7 +1392,7 @@ const _ttpSafeInnerHtml = window.trustedTypes?.createPolicy('safeInnerHtml', {
|
|||
export function safeInnerHtml(node: HTMLElement, value: string): void {
|
||||
|
||||
const options = _extInsaneOptions({
|
||||
allowedTags: ['a', 'button', 'blockquote', 'code', 'div', 'h1', 'h2', 'h3', 'input', 'label', 'li', 'p', 'pre', 'select', 'small', 'span', 'strong', 'textarea', 'ul', 'ol'],
|
||||
allowedTags: ['a', 'button', 'blockquote', 'code', 'div', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'input', 'label', 'li', 'p', 'pre', 'select', 'small', 'span', 'strong', 'textarea', 'ul', 'ol'],
|
||||
allowedAttributes: {
|
||||
'a': ['href', 'x-dispatch'],
|
||||
'button': ['data-href', 'x-dispatch'],
|
||||
|
|
|
@ -30,7 +30,7 @@ import { NotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookEd
|
|||
import { isCompositeNotebookEditorInput, NotebookEditorInput } from 'vs/workbench/contrib/notebook/common/notebookEditorInput';
|
||||
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
|
||||
import { NotebookService } from 'vs/workbench/contrib/notebook/browser/notebookServiceImpl';
|
||||
import { CellKind, CellToolbarLocation, CellToolbarVisibility, CellUri, DisplayOrderKey, UndoRedoPerCell, ExperimentalUseMarkdownRenderer, IResolvedNotebookEditorModel, NotebookDocumentBackupData, NotebookTextDiffEditorPreview, NotebookWorkingCopyTypeIdentifier, ShowCellStatusBar, CompactView, FocusIndicator, InsertToolbarLocation, GlobalToolbar, ConsolidatedOutputButton, ShowFoldingControls, DragAndDropEnabled, NotebookCellEditorOptionsCustomizations, ConsolidatedRunButton } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { CellKind, CellToolbarLocation, CellToolbarVisibility, CellUri, DisplayOrderKey, UndoRedoPerCell, IResolvedNotebookEditorModel, NotebookDocumentBackupData, NotebookTextDiffEditorPreview, NotebookWorkingCopyTypeIdentifier, ShowCellStatusBar, CompactView, FocusIndicator, InsertToolbarLocation, GlobalToolbar, ConsolidatedOutputButton, ShowFoldingControls, DragAndDropEnabled, NotebookCellEditorOptionsCustomizations, ConsolidatedRunButton } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo';
|
||||
import { INotebookEditorModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService';
|
||||
|
@ -651,11 +651,6 @@ configurationRegistry.registerConfiguration({
|
|||
type: 'boolean',
|
||||
default: true
|
||||
},
|
||||
[ExperimentalUseMarkdownRenderer]: {
|
||||
description: nls.localize('notebook.experimental.useMarkdownRenderer.description', "Enable/disable using the new extensible markdown renderer."),
|
||||
type: 'boolean',
|
||||
default: true
|
||||
},
|
||||
[CellToolbarVisibility]: {
|
||||
markdownDescription: nls.localize('notebook.cellToolbarVisibility.description', "Whether the cell toolbar should appear on hover or click."),
|
||||
type: 'string',
|
||||
|
|
|
@ -749,7 +749,6 @@ export interface MarkdownCellRenderTemplate extends BaseCellRenderTemplate {
|
|||
foldingIndicator: HTMLElement;
|
||||
focusIndicatorBottom: HTMLElement;
|
||||
currentEditor?: ICodeEditor;
|
||||
readonly useRenderer: boolean;
|
||||
}
|
||||
|
||||
export interface CodeCellRenderTemplate extends BaseCellRenderTemplate {
|
||||
|
|
|
@ -33,7 +33,6 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView
|
|||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { ILayoutService } from 'vs/platform/layout/browser/layoutService';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { contrastBorder, diffInserted, diffRemoved, editorBackground, errorForeground, focusBorder, foreground, listInactiveSelectionBackground, registerColor, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, textBlockQuoteBackground, textBlockQuoteBorder, textLinkActiveForeground, textLinkForeground, textPreformatForeground, transparent } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
|
@ -55,11 +54,10 @@ import { NotebookEventDispatcher, NotebookLayoutChangedEvent } from 'vs/workbenc
|
|||
import { MarkupCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel';
|
||||
import { CellViewModel, IModelDecorationsChangeAccessor, INotebookEditorViewState, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
|
||||
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
|
||||
import { CellKind, ExperimentalUseMarkdownRenderer, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { CellKind, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange';
|
||||
import { editorGutterModifiedBackground } from 'vs/workbench/contrib/scm/browser/dirtydiffDecorator';
|
||||
import { Webview } from 'vs/workbench/contrib/webview/browser/webview';
|
||||
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
|
||||
import { mark } from 'vs/workbench/contrib/notebook/common/notebookPerformance';
|
||||
import { readFontInfo } from 'vs/editor/browser/config/configuration';
|
||||
import { INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService';
|
||||
|
@ -248,8 +246,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
|
|||
|
||||
private _isDisposed: boolean = false;
|
||||
|
||||
private useRenderer = false;
|
||||
|
||||
get isDisposed() {
|
||||
return this._isDisposed;
|
||||
}
|
||||
|
@ -322,8 +318,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
|
|||
constructor(
|
||||
readonly creationOptions: INotebookEditorCreationOptions,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IStorageService storageService: IStorageService,
|
||||
@IAccessibilityService accessibilityService: IAccessibilityService,
|
||||
@INotebookRendererMessagingService private readonly notebookRendererMessaging: INotebookRendererMessagingService,
|
||||
@INotebookEditorService private readonly notebookEditorService: INotebookEditorService,
|
||||
@INotebookKernelService private readonly notebookKernelService: INotebookKernelService,
|
||||
|
@ -340,7 +334,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
|
|||
this.isEmbedded = creationOptions.isEmbedded ?? false;
|
||||
this._readOnly = creationOptions.isReadOnly ?? false;
|
||||
|
||||
this.useRenderer = !!this.configurationService.getValue<boolean>(ExperimentalUseMarkdownRenderer) && !accessibilityService.isScreenReaderOptimized();
|
||||
this._notebookOptions = new NotebookOptions(this.configurationService);
|
||||
this._register(this._notebookOptions);
|
||||
this._viewContext = new ViewContext(this._notebookOptions, new NotebookEventDispatcher());
|
||||
|
@ -805,7 +798,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
|
|||
const getScopedContextKeyService = (container: HTMLElement) => this._list.contextKeyService.createScoped(container);
|
||||
const renderers = [
|
||||
this.instantiationService.createInstance(CodeCellRenderer, this, this._renderedEditors, this._dndController, getScopedContextKeyService),
|
||||
this.instantiationService.createInstance(MarkupCellRenderer, this, this._dndController, this._renderedEditors, getScopedContextKeyService, { useRenderer: this.useRenderer }),
|
||||
this.instantiationService.createInstance(MarkupCellRenderer, this, this._dndController, this._renderedEditors, getScopedContextKeyService),
|
||||
];
|
||||
|
||||
renderers.forEach(renderer => {
|
||||
|
@ -1290,11 +1283,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
|
|||
}));
|
||||
|
||||
// init rendering
|
||||
if (this.useRenderer) {
|
||||
await this._warmupWithMarkdownRenderer(this.viewModel, viewState);
|
||||
} else {
|
||||
this._list.attachViewModel(this.viewModel);
|
||||
}
|
||||
await this._warmupWithMarkdownRenderer(this.viewModel, viewState);
|
||||
|
||||
mark(textModel.uri, 'customMarkdownLoaded');
|
||||
|
||||
|
@ -2286,11 +2275,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
|
|||
}
|
||||
|
||||
async createMarkupPreview(cell: MarkupCellViewModel) {
|
||||
if (!this.useRenderer) {
|
||||
// TODO: handle case where custom renderer is disabled?
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._webview) {
|
||||
return;
|
||||
}
|
||||
|
@ -2315,11 +2299,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
|
|||
}
|
||||
|
||||
async unhideMarkupPreviews(cells: readonly MarkupCellViewModel[]) {
|
||||
if (!this.useRenderer) {
|
||||
// TODO: handle case where custom renderer is disabled?
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._webview) {
|
||||
return;
|
||||
}
|
||||
|
@ -2332,11 +2311,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
|
|||
}
|
||||
|
||||
async hideMarkupPreviews(cells: readonly MarkupCellViewModel[]) {
|
||||
if (!this.useRenderer) {
|
||||
// TODO: handle case where custom renderer is disabled?
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._webview || !cells.length) {
|
||||
return;
|
||||
}
|
||||
|
@ -2349,11 +2323,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
|
|||
}
|
||||
|
||||
async deleteMarkupPreviews(cells: readonly MarkupCellViewModel[]) {
|
||||
if (!this.useRenderer) {
|
||||
// TODO: handle case where custom renderer is disabled?
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._webview) {
|
||||
return;
|
||||
}
|
||||
|
@ -2366,7 +2335,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
|
|||
}
|
||||
|
||||
private async updateSelectedMarkdownPreviews(): Promise<void> {
|
||||
if (!this.useRenderer || !this._webview) {
|
||||
if (!this._webview) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -580,7 +580,14 @@ export class BackLayerWebView<T extends ICommonCellInfo> extends Disposable {
|
|||
this.notebookEditor.didEndDragMarkupCell(data.cellId);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'renderedMarkup':
|
||||
{
|
||||
const cell = this.notebookEditor.getCellById(data.cellId);
|
||||
if (cell instanceof MarkupCellViewModel) {
|
||||
cell.renderedHtml = data.html;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'telemetryFoundRenderedMarkdownMath':
|
||||
{
|
||||
this.telemetryService.publicLog2<{}, {}>('notebook/markdown/renderedLatex', {});
|
||||
|
|
|
@ -40,7 +40,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
|
|||
import { syncing } from 'vs/platform/theme/common/iconRegistry';
|
||||
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
import { DeleteCellAction, INotebookActionContext, INotebookCellActionContext } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions';
|
||||
import { BaseCellRenderTemplate, CellEditState, CodeCellLayoutInfo, CodeCellRenderTemplate, EXPAND_CELL_INPUT_COMMAND_ID, ICellViewModel, INotebookEditor, isCodeCellRenderTemplate, MarkdownCellRenderTemplate, NOTEBOOK_CELL_EXECUTION_STATE, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_TYPE, NOTEBOOK_EDITOR_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { BaseCellRenderTemplate, CodeCellLayoutInfo, CodeCellRenderTemplate, EXPAND_CELL_INPUT_COMMAND_ID, ICellViewModel, INotebookEditor, isCodeCellRenderTemplate, MarkdownCellRenderTemplate, NOTEBOOK_CELL_EXECUTION_STATE, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_TYPE, NOTEBOOK_EDITOR_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { errorStateIcon, successStateIcon, unfoldIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons';
|
||||
import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellActionView';
|
||||
import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys';
|
||||
|
@ -302,14 +302,11 @@ abstract class AbstractCellRenderer {
|
|||
export class MarkupCellRenderer extends AbstractCellRenderer implements IListRenderer<MarkupCellViewModel, MarkdownCellRenderTemplate> {
|
||||
static readonly TEMPLATE_ID = 'markdown_cell';
|
||||
|
||||
private readonly useRenderer: boolean;
|
||||
|
||||
constructor(
|
||||
notebookEditor: INotebookEditor,
|
||||
dndController: CellDragAndDropController,
|
||||
private renderedEditors: Map<ICellViewModel, ICodeEditor | undefined>,
|
||||
contextKeyServiceProvider: (container: HTMLElement) => IContextKeyService,
|
||||
options: { useRenderer: boolean; },
|
||||
@IConfigurationService private configurationService: IConfigurationService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
|
@ -318,7 +315,6 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen
|
|||
@INotificationService notificationService: INotificationService,
|
||||
) {
|
||||
super(instantiationService, notebookEditor, contextMenuService, menuService, configurationService, keybindingService, notificationService, contextKeyServiceProvider, 'markdown', dndController);
|
||||
this.useRenderer = options.useRenderer;
|
||||
}
|
||||
|
||||
get templateId() {
|
||||
|
@ -361,7 +357,6 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen
|
|||
const titleMenu = disposables.add(this.menuService.createMenu(this.notebookEditor.creationOptions.menuIds.cellTitleToolbar, contextKeyService));
|
||||
|
||||
const templateData: MarkdownCellRenderTemplate = {
|
||||
useRenderer: this.useRenderer,
|
||||
rootContainer,
|
||||
collapsedPart,
|
||||
expandButton,
|
||||
|
@ -386,43 +381,11 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen
|
|||
toJSON: () => { return {}; }
|
||||
};
|
||||
|
||||
if (!this.useRenderer) {
|
||||
this.dndController?.registerDragHandle(templateData, rootContainer, container, () => this.getDragImage(templateData));
|
||||
}
|
||||
|
||||
this.commonRenderTemplate(templateData);
|
||||
|
||||
return templateData;
|
||||
}
|
||||
|
||||
private getDragImage(templateData: MarkdownCellRenderTemplate): HTMLElement {
|
||||
if (templateData.currentRenderedCell?.getEditState() === CellEditState.Editing) {
|
||||
return this.getEditDragImage(templateData);
|
||||
} else {
|
||||
return this.getMarkdownDragImage(templateData);
|
||||
}
|
||||
}
|
||||
|
||||
private getMarkdownDragImage(templateData: MarkdownCellRenderTemplate): HTMLElement {
|
||||
const dragImageContainer = DOM.$('.cell-drag-image.monaco-list-row.focused.markdown-cell-row');
|
||||
DOM.reset(dragImageContainer, templateData.container.cloneNode(true));
|
||||
|
||||
// Remove all rendered content nodes after the
|
||||
const markdownContent = dragImageContainer.querySelector('.cell.markdown');
|
||||
const contentNodes = markdownContent?.children[0].children;
|
||||
if (contentNodes) {
|
||||
for (let i = contentNodes.length - 1; i >= 1; i--) {
|
||||
contentNodes.item(i)!.remove();
|
||||
}
|
||||
}
|
||||
|
||||
return dragImageContainer;
|
||||
}
|
||||
|
||||
private getEditDragImage(templateData: MarkdownCellRenderTemplate): HTMLElement {
|
||||
return new CodeCellDragImageRenderer().getDragImage(templateData, templateData.currentEditor!, 'markdown');
|
||||
}
|
||||
|
||||
renderElement(element: MarkupCellViewModel, index: number, templateData: MarkdownCellRenderTemplate, height: number | undefined): void {
|
||||
if (!this.notebookEditor.hasModel()) {
|
||||
throw new Error('The notebook editor is not attached with view model yet.');
|
||||
|
@ -511,7 +474,7 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen
|
|||
this.setBetweenCellToolbarContext(templateData, element, toolbarContext);
|
||||
|
||||
const scopedInstaService = this.instantiationService.createChild(new ServiceCollection([IContextKeyService, templateData.contextKeyService]));
|
||||
const markdownCell = scopedInstaService.createInstance(StatefulMarkdownCell, this.notebookEditor, element, templateData, cellEditorOptions.getValue(element.internalMetadata), this.renderedEditors, { useRenderer: templateData.useRenderer });
|
||||
const markdownCell = scopedInstaService.createInstance(StatefulMarkdownCell, this.notebookEditor, element, templateData, cellEditorOptions.getValue(element.internalMetadata), this.renderedEditors,);
|
||||
elementDisposables.add(markdownCell);
|
||||
elementDisposables.add(cellEditorOptions.onDidChange(newValue => markdownCell.updateEditorOptions(cellEditorOptions.getValue(element.internalMetadata))));
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { disposableTimeout, raceCancellation } from 'vs/base/common/async';
|
||||
import { CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { Disposable, DisposableStore, IDisposable, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Disposable, DisposableStore, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
|
||||
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
@ -17,100 +17,22 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
|||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
import { getResizesObserver } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets';
|
||||
import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService';
|
||||
import { collapsedIcon, expandedIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons';
|
||||
import { renderIcon } from 'vs/base/browser/ui/iconLabel/iconLabels';
|
||||
|
||||
interface IMarkdownRenderStrategy extends IDisposable {
|
||||
update(): void;
|
||||
}
|
||||
|
||||
class WebviewMarkdownRenderer extends Disposable implements IMarkdownRenderStrategy {
|
||||
constructor(
|
||||
readonly notebookEditor: IActiveNotebookEditor,
|
||||
readonly viewCell: MarkupCellViewModel
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
update(): void {
|
||||
this.notebookEditor.createMarkupPreview(this.viewCell);
|
||||
}
|
||||
}
|
||||
|
||||
class BuiltinMarkdownRenderer extends Disposable implements IMarkdownRenderStrategy {
|
||||
private readonly localDisposables = this._register(new DisposableStore());
|
||||
|
||||
constructor(
|
||||
private readonly notebookEditor: IActiveNotebookEditor,
|
||||
private readonly viewCell: MarkupCellViewModel,
|
||||
private readonly container: HTMLElement,
|
||||
private readonly markdownContainer: HTMLElement,
|
||||
private readonly editorAccessor: () => CodeEditorWidget | null
|
||||
) {
|
||||
super();
|
||||
|
||||
this._register(getResizesObserver(this.markdownContainer, undefined, () => {
|
||||
if (viewCell.getEditState() === CellEditState.Preview) {
|
||||
this.viewCell.renderedMarkdownHeight = container.clientHeight;
|
||||
}
|
||||
})).startObserving();
|
||||
}
|
||||
|
||||
update(): void {
|
||||
|
||||
const markdownRenderer = this.viewCell.getMarkdownRenderer();
|
||||
const renderedHTML = this.viewCell.getHTML();
|
||||
if (renderedHTML) {
|
||||
this.markdownContainer.appendChild(renderedHTML);
|
||||
}
|
||||
|
||||
if (this.editorAccessor()) {
|
||||
// switch from editing mode
|
||||
this.viewCell.renderedMarkdownHeight = this.container.clientHeight;
|
||||
this.relayoutCell();
|
||||
} else {
|
||||
this.localDisposables.clear();
|
||||
this.localDisposables.add(markdownRenderer.onDidRenderAsync(() => {
|
||||
if (this.viewCell.getEditState() === CellEditState.Preview) {
|
||||
this.viewCell.renderedMarkdownHeight = this.container.clientHeight;
|
||||
}
|
||||
this.relayoutCell();
|
||||
}));
|
||||
|
||||
this.localDisposables.add(this.viewCell.textBuffer.onDidChangeContent(() => {
|
||||
this.markdownContainer.innerText = '';
|
||||
this.viewCell.clearHTML();
|
||||
const renderedHTML = this.viewCell.getHTML();
|
||||
if (renderedHTML) {
|
||||
this.markdownContainer.appendChild(renderedHTML);
|
||||
}
|
||||
}));
|
||||
|
||||
this.viewCell.renderedMarkdownHeight = this.container.clientHeight;
|
||||
this.relayoutCell();
|
||||
}
|
||||
}
|
||||
|
||||
relayoutCell() {
|
||||
this.notebookEditor.layoutNotebookCell(this.viewCell, this.viewCell.layoutInfo.totalHeight);
|
||||
}
|
||||
}
|
||||
|
||||
export class StatefulMarkdownCell extends Disposable {
|
||||
|
||||
private editor: CodeEditorWidget | null = null;
|
||||
|
||||
private markdownContainer: HTMLElement;
|
||||
private markdownAccessibilityContainer: HTMLElement;
|
||||
private editorPart: HTMLElement;
|
||||
|
||||
private readonly localDisposables = this._register(new DisposableStore());
|
||||
private readonly focusSwitchDisposable = this._register(new MutableDisposable());
|
||||
private readonly editorDisposables = this._register(new DisposableStore());
|
||||
private foldingState: CellFoldingState;
|
||||
private useRenderer: boolean = false;
|
||||
private renderStrategy: IMarkdownRenderStrategy;
|
||||
|
||||
constructor(
|
||||
private readonly notebookEditor: IActiveNotebookEditor,
|
||||
|
@ -118,25 +40,28 @@ export class StatefulMarkdownCell extends Disposable {
|
|||
private readonly templateData: MarkdownCellRenderTemplate,
|
||||
private editorOptions: IEditorOptions,
|
||||
private readonly renderedEditors: Map<ICellViewModel, ICodeEditor | undefined>,
|
||||
options: { useRenderer: boolean; },
|
||||
@IContextKeyService private readonly contextKeyService: IContextKeyService,
|
||||
@INotebookCellStatusBarService readonly notebookCellStatusBarService: INotebookCellStatusBarService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
) {
|
||||
super();
|
||||
|
||||
this.markdownContainer = templateData.cellContainer;
|
||||
// Create an element that is only used to announce markup cell content to screen readers
|
||||
const id = `aria-markup-cell-${this.viewCell.id}`;
|
||||
this.markdownAccessibilityContainer = templateData.cellContainer;
|
||||
this.markdownAccessibilityContainer.id = id;
|
||||
// Hide the element from non-screen readers
|
||||
this.markdownAccessibilityContainer.style.height = '1px';
|
||||
this.markdownAccessibilityContainer.style.position = 'absolute';
|
||||
this.markdownAccessibilityContainer.style.top = '10000px';
|
||||
this.markdownAccessibilityContainer.ariaHidden = 'false';
|
||||
|
||||
this.templateData.rootContainer.setAttribute('aria-describedby', id);
|
||||
|
||||
this.editorPart = templateData.editorPart;
|
||||
this.useRenderer = options.useRenderer;
|
||||
|
||||
if (this.useRenderer) {
|
||||
this.templateData.container.classList.toggle('webview-backed-markdown-cell', true);
|
||||
this.renderStrategy = new WebviewMarkdownRenderer(this.notebookEditor, this.viewCell);
|
||||
} else {
|
||||
this.renderStrategy = new BuiltinMarkdownRenderer(this.notebookEditor, this.viewCell, this.templateData.container, this.markdownContainer, () => this.editor);
|
||||
}
|
||||
this.templateData.container.classList.toggle('webview-backed-markdown-cell', true);
|
||||
|
||||
this._register(this.renderStrategy);
|
||||
this._register(toDisposable(() => renderedEditors.delete(this.viewCell)));
|
||||
|
||||
this._register(viewCell.onDidChangeState((e) => {
|
||||
|
@ -199,11 +124,9 @@ export class StatefulMarkdownCell extends Disposable {
|
|||
}
|
||||
}));
|
||||
|
||||
if (this.useRenderer) {
|
||||
// the markdown preview's height might already be updated after the renderer calls `element.getHeight()`
|
||||
if (this.viewCell.layoutInfo.totalHeight > 0) {
|
||||
this.relayoutCell();
|
||||
}
|
||||
// the markdown preview's height might already be updated after the renderer calls `element.getHeight()`
|
||||
if (this.viewCell.layoutInfo.totalHeight > 0) {
|
||||
this.relayoutCell();
|
||||
}
|
||||
|
||||
// apply decorations
|
||||
|
@ -211,32 +134,20 @@ export class StatefulMarkdownCell extends Disposable {
|
|||
this._register(viewCell.onCellDecorationsChanged((e) => {
|
||||
e.added.forEach(options => {
|
||||
if (options.className) {
|
||||
if (this.useRenderer) {
|
||||
this.notebookEditor.deltaCellOutputContainerClassNames(this.viewCell.id, [options.className], []);
|
||||
} else {
|
||||
templateData.rootContainer.classList.add(options.className);
|
||||
}
|
||||
this.notebookEditor.deltaCellOutputContainerClassNames(this.viewCell.id, [options.className], []);
|
||||
}
|
||||
});
|
||||
|
||||
e.removed.forEach(options => {
|
||||
if (options.className) {
|
||||
if (this.useRenderer) {
|
||||
this.notebookEditor.deltaCellOutputContainerClassNames(this.viewCell.id, [], [options.className]);
|
||||
} else {
|
||||
templateData.rootContainer.classList.remove(options.className);
|
||||
}
|
||||
this.notebookEditor.deltaCellOutputContainerClassNames(this.viewCell.id, [], [options.className]);
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
viewCell.getCellDecorations().forEach(options => {
|
||||
if (options.className) {
|
||||
if (this.useRenderer) {
|
||||
this.notebookEditor.deltaCellOutputContainerClassNames(this.viewCell.id, [options.className], []);
|
||||
} else {
|
||||
templateData.rootContainer.classList.add(options.className);
|
||||
}
|
||||
this.notebookEditor.deltaCellOutputContainerClassNames(this.viewCell.id, [options.className], []);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -267,7 +178,8 @@ export class StatefulMarkdownCell extends Disposable {
|
|||
private viewUpdateCollapsed(): void {
|
||||
DOM.show(this.templateData.collapsedPart);
|
||||
DOM.hide(this.editorPart);
|
||||
DOM.hide(this.markdownContainer);
|
||||
this.markdownAccessibilityContainer.ariaHidden = 'true';
|
||||
|
||||
this.templateData.container.classList.toggle('collapsed', true);
|
||||
this.viewCell.renderedMarkdownHeight = 0;
|
||||
this.viewCell.layoutChange({});
|
||||
|
@ -278,12 +190,10 @@ export class StatefulMarkdownCell extends Disposable {
|
|||
let editorHeight: number;
|
||||
|
||||
DOM.show(this.editorPart);
|
||||
DOM.hide(this.markdownContainer);
|
||||
this.markdownAccessibilityContainer.ariaHidden = 'true';
|
||||
DOM.hide(this.templateData.collapsedPart);
|
||||
|
||||
if (this.useRenderer) {
|
||||
this.notebookEditor.hideMarkupPreviews([this.viewCell]);
|
||||
}
|
||||
this.notebookEditor.hideMarkupPreviews([this.viewCell]);
|
||||
|
||||
this.templateData.container.classList.toggle('collapsed', false);
|
||||
this.templateData.container.classList.toggle('markdown-cell-edit-mode', true);
|
||||
|
@ -371,16 +281,18 @@ export class StatefulMarkdownCell extends Disposable {
|
|||
this.viewCell.detachTextEditor();
|
||||
DOM.hide(this.editorPart);
|
||||
DOM.hide(this.templateData.collapsedPart);
|
||||
DOM.show(this.markdownContainer);
|
||||
this.markdownAccessibilityContainer.ariaHidden = 'false';
|
||||
this.templateData.container.classList.toggle('collapsed', false);
|
||||
this.templateData.container.classList.toggle('markdown-cell-edit-mode', false);
|
||||
|
||||
this.renderedEditors.delete(this.viewCell);
|
||||
|
||||
this.markdownContainer.innerText = '';
|
||||
this.viewCell.clearHTML();
|
||||
this.markdownAccessibilityContainer.innerText = '';
|
||||
if (this.viewCell.renderedHtml) {
|
||||
DOM.safeInnerHtml(this.markdownAccessibilityContainer, this.viewCell.renderedHtml);
|
||||
}
|
||||
|
||||
this.renderStrategy.update();
|
||||
this.notebookEditor.createMarkupPreview(this.viewCell);
|
||||
}
|
||||
|
||||
private focusEditorIfNeeded() {
|
||||
|
|
|
@ -129,6 +129,12 @@ export interface IInitializedMarkupMessage extends BaseToWebviewMessage {
|
|||
readonly type: 'initializedMarkup';
|
||||
}
|
||||
|
||||
export interface IRenderedMarkupMessage extends BaseToWebviewMessage {
|
||||
readonly type: 'renderedMarkup';
|
||||
readonly cellId: string;
|
||||
readonly html: string;
|
||||
}
|
||||
|
||||
export interface ITelemetryFoundRenderedMarkdownMath extends BaseToWebviewMessage {
|
||||
readonly type: 'telemetryFoundRenderedMarkdownMath';
|
||||
}
|
||||
|
@ -342,6 +348,7 @@ export type FromWebviewMessage = WebviewIntialized |
|
|||
ICellDropMessage |
|
||||
ICellDragEndMessage |
|
||||
IInitializedMarkupMessage |
|
||||
IRenderedMarkupMessage |
|
||||
ITelemetryFoundRenderedMarkdownMath |
|
||||
ITelemetryFoundUnrenderedMarkdownMath;
|
||||
|
||||
|
|
|
@ -1130,6 +1130,27 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re
|
|||
}
|
||||
}
|
||||
|
||||
const root = (this.element.shadowRoot ?? this.element);
|
||||
const html = [];
|
||||
for (const child of root.children) {
|
||||
switch (child.tagName) {
|
||||
case 'LINK':
|
||||
case 'SCRIPT':
|
||||
case 'STYLE':
|
||||
// not worth sending over since it will be stripped before rendering
|
||||
break;
|
||||
|
||||
default:
|
||||
html.push(child.outerHTML);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
postNotebookMessage<webviewMessages.IRenderedMarkupMessage>('renderedMarkup', {
|
||||
cellId: this.id,
|
||||
html: html.join(''),
|
||||
});
|
||||
|
||||
dimensionUpdater.updateHeight(this.id, this.element.offsetHeight, {
|
||||
isOutput: false
|
||||
});
|
||||
|
|
|
@ -6,11 +6,9 @@
|
|||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import * as UUID from 'vs/base/common/uuid';
|
||||
import * as editorCommon from 'vs/editor/common/editorCommon';
|
||||
import * as nls from 'vs/nls';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { EditorFoldingStateDelegate } from 'vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel';
|
||||
import { CellEditState, CellFindMatch, ICellOutputViewModel, ICellViewModel, MarkdownCellLayoutChangeEvent, MarkdownCellLayoutInfo, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { MarkdownRenderer } from 'vs/editor/browser/core/markdownRenderer';
|
||||
import { BaseCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel';
|
||||
import { NotebookCellStateChangedEvent } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher';
|
||||
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
|
||||
|
@ -18,15 +16,21 @@ import { CellKind, INotebookSearchOptions } from 'vs/workbench/contrib/notebook/
|
|||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/viewContext';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { dirname } from 'vs/base/common/resources';
|
||||
|
||||
export class MarkupCellViewModel extends BaseCellViewModel implements ICellViewModel {
|
||||
|
||||
readonly cellKind = CellKind.Markup;
|
||||
|
||||
private _html: HTMLElement | null = null;
|
||||
private _layoutInfo: MarkdownCellLayoutInfo;
|
||||
|
||||
private _renderedHtml?: string;
|
||||
|
||||
public get renderedHtml(): string | undefined { return this._renderedHtml; }
|
||||
public set renderedHtml(value: string | undefined) {
|
||||
this._renderedHtml = value;
|
||||
this._onDidChangeState.fire({ contentChanged: true });
|
||||
}
|
||||
|
||||
get layoutInfo() {
|
||||
return this._layoutInfo;
|
||||
}
|
||||
|
@ -101,8 +105,6 @@ export class MarkupCellViewModel extends BaseCellViewModel implements ICellViewM
|
|||
private readonly _onDidHideInput = new Emitter<void>();
|
||||
readonly onDidHideInput = this._onDidHideInput.event;
|
||||
|
||||
private readonly _mdRenderer: MarkdownRenderer;
|
||||
|
||||
constructor(
|
||||
viewType: string,
|
||||
model: NotebookCellTextModel,
|
||||
|
@ -115,8 +117,6 @@ export class MarkupCellViewModel extends BaseCellViewModel implements ICellViewM
|
|||
) {
|
||||
super(viewType, model, UUID.generateUuid(), viewContext, configurationService, textModelService);
|
||||
|
||||
this._mdRenderer = this._register(instantiationService.createInstance(MarkdownRenderer, { baseUrl: dirname(model.uri) }));
|
||||
|
||||
const { bottomToolbarGap } = this.viewContext.notebookOptions.computeBottomToolbarDimensions(this.viewType);
|
||||
|
||||
this._layoutInfo = {
|
||||
|
@ -246,43 +246,13 @@ export class MarkupCellViewModel extends BaseCellViewModel implements ICellViewM
|
|||
}
|
||||
}
|
||||
|
||||
clearHTML() {
|
||||
this._html = null;
|
||||
}
|
||||
|
||||
getHTML(): HTMLElement | null {
|
||||
if (this.cellKind === CellKind.Markup) {
|
||||
if (this._html) {
|
||||
return this._html;
|
||||
}
|
||||
const renderer = this.getMarkdownRenderer();
|
||||
const text = this.getText();
|
||||
|
||||
if (text.length === 0) {
|
||||
const el = document.createElement('p');
|
||||
el.className = 'emptyMarkdownPlaceholder';
|
||||
el.innerText = nls.localize('notebook.emptyMarkdownPlaceholder', "Empty markdown cell, double click or press enter to edit.");
|
||||
this._html = el;
|
||||
} else {
|
||||
this._html = renderer.render({ value: this.getText(), isTrusted: true }, undefined, { gfm: true }).element;
|
||||
}
|
||||
|
||||
return this._html;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected onDidChangeTextModelContent(): void {
|
||||
this._html = null;
|
||||
this._onDidChangeState.fire({ contentChanged: true });
|
||||
}
|
||||
|
||||
onDeselect() {
|
||||
}
|
||||
|
||||
getMarkdownRenderer() {
|
||||
return this._mdRenderer;
|
||||
}
|
||||
|
||||
private readonly _hasFindResult = this._register(new Emitter<boolean>());
|
||||
public readonly hasFindResult: Event<boolean> = this._hasFindResult.event;
|
||||
|
|
|
@ -838,7 +838,6 @@ export const CellToolbarVisibility = 'notebook.cellToolbarVisibility';
|
|||
export type ShowCellStatusBarType = 'hidden' | 'visible' | 'visibleAfterExecute';
|
||||
export const ShowCellStatusBar = 'notebook.showCellStatusBar';
|
||||
export const NotebookTextDiffEditorPreview = 'notebook.diff.enablePreview';
|
||||
export const ExperimentalUseMarkdownRenderer = 'notebook.experimental.useMarkdownRenderer';
|
||||
export const ExperimentalInsertToolbarAlignment = 'notebook.experimental.insertToolbarAlignment';
|
||||
export const CompactView = 'notebook.compactView';
|
||||
export const FocusIndicator = 'notebook.cellFocusIndicator';
|
||||
|
|
Loading…
Reference in a new issue