提交 1b3b4026 编写于 作者: R rebornix

trusted notebook metadata

上级 4d4f3660
...@@ -21,9 +21,9 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation ...@@ -21,9 +21,9 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { IQuickInputService, IQuickPickItem, QuickPickInput } from 'vs/platform/quickinput/common/quickInput'; import { IQuickInputService, IQuickPickItem, QuickPickInput } from 'vs/platform/quickinput/common/quickInput';
import { CATEGORIES } from 'vs/workbench/common/actions'; import { CATEGORIES } from 'vs/workbench/common/actions';
import { BaseCellRenderTemplate, CellEditState, CellFocusMode, EXPAND_CELL_CONTENT_COMMAND_ID, ICellViewModel, INotebookEditor, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_EDITOR_FOCUSED, NOTEBOOK_CELL_HAS_OUTPUTS, NOTEBOOK_CELL_INPUT_COLLAPSED, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_OUTPUT_COLLAPSED, NOTEBOOK_CELL_TYPE, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE, NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_OUTPUT_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { BaseCellRenderTemplate, CellEditState, CellFocusMode, EXECUTE_CELL_COMMAND_ID, EXPAND_CELL_CONTENT_COMMAND_ID, ICellViewModel, INotebookEditor, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_EDITOR_FOCUSED, NOTEBOOK_CELL_HAS_OUTPUTS, NOTEBOOK_CELL_INPUT_COLLAPSED, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_OUTPUT_COLLAPSED, NOTEBOOK_CELL_TYPE, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE, NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_OUTPUT_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
import { CellEditType, CellKind, ICellEditOperation, ICellRange, isDocumentExcludePattern, NotebookCellMetadata, NotebookCellRunState, NOTEBOOK_EDITOR_CURSOR_BEGIN_END, NOTEBOOK_EDITOR_CURSOR_BOUNDARY, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { CellEditType, CellKind, ICellEditOperation, ICellRange, isDocumentExcludePattern, NotebookCellMetadata, NotebookCellRunState, NotebookDocumentMetadata, NOTEBOOK_EDITOR_CURSOR_BEGIN_END, NOTEBOOK_EDITOR_CURSOR_BOUNDARY, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
...@@ -37,6 +37,7 @@ const NOTEBOOK_FOCUS_PREVIOUS_EDITOR = 'notebook.focusPreviousEditor'; ...@@ -37,6 +37,7 @@ const NOTEBOOK_FOCUS_PREVIOUS_EDITOR = 'notebook.focusPreviousEditor';
const NOTEBOOK_FOCUS_NEXT_EDITOR = 'notebook.focusNextEditor'; const NOTEBOOK_FOCUS_NEXT_EDITOR = 'notebook.focusNextEditor';
const CLEAR_ALL_CELLS_OUTPUTS_COMMAND_ID = 'notebook.clearAllCellsOutputs'; const CLEAR_ALL_CELLS_OUTPUTS_COMMAND_ID = 'notebook.clearAllCellsOutputs';
const RENDER_ALL_MARKDOWN_CELLS = 'notebook.renderAllMarkdownCells'; const RENDER_ALL_MARKDOWN_CELLS = 'notebook.renderAllMarkdownCells';
const TRUST_NOTEBOOK = 'notebook.trust';
// Cell Commands // Cell Commands
const INSERT_CODE_CELL_ABOVE_COMMAND_ID = 'notebook.cell.insertCodeCellAbove'; const INSERT_CODE_CELL_ABOVE_COMMAND_ID = 'notebook.cell.insertCodeCellAbove';
...@@ -64,7 +65,6 @@ const SPLIT_CELL_COMMAND_ID = 'notebook.cell.split'; ...@@ -64,7 +65,6 @@ const SPLIT_CELL_COMMAND_ID = 'notebook.cell.split';
const JOIN_CELL_ABOVE_COMMAND_ID = 'notebook.cell.joinAbove'; const JOIN_CELL_ABOVE_COMMAND_ID = 'notebook.cell.joinAbove';
const JOIN_CELL_BELOW_COMMAND_ID = 'notebook.cell.joinBelow'; const JOIN_CELL_BELOW_COMMAND_ID = 'notebook.cell.joinBelow';
const EXECUTE_CELL_COMMAND_ID = 'notebook.cell.execute';
const CANCEL_CELL_COMMAND_ID = 'notebook.cell.cancelExecution'; const CANCEL_CELL_COMMAND_ID = 'notebook.cell.cancelExecution';
const EXECUTE_CELL_SELECT_BELOW = 'notebook.cell.executeAndSelectBelow'; const EXECUTE_CELL_SELECT_BELOW = 'notebook.cell.executeAndSelectBelow';
const EXECUTE_CELL_INSERT_BELOW = 'notebook.cell.executeAndInsertBelow'; const EXECUTE_CELL_INSERT_BELOW = 'notebook.cell.executeAndInsertBelow';
...@@ -1824,6 +1824,34 @@ registerAction2(class extends ChangeNotebookCellMetadataAction { ...@@ -1824,6 +1824,34 @@ registerAction2(class extends ChangeNotebookCellMetadataAction {
} }
}); });
abstract class ChangeNotebookMetadataAction extends NotebookCellAction {
async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise<void> {
const textModel = context.notebookEditor.viewModel?.notebookDocument;
if (!textModel) {
return;
}
textModel.applyEdits(textModel.versionId, [{ editType: CellEditType.DocumentMetadata, metadata: { ...textModel.metadata, ...this.getMetadataDelta() } }], true, undefined, () => undefined, undefined);
}
abstract getMetadataDelta(): Partial<NotebookDocumentMetadata>;
}
registerAction2(class extends ChangeNotebookMetadataAction {
constructor() {
super({
id: TRUST_NOTEBOOK,
title: localize('notebook.trust', "Trust Notebook"),
});
}
getMetadataDelta(): Partial<NotebookDocumentMetadata> {
return { trusted: true };
}
});
registerAction2(class extends Action2 { registerAction2(class extends Action2 {
constructor() { constructor() {
super({ super({
......
...@@ -60,6 +60,7 @@ export const NOTEBOOK_CELL_OUTPUT_COLLAPSED = new RawContextKey<boolean>('notebo ...@@ -60,6 +60,7 @@ export const NOTEBOOK_CELL_OUTPUT_COLLAPSED = new RawContextKey<boolean>('notebo
// Shared commands // Shared commands
export const EXPAND_CELL_CONTENT_COMMAND_ID = 'notebook.cell.expandCellContent'; export const EXPAND_CELL_CONTENT_COMMAND_ID = 'notebook.cell.expandCellContent';
export const EXECUTE_CELL_COMMAND_ID = 'notebook.cell.execute';
// Kernels // Kernels
......
...@@ -873,7 +873,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor ...@@ -873,7 +873,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
private _updateForMetadata(): void { private _updateForMetadata(): void {
const notebookMetadata = this.viewModel!.metadata; const notebookMetadata = this.viewModel!.metadata;
this._editorEditable?.set(!!notebookMetadata?.editable); this._editorEditable?.set(!!notebookMetadata?.editable);
this._editorRunnable?.set(!!notebookMetadata?.runnable); this._editorRunnable?.set(this.viewModel!.runnable);
this._overflowContainer.classList.toggle('notebook-editor-editable', !!notebookMetadata?.editable); this._overflowContainer.classList.toggle('notebook-editor-editable', !!notebookMetadata?.editable);
this.getDomNode().classList.toggle('notebook-editor-editable', !!notebookMetadata?.editable); this.getDomNode().classList.toggle('notebook-editor-editable', !!notebookMetadata?.editable);
...@@ -1621,7 +1621,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor ...@@ -1621,7 +1621,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
} }
async executeNotebook(): Promise<void> { async executeNotebook(): Promise<void> {
if (!this._notebookViewModel!.metadata.runnable) { if (!this._notebookViewModel!.runnable) {
return; return;
} }
......
...@@ -28,7 +28,7 @@ import { NotebookKernelProviderAssociationRegistry, NotebookViewTypesExtensionRe ...@@ -28,7 +28,7 @@ import { NotebookKernelProviderAssociationRegistry, NotebookViewTypesExtensionRe
import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, BUILTIN_RENDERER_ID, CellKind, CellOutputKind, DisplayOrderKey, IDisplayOutput, INotebookDecorationRenderOptions, INotebookKernelInfo2, INotebookKernelProvider, INotebookRendererInfo, INotebookTextModel, IOrderedMimeType, ITransformedDisplayOutputDto, mimeTypeSupportedByCore, notebookDocumentFilterMatch, NotebookEditorPriority, NOTEBOOK_DISPLAY_ORDER, RENDERER_NOT_AVAILABLE, sortMimeTypes } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, BUILTIN_RENDERER_ID, CellKind, CellOutputKind, DisplayOrderKey, IDisplayOutput, INotebookDecorationRenderOptions, INotebookKernelInfo2, INotebookKernelProvider, INotebookRendererInfo, INotebookTextModel, IOrderedMimeType, ITransformedDisplayOutputDto, mimeTypeIsAlwaysSecure, mimeTypeSupportedByCore, notebookDocumentFilterMatch, NotebookEditorPriority, NOTEBOOK_DISPLAY_ORDER, RENDERER_NOT_AVAILABLE, sortMimeTypes } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { NotebookOutputRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookOutputRenderer'; import { NotebookOutputRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookOutputRenderer';
import { NotebookEditorDescriptor, NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider'; import { NotebookEditorDescriptor, NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider';
import { IMainNotebookController, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { IMainNotebookController, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
...@@ -732,10 +732,10 @@ export class NotebookService extends Disposable implements INotebookService, ICu ...@@ -732,10 +732,10 @@ export class NotebookService extends Disposable implements INotebookService, ICu
getMimeTypeInfo(textModel: NotebookTextModel, output: ITransformedDisplayOutputDto): readonly IOrderedMimeType[] { getMimeTypeInfo(textModel: NotebookTextModel, output: ITransformedDisplayOutputDto): readonly IOrderedMimeType[] {
// TODO@rebornix no string[] casting // TODO@rebornix no string[] casting
return this._getOrderedMimeTypes(output, textModel.metadata.displayOrder as string[] ?? []); return this._getOrderedMimeTypes(textModel, output, textModel.metadata.displayOrder as string[] ?? []);
} }
private _getOrderedMimeTypes(output: IDisplayOutput, documentDisplayOrder: string[]): IOrderedMimeType[] { private _getOrderedMimeTypes(textModel: NotebookTextModel, output: IDisplayOutput, documentDisplayOrder: string[]): IOrderedMimeType[] {
const mimeTypes = Object.keys(output.data); const mimeTypes = Object.keys(output.data);
const coreDisplayOrder = this._displayOrder; const coreDisplayOrder = this._displayOrder;
const sorted = sortMimeTypes(mimeTypes, coreDisplayOrder?.userOrder || [], documentDisplayOrder, coreDisplayOrder?.defaultOrder || []); const sorted = sortMimeTypes(mimeTypes, coreDisplayOrder?.userOrder || [], documentDisplayOrder, coreDisplayOrder?.defaultOrder || []);
...@@ -751,31 +751,36 @@ export class NotebookService extends Disposable implements INotebookService, ICu ...@@ -751,31 +751,36 @@ export class NotebookService extends Disposable implements INotebookService, ICu
orderMimeTypes.push({ orderMimeTypes.push({
mimeType: mimeType, mimeType: mimeType,
rendererId: handler.id, rendererId: handler.id,
isTrusted: textModel.metadata.trusted
}); });
for (let i = 1; i < handlers.length; i++) { for (let i = 1; i < handlers.length; i++) {
orderMimeTypes.push({ orderMimeTypes.push({
mimeType: mimeType, mimeType: mimeType,
rendererId: handlers[i].id rendererId: handlers[i].id,
isTrusted: textModel.metadata.trusted
}); });
} }
if (mimeTypeSupportedByCore(mimeType)) { if (mimeTypeSupportedByCore(mimeType)) {
orderMimeTypes.push({ orderMimeTypes.push({
mimeType: mimeType, mimeType: mimeType,
rendererId: BUILTIN_RENDERER_ID rendererId: BUILTIN_RENDERER_ID,
isTrusted: mimeTypeIsAlwaysSecure(mimeType) || textModel.metadata.trusted
}); });
} }
} else { } else {
if (mimeTypeSupportedByCore(mimeType)) { if (mimeTypeSupportedByCore(mimeType)) {
orderMimeTypes.push({ orderMimeTypes.push({
mimeType: mimeType, mimeType: mimeType,
rendererId: BUILTIN_RENDERER_ID rendererId: BUILTIN_RENDERER_ID,
isTrusted: mimeTypeIsAlwaysSecure(mimeType) || textModel.metadata.trusted
}); });
} else { } else {
orderMimeTypes.push({ orderMimeTypes.push({
mimeType: mimeType, mimeType: mimeType,
rendererId: RENDERER_NOT_AVAILABLE rendererId: RENDERER_NOT_AVAILABLE,
isTrusted: textModel.metadata.trusted
}); });
} }
} }
......
...@@ -63,7 +63,8 @@ export class OutputElement extends Disposable { ...@@ -63,7 +63,8 @@ export class OutputElement extends Disposable {
const mimeTypes = this.notebookService.getMimeTypeInfo(this.notebookEditor.textModel!, this.output); const mimeTypes = this.notebookService.getMimeTypeInfo(this.notebookEditor.textModel!, this.output);
const pick = this.pickedMimeTypes.get(this.output) ?? Math.max(mimeTypes.findIndex(mimeType => mimeType.rendererId !== RENDERER_NOT_AVAILABLE), 0); // there is at least one mimetype which is safe and can be rendered by the core
const pick = this.pickedMimeTypes.get(this.output) ?? Math.max(mimeTypes.findIndex(mimeType => mimeType.rendererId !== RENDERER_NOT_AVAILABLE && mimeType.isTrusted), 0);
if (mimeTypes.length > 1) { if (mimeTypes.length > 1) {
outputItemDiv.style.position = 'relative'; outputItemDiv.style.position = 'relative';
...@@ -104,6 +105,8 @@ export class OutputElement extends Disposable { ...@@ -104,6 +105,8 @@ export class OutputElement extends Disposable {
} else { } else {
result = this.notebookEditor.getOutputRenderer().render(this.output, innerContainer, pickedMimeTypeRenderer.mimeType, this.getNotebookUri(),); result = this.notebookEditor.getOutputRenderer().render(this.output, innerContainer, pickedMimeTypeRenderer.mimeType, this.getNotebookUri(),);
} }
this.pickedMimeTypes.set(this.output, pick);
} else { } else {
// for text and error, there is no mimetype // for text and error, there is no mimetype
const innerContainer = DOM.$('.output-inner-container'); const innerContainer = DOM.$('.output-inner-container');
...@@ -174,10 +177,10 @@ export class OutputElement extends Disposable { ...@@ -174,10 +177,10 @@ export class OutputElement extends Disposable {
async pickActiveMimeTypeRenderer(output: ITransformedDisplayOutputDto) { async pickActiveMimeTypeRenderer(output: ITransformedDisplayOutputDto) {
const mimeTypes = this.notebookService.getMimeTypeInfo(this.notebookEditor.textModel!, output); const mimeTypes = this.notebookService.getMimeTypeInfo(this.notebookEditor.textModel!, output);
const currIndex = this.pickedMimeTypes.get(output) ?? 0; const currIndex = this.pickedMimeTypes.get(output);
// const currIndex = output.pickedMimeTypeIndex; // const currIndex = output.pickedMimeTypeIndex;
const items = mimeTypes.map((mimeType, index): IMimeTypeRenderer => ({ const items = mimeTypes.filter(mimeType => mimeType.isTrusted).map((mimeType, index): IMimeTypeRenderer => ({
label: mimeType.mimeType, label: mimeType.mimeType,
id: mimeType.mimeType, id: mimeType.mimeType,
index: index, index: index,
...@@ -189,7 +192,9 @@ export class OutputElement extends Disposable { ...@@ -189,7 +192,9 @@ export class OutputElement extends Disposable {
const picker = this.quickInputService.createQuickPick(); const picker = this.quickInputService.createQuickPick();
picker.items = items; picker.items = items;
picker.activeItems = items.filter(item => !!item.picked); picker.activeItems = items.filter(item => !!item.picked);
picker.placeholder = nls.localize('promptChooseMimeType.placeHolder', "Select output mimetype to render for current output"); picker.placeholder = items.length !== mimeTypes.length
? nls.localize('promptChooseMimeTypeInSecure.placeHolder', "Select mimetype to render for current output. Rich mimetypes are available only when the notebook is trusted")
: nls.localize('promptChooseMimeType.placeHolder', "Select mimetype to render for current output");
const pick = await new Promise<number | undefined>(resolve => { const pick = await new Promise<number | undefined>(resolve => {
picker.onDidAccept(() => { picker.onDidAccept(() => {
...@@ -284,7 +289,7 @@ export class OutputContainer extends Disposable { ...@@ -284,7 +289,7 @@ export class OutputContainer extends Disposable {
this.templateData.outputContainer!.style.display = 'block'; this.templateData.outputContainer!.style.display = 'block';
// there are outputs, we need to calcualte their sizes and trigger relayout // there are outputs, we need to calcualte their sizes and trigger relayout
// @TODO@rebornix, if there is no resizable output, we should not check their height individually, which hurts the performance // @TODO@rebornix, if there is no resizable output, we should not check their height individually, which hurts the performance
const outputsToRender = this.viewCell.outputs.slice(0, Math.min(OUTPUT_COUNT_LIMIT, this.viewCell.outputs.length)); const outputsToRender = this._calcuateOutputsToRender();
for (let index = 0; index < outputsToRender.length; index++) { for (let index = 0; index < outputsToRender.length; index++) {
const currOutput = this.viewCell.outputs[index]; const currOutput = this.viewCell.outputs[index];
...@@ -356,6 +361,43 @@ export class OutputContainer extends Disposable { ...@@ -356,6 +361,43 @@ export class OutputContainer extends Disposable {
}); });
} }
private _calcuateOutputsToRender() {
const outputs = this.viewCell.outputs.slice(0, Math.min(OUTPUT_COUNT_LIMIT, this.viewCell.outputs.length));
if (!this.notebookEditor.viewModel!.metadata.trusted) {
// not trusted
const secureOutput = outputs.filter(output => {
switch (output.outputKind) {
case CellOutputKind.Text:
return true;
case CellOutputKind.Error:
return true;
case CellOutputKind.Rich:
{
const mimeTypes = [];
for (const property in output.data) {
mimeTypes.push(property);
}
if (mimeTypes.indexOf('text/plain') >= 0
|| mimeTypes.indexOf('text/markdown') >= 0
|| mimeTypes.indexOf('application/json') >= 0
|| mimeTypes.includes('image/png')) {
return true;
}
return false;
}
default:
return false;
}
});
return secureOutput;
}
return outputs;
}
private _updateOutputs(splices: NotebookCellOutputsSplice[]) { private _updateOutputs(splices: NotebookCellOutputsSplice[]) {
if (!splices.length) { if (!splices.length) {
return; return;
...@@ -393,7 +435,7 @@ export class OutputContainer extends Disposable { ...@@ -393,7 +435,7 @@ export class OutputContainer extends Disposable {
}); });
let prevElement: HTMLElement | undefined = undefined; let prevElement: HTMLElement | undefined = undefined;
const outputsToRender = this.viewCell.outputs.slice(0, Math.min(OUTPUT_COUNT_LIMIT, this.viewCell.outputs.length)); const outputsToRender = this._calcuateOutputsToRender();
outputsToRender.reverse().forEach(output => { outputsToRender.reverse().forEach(output => {
if (this.outputEntries.has(output)) { if (this.outputEntries.has(output)) {
......
...@@ -414,6 +414,9 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR ...@@ -414,6 +414,9 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR
const betweenCellToolbar = disposables.add(this.createBetweenCellToolbar(bottomCellContainer, disposables, contextKeyService)); const betweenCellToolbar = disposables.add(this.createBetweenCellToolbar(bottomCellContainer, disposables, contextKeyService));
const statusBar = disposables.add(this.instantiationService.createInstance(CellEditorStatusBar, editorPart)); const statusBar = disposables.add(this.instantiationService.createInstance(CellEditorStatusBar, editorPart));
DOM.hide(statusBar.durationContainer);
DOM.hide(statusBar.cellRunStatusContainer);
const titleMenu = disposables.add(this.cellMenus.getCellTitleMenu(contextKeyService)); const titleMenu = disposables.add(this.cellMenus.getCellTitleMenu(contextKeyService));
const templateData: MarkdownCellRenderTemplate = { const templateData: MarkdownCellRenderTemplate = {
......
...@@ -23,6 +23,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati ...@@ -23,6 +23,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { ChangeCellLanguageAction, INotebookCellActionContext } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions'; import { ChangeCellLanguageAction, INotebookCellActionContext } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions';
import { ICellViewModel, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { ICellViewModel, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { BaseCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel';
import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService'; import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService';
import { CellKind, CellStatusbarAlignment, INotebookCellStatusBarEntry } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { CellKind, CellStatusbarAlignment, INotebookCellStatusBarEntry } from 'vs/workbench/contrib/notebook/common/notebookCommon';
...@@ -339,3 +340,18 @@ export function getResizesObserver(referenceDomElement: HTMLElement | null, dime ...@@ -339,3 +340,18 @@ export function getResizesObserver(referenceDomElement: HTMLElement | null, dime
return new ElementSizeObserver(referenceDomElement, dimension, changeCallback); return new ElementSizeObserver(referenceDomElement, dimension, changeCallback);
} }
} }
export function getExecuteCellPlaceholder(viewCell: BaseCellViewModel) {
return {
alignment: CellStatusbarAlignment.LEFT,
priority: -1,
cellResource: viewCell.uri,
command: undefined,
// text: `${keybinding?.getLabel() || 'Ctrl + Enter'} to run`,
// tooltip: `${keybinding?.getLabel() || 'Ctrl + Enter'} to run`,
text: 'Ctrl + Enter to run',
tooltip: 'Ctrl + Enter to run',
visible: true,
opacity: '0.7'
};
}
...@@ -16,10 +16,9 @@ import { CellFocusMode, CodeCellRenderTemplate, INotebookEditor } from 'vs/workb ...@@ -16,10 +16,9 @@ import { CellFocusMode, CodeCellRenderTemplate, INotebookEditor } from 'vs/workb
import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel';
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { ClickTargetType } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets'; import { ClickTargetType, getExecuteCellPlaceholder } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets';
import { OutputContainer } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellOutput'; import { OutputContainer } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellOutput';
import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService'; import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService';
import { CellStatusbarAlignment } from 'vs/workbench/contrib/notebook/common/notebookCommon';
export class CodeCell extends Disposable { export class CodeCell extends Disposable {
...@@ -35,7 +34,9 @@ export class CodeCell extends Disposable { ...@@ -35,7 +34,9 @@ export class CodeCell extends Disposable {
@INotebookCellStatusBarService readonly notebookCellStatusBarService: INotebookCellStatusBarService, @INotebookCellStatusBarService readonly notebookCellStatusBarService: INotebookCellStatusBarService,
@IOpenerService readonly openerService: IOpenerService, @IOpenerService readonly openerService: IOpenerService,
@ITextFileService readonly textFileService: ITextFileService, @ITextFileService readonly textFileService: ITextFileService,
@IModeService private readonly _modeService: IModeService @IModeService private readonly _modeService: IModeService,
// @IKeybindingService private readonly _keybindingService: IKeybindingService,
) { ) {
super(); super();
...@@ -218,19 +219,14 @@ export class CodeCell extends Disposable { ...@@ -218,19 +219,14 @@ export class CodeCell extends Disposable {
updateForCollapseState(); updateForCollapseState();
const updatePlaceholder = () => { const updatePlaceholder = () => {
if (this.notebookEditor.getActiveCell() === this.viewCell && viewCell.metadata.runState === undefined && viewCell.metadata.lastRunDuration === undefined) { if (this.notebookEditor.getActiveCell() === this.viewCell
&& viewCell.metadata.runState === undefined
&& viewCell.metadata.lastRunDuration === undefined
) {
// active cell and no run status // active cell and no run status
if (this._activeCellRunPlaceholder === null) { if (this._activeCellRunPlaceholder === null) {
this._activeCellRunPlaceholder = this.notebookCellStatusBarService.addEntry({ // const keybinding = this._keybindingService.lookupKeybinding(EXECUTE_CELL_COMMAND_ID);
alignment: CellStatusbarAlignment.LEFT, this._activeCellRunPlaceholder = this.notebookCellStatusBarService.addEntry(getExecuteCellPlaceholder(this.viewCell));
priority: -1,
cellResource: viewCell.uri,
command: undefined,
text: 'Ctrl + Enter to run',
tooltip: 'Ctrl + Enter to run',
visible: true,
opacity: '0.7'
});
} }
return; return;
...@@ -247,6 +243,8 @@ export class CodeCell extends Disposable { ...@@ -247,6 +243,8 @@ export class CodeCell extends Disposable {
this._register(this.viewCell.model.onDidChangeMetadata(() => { this._register(this.viewCell.model.onDidChangeMetadata(() => {
updatePlaceholder(); updatePlaceholder();
})); }));
updatePlaceholder();
} }
private viewUpdate(): void { private viewUpdate(): void {
......
...@@ -7,7 +7,7 @@ import * as DOM from 'vs/base/browser/dom'; ...@@ -7,7 +7,7 @@ import * as DOM from 'vs/base/browser/dom';
import { raceCancellation } from 'vs/base/common/async'; import { raceCancellation } from 'vs/base/common/async';
import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { renderCodicons } from 'vs/base/browser/codicons'; import { renderCodicons } from 'vs/base/browser/codicons';
import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
...@@ -19,7 +19,8 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; ...@@ -19,7 +19,8 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { getResizesObserver } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets'; import { getExecuteCellPlaceholder, getResizesObserver } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets';
import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService';
export class StatefulMarkdownCell extends Disposable { export class StatefulMarkdownCell extends Disposable {
...@@ -29,6 +30,7 @@ export class StatefulMarkdownCell extends Disposable { ...@@ -29,6 +30,7 @@ export class StatefulMarkdownCell extends Disposable {
private localDisposables = new DisposableStore(); private localDisposables = new DisposableStore();
private foldingState: CellFoldingState; private foldingState: CellFoldingState;
private _activeCellRunPlaceholder: IDisposable | null = null;
constructor( constructor(
private readonly notebookEditor: INotebookEditor, private readonly notebookEditor: INotebookEditor,
...@@ -37,6 +39,7 @@ export class StatefulMarkdownCell extends Disposable { ...@@ -37,6 +39,7 @@ export class StatefulMarkdownCell extends Disposable {
private editorOptions: IEditorOptions, private editorOptions: IEditorOptions,
private readonly renderedEditors: Map<ICellViewModel, ICodeEditor | undefined>, private readonly renderedEditors: Map<ICellViewModel, ICodeEditor | undefined>,
@IContextKeyService private readonly contextKeyService: IContextKeyService, @IContextKeyService private readonly contextKeyService: IContextKeyService,
@INotebookCellStatusBarService readonly notebookCellStatusBarService: INotebookCellStatusBarService,
@IInstantiationService private readonly instantiationService: IInstantiationService, @IInstantiationService private readonly instantiationService: IInstantiationService,
) { ) {
super(); super();
...@@ -129,6 +132,31 @@ export class StatefulMarkdownCell extends Disposable { ...@@ -129,6 +132,31 @@ export class StatefulMarkdownCell extends Disposable {
}); });
this.viewUpdate(); this.viewUpdate();
const updatePlaceholder = () => {
if (this.notebookEditor.getActiveCell() === this.viewCell) {
// active cell and no run status
if (this._activeCellRunPlaceholder === null) {
// const keybinding = this._keybindingService.lookupKeybinding(EXECUTE_CELL_COMMAND_ID);
this._activeCellRunPlaceholder = this.notebookCellStatusBarService.addEntry(getExecuteCellPlaceholder(this.viewCell));
}
return;
}
this._activeCellRunPlaceholder?.dispose();
this._activeCellRunPlaceholder = null;
};
this._register(this.notebookEditor.onDidChangeActiveCell(() => {
updatePlaceholder();
}));
this._register(this.viewCell.model.onDidChangeMetadata(() => {
updatePlaceholder();
}));
updatePlaceholder();
} }
private viewUpdate(): void { private viewUpdate(): void {
......
...@@ -453,8 +453,8 @@ export abstract class BaseCellViewModel extends Disposable { ...@@ -453,8 +453,8 @@ export abstract class BaseCellViewModel extends Disposable {
const editable = this.metadata?.editable ?? const editable = this.metadata?.editable ??
documentMetadata.cellEditable; documentMetadata.cellEditable;
const runnable = this.metadata?.runnable ?? const runnable = (this.metadata?.runnable ??
documentMetadata.cellRunnable; documentMetadata.cellRunnable) && !!documentMetadata.trusted;
const hasExecutionOrder = this.metadata?.hasExecutionOrder ?? const hasExecutionOrder = this.metadata?.hasExecutionOrder ??
documentMetadata.cellHasExecutionOrder; documentMetadata.cellHasExecutionOrder;
......
...@@ -180,6 +180,10 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD ...@@ -180,6 +180,10 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD
return this._notebook.metadata; return this._notebook.metadata;
} }
get runnable() {
return !!this._notebook.metadata?.runnable && !!this._notebook.metadata?.trusted;
}
private readonly _onDidChangeViewCells = this._register(new Emitter<INotebookViewCellsUpdateEvent>()); private readonly _onDidChangeViewCells = this._register(new Emitter<INotebookViewCellsUpdateEvent>());
get onDidChangeViewCells(): Event<INotebookViewCellsUpdateEvent> { return this._onDidChangeViewCells.event; } get onDidChangeViewCells(): Event<INotebookViewCellsUpdateEvent> { return this._onDidChangeViewCells.event; }
......
...@@ -86,7 +86,7 @@ export interface NotebookDocumentMetadata { ...@@ -86,7 +86,7 @@ export interface NotebookDocumentMetadata {
displayOrder?: (string | glob.IRelativePattern)[]; displayOrder?: (string | glob.IRelativePattern)[];
custom?: { [key: string]: unknown }; custom?: { [key: string]: unknown };
runState?: NotebookRunState; runState?: NotebookRunState;
trusted?: boolean; trusted: boolean;
} }
export enum NotebookCellRunState { export enum NotebookCellRunState {
...@@ -185,6 +185,7 @@ export enum MimeTypeRendererResolver { ...@@ -185,6 +185,7 @@ export enum MimeTypeRendererResolver {
export interface IOrderedMimeType { export interface IOrderedMimeType {
mimeType: string; mimeType: string;
rendererId: string; rendererId: string;
isTrusted: boolean;
} }
export interface ITransformedDisplayOutputDto extends IDisplayOutput { export interface ITransformedDisplayOutputDto extends IDisplayOutput {
...@@ -538,6 +539,19 @@ export namespace CellUri { ...@@ -538,6 +539,19 @@ export namespace CellUri {
} }
} }
export function mimeTypeIsAlwaysSecure(mimeType: string) {
if ([
'application/json',
'text/markdown',
'image/png',
'text/plain'
].indexOf(mimeType) > -1) {
return true;
}
return false;
}
export function mimeTypeSupportedByCore(mimeType: string) { export function mimeTypeSupportedByCore(mimeType: string) {
if ([ if ([
'application/json', 'application/json',
......
...@@ -156,7 +156,7 @@ suite('NotebookViewModel', () => { ...@@ -156,7 +156,7 @@ suite('NotebookViewModel', () => {
['var e = 5;', 'javascript', CellKind.Code, [], { editable: false, runnable: false }], ['var e = 5;', 'javascript', CellKind.Code, [], { editable: false, runnable: false }],
], ],
(editor, viewModel) => { (editor, viewModel) => {
viewModel.notebookDocument.metadata = { editable: true, runnable: true, cellRunnable: true, cellEditable: true, cellHasExecutionOrder: true }; viewModel.notebookDocument.metadata = { editable: true, runnable: true, cellRunnable: true, cellEditable: true, cellHasExecutionOrder: true, trusted: true };
const defaults = { hasExecutionOrder: true }; const defaults = { hasExecutionOrder: true };
...@@ -190,7 +190,7 @@ suite('NotebookViewModel', () => { ...@@ -190,7 +190,7 @@ suite('NotebookViewModel', () => {
...defaults ...defaults
}); });
viewModel.notebookDocument.metadata = { editable: true, runnable: true, cellRunnable: false, cellEditable: true, cellHasExecutionOrder: true }; viewModel.notebookDocument.metadata = { editable: true, runnable: true, cellRunnable: false, cellEditable: true, cellHasExecutionOrder: true, trusted: true };
assert.deepEqual(viewModel.viewCells[0].getEvaluatedMetadata(viewModel.metadata), <NotebookCellMetadata>{ assert.deepEqual(viewModel.viewCells[0].getEvaluatedMetadata(viewModel.metadata), <NotebookCellMetadata>{
editable: true, editable: true,
...@@ -222,7 +222,7 @@ suite('NotebookViewModel', () => { ...@@ -222,7 +222,7 @@ suite('NotebookViewModel', () => {
...defaults ...defaults
}); });
viewModel.notebookDocument.metadata = { editable: true, runnable: true, cellRunnable: false, cellEditable: false, cellHasExecutionOrder: true }; viewModel.notebookDocument.metadata = { editable: true, runnable: true, cellRunnable: false, cellEditable: false, cellHasExecutionOrder: true, trusted: true };
assert.deepEqual(viewModel.viewCells[0].getEvaluatedMetadata(viewModel.metadata), <NotebookCellMetadata>{ assert.deepEqual(viewModel.viewCells[0].getEvaluatedMetadata(viewModel.metadata), <NotebookCellMetadata>{
editable: false, editable: false,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册