diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index b49638d970..4dfaba0cf8 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -23,7 +23,8 @@ "onCommand:markdown.showLockedPreviewToSide", "onCommand:markdown.showSource", "onCommand:markdown.showPreviewSecuritySelector", - "onWebviewPanel:markdown.preview" + "onWebviewPanel:markdown.preview", + "onCommand:notebook.showPreview" ], "contributes": { "commands": [ @@ -77,6 +78,11 @@ "command": "markdown.preview.toggleLock", "title": "%markdown.preview.toggleLock.title%", "category": "Markdown" + }, + { + "command": "notebook.showPreview", + "title": "notebook.showPreview", + "category": "Notebook" } ], "menus": { @@ -154,6 +160,10 @@ { "command": "markdown.preview.toggleLock", "when": "markdownPreviewFocus" + }, + { + "command": "notebook.showPreview", + "when": "false" } ] }, diff --git a/extensions/markdown-language-features/src/commandManager.ts b/extensions/markdown-language-features/src/commandManager.ts index 174f30cd44..966049df92 100644 --- a/extensions/markdown-language-features/src/commandManager.ts +++ b/extensions/markdown-language-features/src/commandManager.ts @@ -8,7 +8,8 @@ import * as vscode from 'vscode'; export interface Command { readonly id: string; - execute(...args: any[]): void; + // {{SQL CARBON EDIT}} + execute(...args: any[]): any; } export class CommandManager { @@ -26,7 +27,8 @@ export class CommandManager { return command; } - private registerCommand(id: string, impl: (...args: any[]) => void, thisArg?: any) { + // {{SQL CARBON EDIT}} + private registerCommand(id: string, impl: (...args: any[]) => any, thisArg?: any) { if (this.commands.has(id)) { return; } diff --git a/extensions/markdown-language-features/src/commands/index.ts b/extensions/markdown-language-features/src/commands/index.ts index 087f93987f..09c37d1bfd 100644 --- a/extensions/markdown-language-features/src/commands/index.ts +++ b/extensions/markdown-language-features/src/commands/index.ts @@ -11,3 +11,5 @@ export { RefreshPreviewCommand } from './refreshPreview'; export { ShowPreviewSecuritySelectorCommand } from './showPreviewSecuritySelector'; export { MoveCursorToPositionCommand } from './moveCursorToPosition'; export { ToggleLockCommand } from './toggleLock'; +// {{SQL CARBON EDIT}} +export { ShowNotebookPreview } from './showNotebookPreview'; diff --git a/extensions/markdown-language-features/src/commands/showNotebookPreview.ts b/extensions/markdown-language-features/src/commands/showNotebookPreview.ts new file mode 100644 index 0000000000..ddab003f02 --- /dev/null +++ b/extensions/markdown-language-features/src/commands/showNotebookPreview.ts @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import * as vscode from 'vscode'; + +import { Command } from '../commandManager'; +import { MarkdownEngine } from '../markdownEngine'; + +export class ShowNotebookPreview implements Command { + public readonly id = 'notebook.showPreview'; + + public constructor( + private readonly engine: MarkdownEngine + ) { } + + public async execute(document: vscode.Uri, textContent: string): Promise { + return this.engine.renderText(document, textContent); + } +} \ No newline at end of file diff --git a/extensions/markdown-language-features/src/extension.ts b/extensions/markdown-language-features/src/extension.ts index b73ed0f6c1..99335209bf 100644 --- a/extensions/markdown-language-features/src/extension.ts +++ b/extensions/markdown-language-features/src/extension.ts @@ -59,6 +59,8 @@ export function activate(context: vscode.ExtensionContext) { commandManager.register(new commands.OnPreviewStyleLoadErrorCommand()); commandManager.register(new commands.OpenDocumentLinkCommand(engine)); commandManager.register(new commands.ToggleLockCommand(previewManager)); + // {{SQL CARBON EDIT}} + commandManager.register(new commands.ShowNotebookPreview(engine)); context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(() => { logger.updateConfiguration(); diff --git a/extensions/markdown-language-features/src/markdownEngine.ts b/extensions/markdown-language-features/src/markdownEngine.ts index 2571c04875..618e7edc0f 100644 --- a/extensions/markdown-language-features/src/markdownEngine.ts +++ b/extensions/markdown-language-features/src/markdownEngine.ts @@ -86,6 +86,12 @@ export class MarkdownEngine { return { text, offset }; } + // {{SQL CARBON EDIT}} + public async renderText(document: vscode.Uri, text: string): Promise { + const engine = await this.getEngine(document); + return engine.render(text); + } + public async render(document: vscode.Uri, stripFrontmatter: boolean, text: string): Promise { let offset = 0; if (stripFrontmatter) { diff --git a/src/sql/parts/notebook/cellViews/code.component.ts b/src/sql/parts/notebook/cellViews/code.component.ts index f5528dc0c4..582b7aabc7 100644 --- a/src/sql/parts/notebook/cellViews/code.component.ts +++ b/src/sql/parts/notebook/cellViews/code.component.ts @@ -4,13 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./code'; -import { OnInit, Component, Input, Inject, forwardRef, ElementRef, ChangeDetectorRef, OnDestroy, ViewChild } from '@angular/core'; +import { OnInit, Component, Input, Inject, forwardRef, ElementRef, ChangeDetectorRef, OnDestroy, ViewChild, Output, EventEmitter } from '@angular/core'; import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service'; import { AngularDisposable } from 'sql/base/common/lifecycle'; import { ComponentBase } from 'sql/parts/modelComponents/componentBase'; import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/parts/modelComponents/interfaces'; import { QueryTextEditor } from 'sql/parts/modelComponents/queryTextEditor'; +import { ICellModel } from 'sql/parts/notebook/cellViews/interfaces'; import { IColorTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; import * as themeColors from 'vs/workbench/common/theme'; @@ -26,7 +27,6 @@ import * as DOM from 'vs/base/browser/dom'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; - export const CODE_SELECTOR: string = 'code-component'; @Component({ @@ -36,9 +36,8 @@ export const CODE_SELECTOR: string = 'code-component'; export class CodeComponent extends AngularDisposable implements OnInit { @ViewChild('toolbar', { read: ElementRef }) private toolbarElement: ElementRef; @ViewChild('editor', { read: ElementRef }) private codeElement: ElementRef; - @Input() id: string; - @Input() content: string; - @Input() language: string; + @Input() cellModel: ICellModel; + @Output() public onContentChanged = new EventEmitter(); private readonly _minimumHeight = 30; private _editor: QueryTextEditor; @@ -81,18 +80,19 @@ export class CodeComponent extends AngularDisposable implements OnInit { this._editor.setVisible(true); this._editor.setMinimumHeight(this._minimumHeight); let uri = this.createUri(); - this._editorInput = instantiationService.createInstance(UntitledEditorInput, uri, false, this.language, '', ''); + this._editorInput = instantiationService.createInstance(UntitledEditorInput, uri, false, this.cellModel.language, '', ''); this._editor.setInput(this._editorInput, undefined); this._editorInput.resolve().then(model => { this._editorModel = model.textEditorModel; - this._modelService.updateModel(this._editorModel, this.content); + this._modelService.updateModel(this._editorModel, this.cellModel.source); }); this._register(this._editor); this._register(this._editorInput); this._register(this._editorModel.onDidChangeContent(e => { - this.content = this._editorModel.getValue(); this._editor.setHeightToScrollHeight(); + this.cellModel.source = this._editorModel.getValue(); + this.onContentChanged.emit(); })); this.layout(); } @@ -105,22 +105,22 @@ export class CodeComponent extends AngularDisposable implements OnInit { } private createUri(): URI { - let uri = URI.from({ scheme: Schemas.untitled, path: `notebook-editor-${this.id}` }); + let uri = URI.from({ scheme: Schemas.untitled, path: `notebook-editor-${this.cellModel.id}` }); // Use this to set the internal (immutable) and public (shared with extension) uri properties - this._uri = uri.toString(); + this.cellModel.cellUri = uri; return uri; } /// Editor Functions private updateModel() { if (this._editorModel) { - this._modelService.updateModel(this._editorModel, this.content); + this._modelService.updateModel(this._editorModel, this.cellModel.source); } } private updateLanguageMode() { if (this._editorModel && this._editor) { - this._modeService.getOrCreateMode(this.language).then((modeValue) => { + this._modeService.getOrCreateMode(this.cellModel.language).then((modeValue) => { this._modelService.setMode(this._editorModel, modeValue); }); } diff --git a/src/sql/parts/notebook/cellViews/codeCell.component.html b/src/sql/parts/notebook/cellViews/codeCell.component.html index 801b9eb3c7..b7f97fed40 100644 --- a/src/sql/parts/notebook/cellViews/codeCell.component.html +++ b/src/sql/parts/notebook/cellViews/codeCell.component.html @@ -6,7 +6,7 @@ -->
- +
Place Holder for output area diff --git a/src/sql/parts/notebook/cellViews/interfaces.ts b/src/sql/parts/notebook/cellViews/interfaces.ts index 0102a9e89b..6ffe170a98 100644 --- a/src/sql/parts/notebook/cellViews/interfaces.ts +++ b/src/sql/parts/notebook/cellViews/interfaces.ts @@ -5,7 +5,7 @@ import { OnDestroy } from '@angular/core'; import { AngularDisposable } from 'sql/base/common/lifecycle'; - +import URI from 'vs/base/common/uri'; export abstract class CellView extends AngularDisposable implements OnDestroy { constructor() { @@ -21,6 +21,7 @@ export interface ICellModel { source: string; cellType: CellType; active: boolean; + cellUri?: URI; } export type CellType = 'code' | 'markdown' | 'raw'; diff --git a/src/sql/parts/notebook/cellViews/textCell.component.html b/src/sql/parts/notebook/cellViews/textCell.component.html new file mode 100644 index 0000000000..98265758f8 --- /dev/null +++ b/src/sql/parts/notebook/cellViews/textCell.component.html @@ -0,0 +1,13 @@ + +
+
+ +
+
+
+
diff --git a/src/sql/parts/notebook/cellViews/textCell.component.ts b/src/sql/parts/notebook/cellViews/textCell.component.ts new file mode 100644 index 0000000000..2f5e64be7f --- /dev/null +++ b/src/sql/parts/notebook/cellViews/textCell.component.ts @@ -0,0 +1,67 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Source EULA. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ +import 'vs/css!./textCell'; + +import { OnInit, Component, Input, Inject, forwardRef, ElementRef, ChangeDetectorRef, OnDestroy, ViewChild } from '@angular/core'; + +import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service'; +import { CellView, ICellModel } from 'sql/parts/notebook/cellViews/interfaces'; + +import { IColorTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import * as themeColors from 'vs/workbench/common/theme'; +import { ICommandService } from 'vs/platform/commands/common/commands'; + +export const TEXT_SELECTOR: string = 'text-cell-component'; + +@Component({ + selector: TEXT_SELECTOR, + templateUrl: decodeURI(require.toUrl('./textCell.component.html')) +}) +export class TextCellComponent extends CellView implements OnInit { + @ViewChild('preview', { read: ElementRef }) private output: ElementRef; + @Input() cellModel: ICellModel; + private _content: string; + constructor( + @Inject(forwardRef(() => CommonServiceInterface)) private _bootstrapService: CommonServiceInterface, + @Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef, + @Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService, + @Inject(ICommandService) private _commandService: ICommandService + ) { + super(); + } + + ngOnChanges() { + this.updatePreview(); + } + + private updatePreview() { + if (this._content !== this.cellModel.source) { + this._content = this.cellModel.source; + // todo: pass in the notebook filename instead of undefined value + this._commandService.executeCommand('notebook.showPreview', undefined, this._content).then((htmlcontent) => { + let outputElement = this.output.nativeElement; + outputElement.innerHTML = htmlcontent; + }); + } + } + + ngOnInit() { + this._register(this.themeService.onDidColorThemeChange(this.updateTheme, this)); + this.updateTheme(this.themeService.getColorTheme()); + } + + // Todo: implement layout + public layout() { + } + + private updateTheme(theme: IColorTheme): void { + let outputElement = this.output.nativeElement; + outputElement.style.borderTopColor = theme.getColor(themeColors.SIDE_BAR_BACKGROUND, true).toString(); + } + + public handleContentChanged(): void { + this.updatePreview(); + } +} diff --git a/src/sql/parts/notebook/cellViews/textCell.css b/src/sql/parts/notebook/cellViews/textCell.css new file mode 100644 index 0000000000..5f6d5d2d16 --- /dev/null +++ b/src/sql/parts/notebook/cellViews/textCell.css @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +text-cell-component { + display: block; +} + +text-cell-component .notebook-preview { + border-top-width: 1px; + border-top-style: solid; +} diff --git a/src/sql/parts/notebook/notebook.component.html b/src/sql/parts/notebook/notebook.component.html index e13738fb3b..0715fc62b6 100644 --- a/src/sql/parts/notebook/notebook.component.html +++ b/src/sql/parts/notebook/notebook.component.html @@ -12,8 +12,10 @@
- + + +
diff --git a/src/sql/parts/notebook/notebook.component.ts b/src/sql/parts/notebook/notebook.component.ts index 26af70af40..802666d8f2 100644 --- a/src/sql/parts/notebook/notebook.component.ts +++ b/src/sql/parts/notebook/notebook.component.ts @@ -38,7 +38,10 @@ export class NotebookComponent extends AngularDisposable implements OnInit { let cell2: ICellModel = { id: '2', language: 'sql', source: 'select 1', cellType: CellTypes.Code, active: false }; - this.cells.push(cell1, cell2); + let cell3: ICellModel = { + id: '3', language: 'markdown', source: '## This is test!', cellType: CellTypes.Markdown, active: false + }; + this.cells.push(cell1, cell2, cell3); } ngOnInit() { diff --git a/src/sql/parts/notebook/notebook.module.ts b/src/sql/parts/notebook/notebook.module.ts index ce4237d300..0bc516dcb5 100644 --- a/src/sql/parts/notebook/notebook.module.ts +++ b/src/sql/parts/notebook/notebook.module.ts @@ -25,6 +25,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { Registry } from 'vs/platform/registry/common/platform'; import { CodeComponent } from 'sql/parts/notebook/cellViews/code.component'; import { CodeCellComponent } from 'sql/parts/notebook/cellViews/codeCell.component'; +import { TextCellComponent } from 'sql/parts/notebook/cellViews/textCell.component'; export const NotebookModule = (params, selector: string, instantiationService: IInstantiationService): any => { @NgModule({ @@ -35,6 +36,7 @@ export const NotebookModule = (params, selector: string, instantiationService: I InputBox, CodeComponent, CodeCellComponent, + TextCellComponent, NotebookComponent, ComponentHostDirective ],