moves notebooks code to browser (#7313)

This commit is contained in:
Anthony Dresser
2019-09-23 13:32:29 -07:00
committed by GitHub
parent 6f06ab440a
commit 5e3ec6ea39
11 changed files with 20 additions and 28 deletions

View File

@@ -0,0 +1,191 @@
/*
https://raw.githubusercontent.com/isagalaev/highlight.js/master/src/styles/vs2015.css
*/
/*
* Visual Studio 2015 dark style
* Author: Nicolas LLOBERA <nllobera@gmail.com>
*/
.notebook-preview .hljs-keyword,
.notebook-preview .hljs-literal,
.notebook-preview .hljs-symbol,
.notebook-preview .hljs-name {
color: #569CD6;
}
.notebook-preview .hljs-link {
color: #569CD6;
text-decoration: underline;
}
.notebook-preview .hljs-built_in,
.notebook-preview .hljs-type {
color: #4EC9B0;
}
.notebook-preview .hljs-number,
.notebook-preview .hljs-class {
color: #B8D7A3;
}
.notebook-preview .hljs-string,
.notebook-preview .hljs-meta-string {
color: #D69D85;
}
.notebook-preview .hljs-regexp,
.notebook-preview .hljs-template-tag {
color: #9A5334;
}
.notebook-preview .hljs-subst,
.notebook-preview .hljs-function,
.notebook-preview .hljs-title,
.notebook-preview .hljs-params,
.notebook-preview .hljs-formula {
color: #DCDCDC;
}
.notebook-preview pre code .hljs-subst,
.notebook-preview pre code .hljs-function,
.notebook-preview pre code .hljs-title,
.notebook-preview pre code .hljs-params,
.notebook-preview pre code .hljs-formula {
color: var(--vscode-editor-foreground);
}
.notebook-preview .hljs-comment,
.notebook-preview .hljs-quote {
color: #57A64A;
font-style: italic;
}
.notebook-preview .hljs-doctag {
color: #608B4E;
}
.notebook-preview .hljs-meta,
.notebook-preview .hljs-meta-keyword,
.notebook-preview .hljs-tag {
color: #9B9B9B;
}
.notebook-preview .hljs-variable,
.notebook-preview .hljs-template-variable {
color: #BD63C5;
}
.notebook-preview .hljs-attr,
.notebook-preview .hljs-attribute,
.notebook-preview .hljs-builtin-name {
color: #9CDCFE;
}
.notebook-preview .hljs-section {
color: gold;
}
.notebook-preview .hljs-emphasis {
font-style: italic;
}
.notebook-preview .hljs-strong {
font-weight: bold;
}
/*.hljs-code {
font-family:'Monospace';
}*/
.notebook-preview .hljs-bullet,
.notebook-preview .hljs-selector-tag,
.notebook-preview .hljs-selector-id,
.notebook-preview .hljs-selector-class,
.notebook-preview .hljs-selector-attr,
.notebook-preview .hljs-selector-pseudo {
color: #D7BA7D;
}
.notebook-preview .hljs-addition {
background-color: var(--vscode-diffEditor-insertedTextBackground, rgba(155, 185, 85, 0.2));
color: rgb(155, 185, 85);
display: inline-block;
width: 100%;
}
.notebook-preview .hljs-deletion {
background: var(--vscode-diffEditor-removedTextBackground, rgba(255, 0, 0, 0.2));
color: rgb(255, 0, 0);
display: inline-block;
width: 100%;
}
/*
From https://raw.githubusercontent.com/isagalaev/highlight.js/master/src/styles/vs.css
*/
/*
Visual Studio-like style based on original C# coloring by Jason Diamond <jason@diamond.name>
*/
.notebook-preview .hljs-function,
.notebook-preview .hljs-params {
color: inherit;
}
.notebook-preview .hljs-comment,
.notebook-preview .hljs-quote,
.notebook-preview .hljs-variable {
color: #008000;
}
.notebook-preview .hljs-keyword,
.notebook-preview .hljs-selector-tag,
.notebook-preview .hljs-built_in,
.notebook-preview .hljs-name,
.notebook-preview .hljs-tag {
color: #00f;
}
.notebook-preview .hljs-string,
.notebook-preview .hljs-title,
.notebook-preview .hljs-section,
.notebook-preview .hljs-attribute,
.notebook-preview .hljs-literal,
.notebook-preview .hljs-template-tag,
.notebook-preview .hljs-template-variable,
.notebook-preview .hljs-type {
color: #a31515;
}
.notebook-preview .hljs-selector-attr,
.notebook-preview .hljs-selector-pseudo,
.notebook-preview .hljs-meta {
color: #2b91af;
}
.notebook-preview .hljs-doctag {
color: #808080;
}
.notebook-preview .hljs-attr {
color: #f00;
}
.notebook-preview .hljs-symbol,
.notebook-preview .hljs-bullet,
.notebook-preview .hljs-link {
color: #00b0e8;
}
.notebook-preview .hljs-emphasis {
font-style: italic;
}
.notebook-preview .hljs-strong {
font-weight: bold;
}

View File

@@ -0,0 +1,231 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.notebook-preview {
font-size: 14px;
line-height: 22px;
word-wrap: break-word;
}
.notebook-preview #code-csp-warning {
position: fixed;
top: 0;
right: 0;
color: white;
margin: 16px;
text-align: center;
font-size: 12px;
font-family: sans-serif;
background-color:#444444;
cursor: pointer;
padding: 6px;
box-shadow: 1px 1px 1px rgba(0,0,0,.25);
}
.notebook-preview #code-csp-warning:hover {
text-decoration: none;
background-color:#007acc;
box-shadow: 2px 2px 2px rgba(0,0,0,.25);
}
.notebook-preview .scrollBeyondLastLine {
margin-bottom: calc(100vh - 22px);
}
.notebook-preview .showEditorSelection .code-line {
position: relative;
}
.notebook-preview .showEditorSelection .code-active-line:before,
.notebook-preview .showEditorSelection .code-line:hover:before {
content: "";
display: block;
position: absolute;
top: 0;
left: -12px;
height: 100%;
}
.notebook-preview .showEditorSelection li.code-active-line:before,
.notebook-preview .showEditorSelection li.code-line:hover:before {
left: -30px;
}
.notebook-preview .showEditorSelection .code-active-line:before {
border-left: 3px solid rgba(0, 0, 0, 0.15);
}
.notebook-preview .showEditorSelection .code-line:hover:before {
border-left: 3px solid rgba(0, 0, 0, 0.40);
}
.notebook-preview .showEditorSelection .code-line .code-line:hover:before {
border-left: none;
}
.vs-dark .notebook-preview .showEditorSelection .code-active-line:before {
border-left: 3px solid rgba(255, 255, 255, 0.4);
}
.vs-dark .notebook-preview .showEditorSelection .code-line:hover:before {
border-left: 3px solid rgba(255, 255, 255, 0.60);
}
.vs-dark .notebook-preview .showEditorSelection .code-line .code-line:hover:before {
border-left: none;
}
.hc-black .notebook-preview .showEditorSelection .code-active-line:before {
border-left: 3px solid rgba(255, 160, 0, 0.7);
}
.hc-black .notebook-preview .showEditorSelection .code-line:hover:before {
border-left: 3px solid rgba(255, 160, 0, 1);
}
.hc-black .notebook-preview .showEditorSelection .code-line .code-line:hover:before {
border-left: none;
}
.notebook-preview img {
max-width: 100%;
max-height: 100%;
}
.notebookEditor a {
text-decoration: none;
}
.notebookEditor a:hover {
text-decoration: underline;
}
.notebook-preview a:focus,
.notebook-preview input:focus,
.notebook-preview select:focus,
.notebook-preview textarea:focus {
outline: 1px solid -webkit-focus-ring-color;
outline-offset: -1px;
}
.notebook-preview hr {
border: 0;
height: 2px;
border-bottom: 2px solid;
}
.notebook-preview h1 {
padding-bottom: 0.3em;
line-height: 1.2;
border-bottom-width: 1px;
border-bottom-style: solid;
}
.notebook-preview h1, .notebook-preview h2, .notebook-preview h3 {
font-weight: normal;
}
.notebook-preview h1 code,
.notebook-preview h2 code,
.notebook-preview h3 code,
.notebook-preview h4 code,
.notebook-preview h5 code,
.notebook-preview h6 code {
font-size: inherit;
line-height: auto;
}
.notebook-preview table {
border-collapse: collapse;
}
.notebook-preview table > thead > tr > th {
text-align: left;
border-bottom: 1px solid;
}
.notebook-preview table > thead > tr > th,
.notebook-preview table > thead > tr > td,
.notebook-preview table > tbody > tr > th,
.notebook-preview .notebook-preview table > tbody > tr > td {
padding: 5px 10px;
}
.notebook-preview table > tbody > tr + tr > td {
border-top: 1px solid;
}
.notebook-preview blockquote {
margin: 0 7px 0 5px;
padding: 0 16px 0 10px;
border-left-width: 5px;
border-left-style: solid;
}
.notebook-preview code {
font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Courier New", monospace, "Droid Sans Fallback";
font-size: 12px;
line-height: 19px;
}
.notebook-preview pre {
white-space: pre-wrap;
}
.notebook-preview .mac code {
font-size: 12px;
line-height: 18px;
}
.notebook-preview pre:not(.hljs),
.notebook-preview pre.hljs code > div {
padding: 16px;
border-radius: 3px;
overflow: auto;
}
/** Theming */
.notebook-preview pre code {
color: var(--vscode-editor-foreground);
}
.notebook-preview pre {
background-color: rgba(220, 220, 220, 0.4);
}
.vs-dark .notebook-preview pre {
background-color: rgba(10, 10, 10, 0.4);
}
.hc-black .notebook-preview pre {
background-color: rgb(0, 0, 0);
}
.hc-black .notebook-preview h1 {
border-color: rgb(0, 0, 0);
}
.notebook-preview table > thead > tr > th {
border-color: rgba(0, 0, 0, 0.69);
}
.vs-dark .notebook-preview table > thead > tr > th {
border-color: rgba(255, 255, 255, 0.69);
}
.notebook-preview h1,
.notebook-preview hr,
.notebook-preview table > tbody > tr + tr > td {
border-color: rgba(0, 0, 0, 0.18);
}
.vs-dark .notebook-preview h1,
.vs-dark .notebook-preview hr,
.vs-dark .notebook-preview table > tbody > tr + tr > td {
border-color: rgba(255, 255, 255, 0.18);
}

View File

@@ -0,0 +1,18 @@
<!--
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-->
<div style="overflow: hidden; width: 100%; height: 100%; display: flex; flex-flow: column" (mouseover)="hover=true" (mouseleave)="hover=false">
<div class="notebook-text" style="flex: 0 0 auto;">
<code-component *ngIf="isEditMode" [cellModel]="cellModel" (onContentChanged)="handleContentChanged()" [model]="model" [activeCellId]="activeCellId">
</code-component>
</div>
<div style="overflow: hidden; width: 100%; height: 100%; display: flex; flex-flow: row">
<div #preview link-handler [isTrusted]="isTrusted" [notebookUri]="notebookUri" class="notebook-preview" style="flex: 1 1 auto" (dblclick)="toggleEditMode()">
</div>
<div #moreactions class="moreActions" style="flex: 0 0 auto; display: flex; flex-flow:column;width: 20px; min-height: 20px; max-height: 20px; padding-top: 0px; orientation: portrait">
</div>
</div>
</div>

View File

@@ -0,0 +1,266 @@
/*---------------------------------------------------------------------------------------------
* 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 'vs/css!./media/markdown';
import 'vs/css!./media/highlight';
import { OnInit, Component, Input, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, OnChanges, SimpleChange, HostListener } from '@angular/core';
import { localize } from 'vs/nls';
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';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { Emitter } from 'vs/base/common/event';
import { URI } from 'vs/base/common/uri';
import * as DOM from 'vs/base/browser/dom';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { toDisposable } from 'vs/base/common/lifecycle';
import { IMarkdownRenderResult } from 'vs/editor/contrib/markdown/markdownRenderer';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { NotebookMarkdownRenderer } from 'sql/workbench/parts/notebook/browser/outputs/notebookMarkdown';
import { CellView } from 'sql/workbench/parts/notebook/browser/cellViews/interfaces';
import { ICellModel } from 'sql/workbench/parts/notebook/browser/models/modelInterfaces';
import { NotebookModel } from 'sql/workbench/parts/notebook/browser/models/notebookModel';
import { ISanitizer, defaultSanitizer } from 'sql/workbench/parts/notebook/browser/outputs/sanitizer';
import { CellToggleMoreActions } from 'sql/workbench/parts/notebook/browser/cellToggleMoreActions';
import { CommonServiceInterface } from 'sql/platform/bootstrap/browser/commonServiceInterface.service';
import { useInProcMarkdown, convertVscodeResourceToFileInSubDirectories } from 'sql/workbench/parts/notebook/browser/models/notebookUtils';
export const TEXT_SELECTOR: string = 'text-cell-component';
const USER_SELECT_CLASS = 'actionselect';
@Component({
selector: TEXT_SELECTOR,
templateUrl: decodeURI(require.toUrl('./textCell.component.html'))
})
export class TextCellComponent extends CellView implements OnInit, OnChanges {
@ViewChild('preview', { read: ElementRef }) private output: ElementRef;
@ViewChild('moreactions', { read: ElementRef }) private moreActionsElementRef: ElementRef;
@Input() cellModel: ICellModel;
@Input() set model(value: NotebookModel) {
this._model = value;
}
@Input() set activeCellId(value: string) {
this._activeCellId = value;
}
@Input() set hover(value: boolean) {
this._hover = value;
if (!this.isActive()) {
// Only make a change if we're not active, since this has priority
this.updateMoreActions();
}
}
@HostListener('document:keydown.escape', ['$event'])
handleKeyboardEvent() {
if (this.isEditMode) {
this.toggleEditMode(false);
}
this.cellModel.active = false;
this._model.updateActiveCell(undefined);
}
private _content: string | string[];
private _lastTrustedMode: boolean;
private isEditMode: boolean;
private _sanitizer: ISanitizer;
private _model: NotebookModel;
private _activeCellId: string;
private readonly _onDidClickLink = this._register(new Emitter<URI>());
public readonly onDidClickLink = this._onDidClickLink.event;
private _cellToggleMoreActions: CellToggleMoreActions;
private _hover: boolean;
private markdownRenderer: NotebookMarkdownRenderer;
private markdownResult: IMarkdownRenderResult;
constructor(
@Inject(forwardRef(() => CommonServiceInterface)) private _bootstrapService: CommonServiceInterface,
@Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef,
@Inject(IInstantiationService) private _instantiationService: IInstantiationService,
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService,
@Inject(ICommandService) private _commandService: ICommandService,
@Inject(IOpenerService) private readonly openerService: IOpenerService,
@Inject(IConfigurationService) private configurationService: IConfigurationService,
) {
super();
this.isEditMode = true;
this._cellToggleMoreActions = this._instantiationService.createInstance(CellToggleMoreActions);
this.markdownRenderer = this._instantiationService.createInstance(NotebookMarkdownRenderer);
this._register(toDisposable(() => {
if (this.markdownResult) {
this.markdownResult.dispose();
}
}));
}
//Gets sanitizer from ISanitizer interface
private get sanitizer(): ISanitizer {
if (this._sanitizer) {
return this._sanitizer;
}
return this._sanitizer = defaultSanitizer;
}
get model(): NotebookModel {
return this._model;
}
get activeCellId(): string {
return this._activeCellId;
}
private setLoading(isLoading: boolean): void {
this.cellModel.loaded = !isLoading;
this._changeRef.detectChanges();
}
ngOnInit() {
this._register(this.themeService.onDidColorThemeChange(this.updateTheme, this));
this.updateTheme(this.themeService.getColorTheme());
this._cellToggleMoreActions.onInit(this.moreActionsElementRef, this.model, this.cellModel);
this.setFocusAndScroll();
this._register(this.cellModel.onOutputsChanged(e => {
this.updatePreview();
}));
}
ngOnChanges(changes: { [propKey: string]: SimpleChange }) {
for (let propName in changes) {
if (propName === 'activeCellId') {
let changedProp = changes[propName];
this._activeCellId = changedProp.currentValue;
this.toggleUserSelect(this.isActive());
// If the activeCellId is undefined (i.e. in an active cell update), don't unnecessarily set editMode to false;
// it will be set to true in a subsequent call to toggleEditMode()
if (changedProp.previousValue !== undefined) {
this.toggleEditMode(false);
}
break;
}
}
}
public get isTrusted(): boolean {
return this.model.trustedMode;
}
public get notebookUri(): URI {
return this.model.notebookUri;
}
/**
* Updates the preview of markdown component with latest changes
* If content is empty and in non-edit mode, default it to 'Double-click to edit'
* Sanitizes the data to be shown in markdown cell
*/
private updatePreview(): void {
let trustedChanged = this.cellModel && this._lastTrustedMode !== this.cellModel.trustedMode;
let cellModelSourceJoined = Array.isArray(this.cellModel.source) ? this.cellModel.source.join('') : this.cellModel.source;
let contentJoined = Array.isArray(this._content) ? this._content.join('') : this._content;
let contentChanged = contentJoined !== cellModelSourceJoined || cellModelSourceJoined.length === 0;
if (trustedChanged || contentChanged) {
this._lastTrustedMode = this.cellModel.trustedMode;
if ((!cellModelSourceJoined) && !this.isEditMode) {
this._content = localize('doubleClickEdit', "Double-click to edit");
} else {
this._content = this.cellModel.source;
}
if (useInProcMarkdown(this.configurationService)) {
this.markdownRenderer.setNotebookURI(this.cellModel.notebookModel.notebookUri);
this.markdownResult = this.markdownRenderer.render({
isTrusted: true,
value: Array.isArray(this._content) ? this._content.join('') : this._content
});
this.markdownResult.element.innerHTML = this.sanitizeContent(this.markdownResult.element.innerHTML);
this.setLoading(false);
let outputElement = <HTMLElement>this.output.nativeElement;
outputElement.innerHTML = this.markdownResult.element.innerHTML;
} else {
this._commandService.executeCommand<string>('notebook.showPreview', this.cellModel.notebookModel.notebookUri, this._content).then((htmlcontent) => {
htmlcontent = convertVscodeResourceToFileInSubDirectories(htmlcontent, this.cellModel);
htmlcontent = this.sanitizeContent(htmlcontent);
let outputElement = <HTMLElement>this.output.nativeElement;
outputElement.innerHTML = htmlcontent;
this.setLoading(false);
});
}
}
}
//Sanitizes the content based on trusted mode of Cell Model
private sanitizeContent(content: string): string {
if (this.cellModel && !this.cellModel.trustedMode) {
content = this.sanitizer.sanitize(content);
}
return content;
}
// Todo: implement layout
public layout() {
}
private updateTheme(theme: IColorTheme): void {
let outputElement = <HTMLElement>this.output.nativeElement;
outputElement.style.borderTopColor = theme.getColor(themeColors.SIDE_BAR_BACKGROUND, true).toString();
let moreActionsEl = <HTMLElement>this.moreActionsElementRef.nativeElement;
moreActionsEl.style.borderRightColor = theme.getColor(themeColors.SIDE_BAR_BACKGROUND, true).toString();
}
public handleContentChanged(): void {
this.updatePreview();
}
public toggleEditMode(editMode?: boolean): void {
this.isEditMode = editMode !== undefined ? editMode : !this.isEditMode;
this.updateMoreActions();
this.updatePreview();
this._changeRef.detectChanges();
}
private updateMoreActions(): void {
if (!this.isEditMode && (this.isActive() || this._hover)) {
this.toggleMoreActionsButton(true);
}
else {
this.toggleMoreActionsButton(false);
}
}
private toggleUserSelect(userSelect: boolean): void {
if (!this.output) {
return;
}
if (userSelect) {
DOM.addClass(this.output.nativeElement, USER_SELECT_CLASS);
} else {
DOM.removeClass(this.output.nativeElement, USER_SELECT_CLASS);
}
}
private setFocusAndScroll(): void {
this.toggleEditMode(this.isActive());
if (this.output && this.output.nativeElement) {
(<HTMLElement>this.output.nativeElement).scrollTo({ behavior: 'smooth' });
}
}
protected isActive() {
return this.cellModel && this.cellModel.id === this.activeCellId;
}
protected toggleMoreActionsButton(isActiveOrHovered: boolean) {
this._cellToggleMoreActions.toggleVisible(!isActiveOrHovered);
}
}

View File

@@ -0,0 +1,50 @@
/*---------------------------------------------------------------------------------------------
* 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 {
user-select: none;
padding-left: 8px;
padding-right: 8px;
}
.notebook-preview.actionselect {
user-select: text;
}
text-cell-component table {
border-collapse: collapse;
border-spacing: 0;
border: none;
font-size: 12px;
table-layout: auto;
margin-left: auto;
margin-right: auto;
margin-bottom: 1em;
display: table-row;
}
text-cell-component thead {
vertical-align: bottom;
}
text-cell-component td,
text-cell-component th,
text-cell-component tr {
text-align: left;
vertical-align: middle;
padding: 0.5em 0.5em;
line-height: normal;
white-space: normal;
max-width: none;
border: none;
}
text-cell-component th {
font-weight: bold;
}