diff --git a/src/sql/media/icons/chevron_down.svg b/src/sql/media/icons/chevron_down.svg
new file mode 100644
index 0000000000..3a454b37ab
--- /dev/null
+++ b/src/sql/media/icons/chevron_down.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/sql/media/icons/chevron_down_inverse.svg b/src/sql/media/icons/chevron_down_inverse.svg
new file mode 100644
index 0000000000..b09a9f4e6e
--- /dev/null
+++ b/src/sql/media/icons/chevron_down_inverse.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/sql/media/icons/chevron_up.svg b/src/sql/media/icons/chevron_up.svg
new file mode 100644
index 0000000000..b3fbc2bbe6
--- /dev/null
+++ b/src/sql/media/icons/chevron_up.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/sql/media/icons/chevron_up_inverse.svg b/src/sql/media/icons/chevron_up_inverse.svg
new file mode 100644
index 0000000000..f59d76d752
--- /dev/null
+++ b/src/sql/media/icons/chevron_up_inverse.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/sql/media/icons/close-blue.svg b/src/sql/media/icons/close-blue.svg
new file mode 100644
index 0000000000..44dcff7ea9
--- /dev/null
+++ b/src/sql/media/icons/close-blue.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/sql/media/icons/common-icons.css b/src/sql/media/icons/common-icons.css
index b8c6d54340..1a0b985de0 100644
--- a/src/sql/media/icons/common-icons.css
+++ b/src/sql/media/icons/common-icons.css
@@ -278,6 +278,119 @@
background-image: url('stop_inverse.svg')
}
+/* Notebook cells */
+.codicon.toolbarIconRunInactive {
+ background-image: url('execute_cell_grey.svg');
+}
+.codicon.toolbarIconRun {
+ background-image: url('execute_cell.svg');
+}
+.codicon.toolbarIconRunError {
+ background-image: url('execute_cell_error.svg');
+}
+.codicon.toolbarIconStop {
+ background-image: url('stop_cell_solidanimation.svg');
+}
+.vs-dark .codicon.toolbarIconRunInactive {
+ background-image: url('execute_cell_dark.svg');
+}
+.vs-dark .codicon.toolbarIconRun {
+ background-image: url('execute_cell_white.svg');
+}
+.hc-black .codicon.toolbarIconRunInactive {
+ background-image: url('execute_cell_hc.svg');
+}
+.hc-black .codicon.toolbarIconRun {
+ background-image: url('execute_cell_orange_hc.svg');
+}
+.vs-dark .codicon.toolbarIconStop,
+.hc-black .codicon.toolbarIconStop {
+ background-image: url('stop_cell_solidanimation_inverse.svg');
+}
+
+.codicon.arrow-up {
+ background-image: url("chevron_up.svg");
+}
+.vs-dark .codicon.arrow-up,
+.hc-black .codicon.arrow-up {
+ background-image: url("chevron_up_inverse.svg");
+}
+
+.codicon.arrow-down {
+ background-image: url("chevron_down.svg");
+}
+.vs-dark .codicon.arrow-down,
+.hc-black .codicon.arrow-down {
+ background-image: url("chevron_down_inverse.svg");
+}
+
+/* Icons as masked elements for easy theme switching */
+.codicon.bold {
+ -webkit-mask-image: url('toolbar-bold.svg');
+ mask-image: url('toolbar-bold.svg');
+}
+.codicon.italic {
+ -webkit-mask-image: url('toolbar-italic.svg');
+ mask-image: url('toolbar-italic.svg');
+}
+.codicon.highlight {
+ -webkit-mask-image: url('toolbar-highlight.svg');
+ mask-image: url('toolbar-highlight.svg');
+}
+.codicon.code {
+ -webkit-mask-image: url('toolbar-code.svg');
+ mask-image: url('toolbar-code.svg');
+}
+.codicon.insert-link {
+ -webkit-mask-image: url('toolbar-link.svg');
+ mask-image: url('toolbar-link.svg');
+}
+.codicon.list {
+ -webkit-mask-image: url('toolbar-list.svg');
+ mask-image: url('toolbar-list.svg');
+}
+.codicon.ordered-list {
+ -webkit-mask-image: url('toolbar-ordered-list.svg');
+ mask-image: url('toolbar-ordered-list.svg');
+}
+.codicon.insert-image {
+ -webkit-mask-image: url('toolbar-image.svg');
+ mask-image: url('toolbar-image.svg');
+}
+.codicon.split-toggle-on {
+ -webkit-mask-image: url('toolbar-preview-toggle-on.svg');
+ mask-image: url('toolbar-preview-toggle-on.svg');
+}
+.codicon.split-toggle-off {
+ -webkit-mask-image: url('toolbar-preview-toggle-off.svg');
+ mask-image: url('toolbar-preview-toggle-off.svg');
+}
+
+/* Cell toolbar icons */
+.cell-tool-close {
+ background-image: url('close-blue.svg');
+}
+.cell-tool-edit {
+ background-image: url('edit.svg');
+}
+.cell-tool-add {
+ background-image: url('new-blue.svg');
+}
+.cell-tool-move-up {
+ background-image: url('down-arrow-blue.svg');
+ transform: scale(-1);
+}
+.cell-tool-move-down {
+ background-image: url('down-arrow-blue.svg');
+}
+.cell-tool-delete {
+ background-image: url('garbage-can-blue.svg');
+}
+.cell-tool-more {
+ background-image: url('ellipsis-blue.svg');
+}
+
+
.small {
width: 16px;
height: 16px;
diff --git a/src/sql/media/icons/down-arrow-blue.svg b/src/sql/media/icons/down-arrow-blue.svg
new file mode 100644
index 0000000000..fc472f1410
--- /dev/null
+++ b/src/sql/media/icons/down-arrow-blue.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/sql/media/icons/ellipsis-blue.svg b/src/sql/media/icons/ellipsis-blue.svg
new file mode 100644
index 0000000000..0c6d78f456
--- /dev/null
+++ b/src/sql/media/icons/ellipsis-blue.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/sql/media/icons/execute_cell.svg b/src/sql/media/icons/execute_cell.svg
new file mode 100644
index 0000000000..d83854d930
--- /dev/null
+++ b/src/sql/media/icons/execute_cell.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/sql/media/icons/execute_cell_dark.svg b/src/sql/media/icons/execute_cell_dark.svg
new file mode 100644
index 0000000000..b4d7cac08c
--- /dev/null
+++ b/src/sql/media/icons/execute_cell_dark.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/sql/media/icons/execute_cell_error.svg b/src/sql/media/icons/execute_cell_error.svg
new file mode 100644
index 0000000000..03de5943ed
--- /dev/null
+++ b/src/sql/media/icons/execute_cell_error.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/sql/media/icons/execute_cell_grey.svg b/src/sql/media/icons/execute_cell_grey.svg
new file mode 100644
index 0000000000..6e4c627a97
--- /dev/null
+++ b/src/sql/media/icons/execute_cell_grey.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/sql/media/icons/execute_cell_hc.svg b/src/sql/media/icons/execute_cell_hc.svg
new file mode 100644
index 0000000000..d1ffb0988f
--- /dev/null
+++ b/src/sql/media/icons/execute_cell_hc.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/sql/media/icons/execute_cell_orange_hc.svg b/src/sql/media/icons/execute_cell_orange_hc.svg
new file mode 100644
index 0000000000..afb33e8cdf
--- /dev/null
+++ b/src/sql/media/icons/execute_cell_orange_hc.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/sql/media/icons/execute_cell_white.svg b/src/sql/media/icons/execute_cell_white.svg
new file mode 100644
index 0000000000..0af50fc2b8
--- /dev/null
+++ b/src/sql/media/icons/execute_cell_white.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/sql/media/icons/garbage-can-blue.svg b/src/sql/media/icons/garbage-can-blue.svg
new file mode 100644
index 0000000000..ab482a4fff
--- /dev/null
+++ b/src/sql/media/icons/garbage-can-blue.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/sql/media/icons/new-blue.svg b/src/sql/media/icons/new-blue.svg
new file mode 100644
index 0000000000..0ebe28953d
--- /dev/null
+++ b/src/sql/media/icons/new-blue.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/sql/media/icons/stop_cell_solidanimation.svg b/src/sql/media/icons/stop_cell_solidanimation.svg
new file mode 100644
index 0000000000..38b4eaac4c
--- /dev/null
+++ b/src/sql/media/icons/stop_cell_solidanimation.svg
@@ -0,0 +1,16 @@
+
\ No newline at end of file
diff --git a/src/sql/media/icons/stop_cell_solidanimation_inverse.svg b/src/sql/media/icons/stop_cell_solidanimation_inverse.svg
new file mode 100644
index 0000000000..1a3943d55d
--- /dev/null
+++ b/src/sql/media/icons/stop_cell_solidanimation_inverse.svg
@@ -0,0 +1,16 @@
+
\ No newline at end of file
diff --git a/src/sql/media/icons/toolbar-bold.svg b/src/sql/media/icons/toolbar-bold.svg
new file mode 100644
index 0000000000..8cb47f4d5b
--- /dev/null
+++ b/src/sql/media/icons/toolbar-bold.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/sql/media/icons/toolbar-code.svg b/src/sql/media/icons/toolbar-code.svg
new file mode 100644
index 0000000000..44ede8af94
--- /dev/null
+++ b/src/sql/media/icons/toolbar-code.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/sql/media/icons/toolbar-highlight.svg b/src/sql/media/icons/toolbar-highlight.svg
new file mode 100644
index 0000000000..e235385408
--- /dev/null
+++ b/src/sql/media/icons/toolbar-highlight.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/sql/media/icons/toolbar-image.svg b/src/sql/media/icons/toolbar-image.svg
new file mode 100644
index 0000000000..df9c8276b0
--- /dev/null
+++ b/src/sql/media/icons/toolbar-image.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/sql/media/icons/toolbar-italic.svg b/src/sql/media/icons/toolbar-italic.svg
new file mode 100644
index 0000000000..6cfd388eb5
--- /dev/null
+++ b/src/sql/media/icons/toolbar-italic.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/sql/media/icons/toolbar-link.svg b/src/sql/media/icons/toolbar-link.svg
new file mode 100644
index 0000000000..d4da1b11e2
--- /dev/null
+++ b/src/sql/media/icons/toolbar-link.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/sql/media/icons/toolbar-list.svg b/src/sql/media/icons/toolbar-list.svg
new file mode 100644
index 0000000000..59bb14a7ce
--- /dev/null
+++ b/src/sql/media/icons/toolbar-list.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/sql/media/icons/toolbar-ordered-list.svg b/src/sql/media/icons/toolbar-ordered-list.svg
new file mode 100644
index 0000000000..60158dff0c
--- /dev/null
+++ b/src/sql/media/icons/toolbar-ordered-list.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/sql/media/icons/toolbar-preview-toggle-off.svg b/src/sql/media/icons/toolbar-preview-toggle-off.svg
new file mode 100644
index 0000000000..b5a8538ea6
--- /dev/null
+++ b/src/sql/media/icons/toolbar-preview-toggle-off.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/sql/media/icons/toolbar-preview-toggle-on.svg b/src/sql/media/icons/toolbar-preview-toggle-on.svg
new file mode 100644
index 0000000000..9294a58edc
--- /dev/null
+++ b/src/sql/media/icons/toolbar-preview-toggle-on.svg
@@ -0,0 +1,5 @@
+
diff --git a/src/sql/platform/theme/common/colorRegistry.ts b/src/sql/platform/theme/common/colorRegistry.ts
index f6814a52f0..296055663c 100644
--- a/src/sql/platform/theme/common/colorRegistry.ts
+++ b/src/sql/platform/theme/common/colorRegistry.ts
@@ -41,4 +41,20 @@ export const gradientOne = registerColor('gradientOne', { light: '#f0f0f0', dark
export const gradientTwo = registerColor('gradientTwo', { light: gradientTwoColorOne, dark: gradientTwoColorTwo, hc: gradientTwoColorTwo }, nls.localize('gradientTwo', "The bottom color for the banner image gradient"));
export const gradientBackground = registerColor('gradientBackground', { light: '#fff', dark: 'transparent', hc: 'transparent' }, nls.localize('gradientBackground', "The background color for the banner image gradient"));
+// --- Notebook Colors
+export const toolbarBackground = registerColor('notebook.toolbarBackground', { light: '#F5F5F5', dark: '#252423', hc: '#000000' }, nls.localize('notebook.toolbarBackground', "Notebook: Markdown toolbar background"));
+export const toolbarIcon = registerColor('notebook.toolbarIcon', { light: '#323130', dark: '#FFFFFe', hc: '#FFFFFe' }, nls.localize('notebook.toolbarIcon', "Notebook: Markdown toolbar icons"));
+export const toolbarBottomBorder = registerColor('notebook.toolbarBottomBorder', { light: '#D4D4D4', dark: '#323130', hc: '#E86E58' }, nls.localize('notebook.toolbarBottomBorder', "Notebook: Markdown toolbar bottom border"));
+// Notebook: All cells
+export const cellBorder = registerColor('notebook.cellBorder', { light: '#0078D4', dark: '#0078D4', hc: '#E86E58' }, nls.localize('notebook.cellBorder', "Notebook: Active cell border"));
+// Notebook: Markdown cell
+export const markdownEditorBackground = registerColor('notebook.markdownEditorBackground', { light: '#FFFFFe', dark: '#1B1A19', hc: '#000000' }, nls.localize('notebook.markdownEditorBackground', "Notebook: Markdown editor background"));
+export const splitBorder = registerColor('notebook.splitBorder', { light: '#E6E6E6', dark: '#323130', hc: '#872412' }, nls.localize('notebook.splitBorder', "Notebook: Border between Markdown editor and preview"));
+// Notebook: Code cell
+export const codeEditorBackground = registerColor('notebook.codeEditorBackground', { light: '#F5F5F5', dark: '#333333', hc: '#000000' }, nls.localize('notebook.codeEditorBackground', "Notebook: Code editor background"));
+export const codeEditorBackgroundActive = registerColor('notebook.codeEditorBackgroundActive', { light: '#FFFFFe', dark: null, hc: null }, nls.localize('notebook.codeEditorBackgroundActive', "Notebook: Code editor background of active cell"));
+export const codeEditorLineNumber = registerColor('notebook.codeEditorLineNumber', { light: '#A19F9D', dark: '#A19F9D', hc: '#FFFFFe' }, nls.localize('notebook.codeEditorLineNumber', "Notebook: Code editor line numbers"));
+export const codeEditorToolbarIcon = registerColor('notebook.codeEditorToolbarIcon', { light: '#999999', dark: '#A19F9D', hc: '#FFFFFe' }, nls.localize('notebook.codeEditorToolbarIcon', "Notebook: Code editor toolbar icons"));
+export const codeEditorToolbarBackground = registerColor('notebook.codeEditorToolbarBackground', { light: '#EEEEEE', dark: '#333333', hc: '#000000' }, nls.localize('notebook.codeEditorToolbarBackground', "Notebook: Code editor toolbar background"));
+export const codeEditorToolbarBorder = registerColor('notebook.codeEditorToolbarBorder', { light: '#C8C6C4', dark: '#333333', hc: '#000000' }, nls.localize('notebook.codeEditorToolbarBorder', "Notebook: Code editor toolbar right border"));
diff --git a/src/sql/workbench/contrib/notebook/browser/cellViews/cellToolbar.component.html b/src/sql/workbench/contrib/notebook/browser/cellViews/cellToolbar.component.html
new file mode 100644
index 0000000000..43bea07c7f
--- /dev/null
+++ b/src/sql/workbench/contrib/notebook/browser/cellViews/cellToolbar.component.html
@@ -0,0 +1,15 @@
+
+
diff --git a/src/sql/workbench/contrib/notebook/browser/cellViews/cellToolbar.component.ts b/src/sql/workbench/contrib/notebook/browser/cellViews/cellToolbar.component.ts
new file mode 100644
index 0000000000..28e07d8aa1
--- /dev/null
+++ b/src/sql/workbench/contrib/notebook/browser/cellViews/cellToolbar.component.ts
@@ -0,0 +1,26 @@
+/*---------------------------------------------------------------------------------------------
+ * 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!./cellToolbar';
+
+import { Component } from '@angular/core';
+import { localize } from 'vs/nls';
+
+export const CELL_TOOLBAR_SELECTOR: string = 'cell-toolbar-component';
+
+@Component({
+ selector: CELL_TOOLBAR_SELECTOR,
+ templateUrl: decodeURI(require.toUrl('./cellToolbar.component.html'))
+})
+export class CellToolbarComponent {
+ public buttonEdit = localize('buttonEdit', "Edit");
+ public buttonClose = localize('buttonClose', "Close");
+ public buttonAdd = localize('buttonAdd', "Add new cell");
+ public buttonMoveDown = localize('buttonMoveDown', "Move cell down");
+ public buttonMoveUp = localize('buttonMoveUp', "Move cell up");
+ public buttonDelete = localize('buttonDelete', "Delete cell");
+
+ constructor() {
+ }
+}
diff --git a/src/sql/workbench/contrib/notebook/browser/cellViews/cellToolbar.css b/src/sql/workbench/contrib/notebook/browser/cellViews/cellToolbar.css
new file mode 100644
index 0000000000..68bf5e2cf9
--- /dev/null
+++ b/src/sql/workbench/contrib/notebook/browser/cellViews/cellToolbar.css
@@ -0,0 +1,47 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the Source EULA. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+cell-toolbar-component {
+ position: absolute;
+ left: 25px;
+ top: -21px;
+}
+
+cell-toolbar-component ul {
+ display: inline-block;
+ list-style: none;
+ margin: 0;
+ padding: 5px 10px 0 10px;
+}
+
+cell-toolbar-component li {
+ display: inline-block;
+ margin-right: 4px;
+ text-align: center;
+}
+
+cell-toolbar-component li:last-child {
+ margin-right: 0;
+}
+
+cell-toolbar-component li a {
+ background: 50% 50% no-repeat;
+ display: block;
+ height: 16px;
+ width: 16px;
+}
+
+cell-toolbar-component li div {
+ background: 50% 50% no-repeat;
+ height: 16px;
+ width: 16px;
+}
+
+cell-toolbar-component .offscreen {
+ height: 1px;
+ text-indent: -999999px;
+ margin-top: -1px;
+ position: absolute;
+}
diff --git a/src/sql/workbench/contrib/notebook/browser/cellViews/code.css b/src/sql/workbench/contrib/notebook/browser/cellViews/code.css
index 3a51cbf1f1..d1ebc48294 100644
--- a/src/sql/workbench/contrib/notebook/browser/cellViews/code.css
+++ b/src/sql/workbench/contrib/notebook/browser/cellViews/code.css
@@ -9,14 +9,20 @@ code-component {
display: block;
}
+.notebook-cell:not(.active) code-component .toolbar {
+ border-right-color: transparent!important;
+}
+
code-component .toolbar {
+ border-right-style: solid;
border-right-width: 1px;
- flex: 0 0 auto;
+ box-sizing: border-box;
display: flex;
+ flex: 0 0 auto;
flex-flow:column;
- width: 40px;
min-height: 40px;
- orientation: portrait
+ orientation: portrait;
+ width: 52px;
}
code-component .toolbar.markdown {
@@ -29,36 +35,37 @@ code-component .toolbar .carbon-taskbar {
margin-top: 5px;
}
-code-component .toolbarIconRun {
+code-component .toolbar .codicon {
height: 20px;
- background-image: url('./media/light/execute_cell.svg');
padding-bottom: 10px;
}
-.vs-dark code-component .toolbarIconRun,
-.hc-black code-component .toolbarIconRun {
- background-image: url('./media/dark/execute_cell_inverse.svg');
+.notebook-cell:not(.active):hover code-component .toolbarIconRun {
+ background-image: url('./media/execute_cell_grey.svg');
}
-
-code-component .toolbarIconRunError {
- height: 20px;
- background-image: url('./media/light/execute_cell_error.svg');
- padding-bottom: 10px;
+.vs-dark .notebook-cell:not(.active):hover code-component .toolbarIconRun {
+ background-image: url('./media/execute_cell_dark.svg');
}
-
-code-component .toolbarIconStop {
- height: 20px;
- background-image: url('./media/light/stop_cell_solidanimation.svg');
- padding-bottom: 10px;
-}
-
-.vs-dark code-component .toolbarIconStop,
-.hc-black code-component .toolbarIconStop {
- background-image: url('./media/dark/stop_cell_solidanimation_inverse.svg');
+.hc-black .notebook-cell:not(.active):hover code-component .toolbarIconRun {
+ background-image: url('./media/execute_cell_hc.svg');
}
code-component .editor {
- padding: 5px 0px 5px 0px
+ margin: 14px 0px 5px 0px
+}
+
+code-cell-component code-component .monaco-editor .margin-view-overlays .line-numbers {
+ left: 0!important;
+}
+code-cell-component code-component .monaco-scrollable-element.editor-scrollable.vs {
+ left: 40px!important;
+}
+
+code-cell-component .monaco-editor .margin,
+code-cell-component code-component .monaco-editor,
+code-cell-component code-component .monaco-editor-background,
+code-cell-component code-component .monaco-editor .inputarea.ime-input {
+ background-color: transparent;
}
/* overview ruler */
@@ -84,7 +91,7 @@ code-component .carbon-taskbar .codicon.hideIcon {
padding-left: 0px;
padding-top: 6px;
font-family: monospace;
- font-size: 12px;
+ font-size: 14px;
}
code-component .carbon-taskbar .codicon.hideIcon.execCountTen {
@@ -95,10 +102,6 @@ code-component .carbon-taskbar .codicon.hideIcon.execCountHundred {
margin-left: -6px;
}
-code-component .carbon-taskbar.monaco-toolbar .monaco-action-bar.animated .actions-container {
- padding-left: 10px
-}
-
code-component .hide-component-button {
height: 16px;
width: 100%;
@@ -106,23 +109,5 @@ code-component .hide-component-button {
border-width: 0px;
background-repeat: no-repeat;
background-position: center;
- background-color: inherit;
-}
-
-code-component .hide-component-button.icon-hide-cell {
- background-image: url("./media/light/chevron_up.svg");
-}
-
-code-component .hide-component-button.icon-show-cell {
- background-image: url("./media/light/chevron_down.svg");
-}
-
-.vs-dark code-component .hide-component-button.icon-hide-cell,
-.hc-black code-component .hide-component-button.icon-hide-cell {
- background-image: url("./media/dark/chevron_up_inverse.svg");
-}
-
-.vs-dark code-component .hide-component-button.icon-show-cell,
-.hc-black code-component .hide-component-button.icon-show-cell {
- background-image: url("./media/dark/chevron_down_inverse.svg");
+ background-color: transparent;
}
diff --git a/src/sql/workbench/contrib/notebook/browser/cellViews/collapse.component.html b/src/sql/workbench/contrib/notebook/browser/cellViews/collapse.component.html
index 0a5d144eb1..73fcf69d1e 100644
--- a/src/sql/workbench/contrib/notebook/browser/cellViews/collapse.component.html
+++ b/src/sql/workbench/contrib/notebook/browser/cellViews/collapse.component.html
@@ -5,5 +5,5 @@
*--------------------------------------------------------------------------------------------*/
-->
-
+
diff --git a/src/sql/workbench/contrib/notebook/browser/cellViews/collapse.component.ts b/src/sql/workbench/contrib/notebook/browser/cellViews/collapse.component.ts
index 6a9ef95efa..32745c21d0 100644
--- a/src/sql/workbench/contrib/notebook/browser/cellViews/collapse.component.ts
+++ b/src/sql/workbench/contrib/notebook/browser/cellViews/collapse.component.ts
@@ -20,10 +20,10 @@ export class CollapseComponent extends CellView implements OnInit, OnChanges {
@ViewChild('collapseCellButton', { read: ElementRef }) private collapseCellButtonElement: ElementRef;
private readonly expandButtonTitle = localize('expandCellContents', "Expand code cell contents");
- private readonly expandButtonClass = 'icon-show-cell';
+ private readonly expandButtonClass = 'arrow-down';
private readonly collapseButtonTitle = localize('collapseCellContents', "Collapse code cell contents");
- private readonly collapseButtonClass = 'icon-hide-cell';
+ private readonly collapseButtonClass = 'arrow-up';
@Input() cellModel: ICellModel;
@Input() activeCellId: string;
diff --git a/src/sql/workbench/contrib/notebook/browser/cellViews/markdownToolbar.component.html b/src/sql/workbench/contrib/notebook/browser/cellViews/markdownToolbar.component.html
new file mode 100644
index 0000000000..3a80f4cc3a
--- /dev/null
+++ b/src/sql/workbench/contrib/notebook/browser/cellViews/markdownToolbar.component.html
@@ -0,0 +1,7 @@
+
+
diff --git a/src/sql/workbench/contrib/notebook/browser/cellViews/markdownToolbar.component.ts b/src/sql/workbench/contrib/notebook/browser/cellViews/markdownToolbar.component.ts
new file mode 100644
index 0000000000..ed9cf102ab
--- /dev/null
+++ b/src/sql/workbench/contrib/notebook/browser/cellViews/markdownToolbar.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!./markdownToolbar';
+import { Component, Input, Inject, ViewChild, ElementRef } from '@angular/core';
+import { localize } from 'vs/nls';
+import { ICellModel } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
+import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
+import { TransformMarkdownAction, MarkdownButtonType } from 'sql/workbench/contrib/notebook/browser/markdownToolbarActions';
+import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
+
+export const MARKDOWN_TOOLBAR_SELECTOR: string = 'markdown-toolbar-component';
+
+@Component({
+ selector: MARKDOWN_TOOLBAR_SELECTOR,
+ templateUrl: decodeURI(require.toUrl('./markdownToolbar.component.html'))
+})
+export class MarkdownToolbarComponent {
+ @ViewChild('mdtoolbar', { read: ElementRef }) private mdtoolbar: ElementRef;
+
+ public buttonBold = localize('buttonBold', "Bold");
+ public buttonItalic = localize('buttonItalic', "Italic");
+ public buttonHighlight = localize('buttonHighlight', "Highlight");
+ public buttonCode = localize('buttonCode', "Code");
+ public buttonLink = localize('buttonLink', "Link");
+ public buttonList = localize('buttonList', "List");
+ public buttonOrderedList = localize('buttonOrderedList', "Ordered list");
+ public buttonImage = localize('buttonImage', "Image");
+ public buttonPreview = localize('buttonPreview', "Markdown preview toggle - off");
+
+ @Input() public cellModel: ICellModel;
+ private _actionBar: Taskbar;
+
+ constructor(
+ @Inject(IInstantiationService) private _instantiationService: IInstantiationService
+ ) { }
+
+ ngOnInit() {
+ this.initActionBar();
+ }
+
+ private initActionBar() {
+ let boldButton = this._instantiationService.createInstance(TransformMarkdownAction, 'notebook.boldText', '', 'bold', this.buttonBold, this.cellModel, MarkdownButtonType.BOLD);
+ let italicButton = this._instantiationService.createInstance(TransformMarkdownAction, 'notebook.italicText', '', 'italic', this.buttonItalic, this.cellModel, MarkdownButtonType.ITALIC);
+ let highlightButton = this._instantiationService.createInstance(TransformMarkdownAction, 'notebook.highlightText', '', 'highlight', this.buttonHighlight, this.cellModel, MarkdownButtonType.HIGHLIGHT);
+ let codeButton = this._instantiationService.createInstance(TransformMarkdownAction, 'notebook.codeText', '', 'code', this.buttonCode, this.cellModel, MarkdownButtonType.CODE);
+ let linkButton = this._instantiationService.createInstance(TransformMarkdownAction, 'notebook.linkText', '', 'insert-link', this.buttonLink, this.cellModel, MarkdownButtonType.LINK);
+ let listButton = this._instantiationService.createInstance(TransformMarkdownAction, 'notebook.listText', '', 'list', this.buttonList, this.cellModel, MarkdownButtonType.UNORDERED_LIST);
+ let orderedListButton = this._instantiationService.createInstance(TransformMarkdownAction, 'notebook.orderedText', '', 'ordered-list', this.buttonOrderedList, this.cellModel, MarkdownButtonType.ORDERED_LIST);
+ let imageButton = this._instantiationService.createInstance(TransformMarkdownAction, 'notebook.imageText', '', 'insert-image', this.buttonImage, this.cellModel, MarkdownButtonType.IMAGE);
+
+ let taskbar = this.mdtoolbar.nativeElement;
+ this._actionBar = new Taskbar(taskbar);
+ this._actionBar.context = this;
+ this._actionBar.setContent([
+ { action: boldButton },
+ { action: italicButton },
+ { action: highlightButton },
+ { action: codeButton },
+ { action: linkButton },
+ { action: listButton },
+ { action: orderedListButton },
+ { action: imageButton }
+ ]);
+ }
+}
diff --git a/src/sql/workbench/contrib/notebook/browser/cellViews/markdownToolbar.css b/src/sql/workbench/contrib/notebook/browser/cellViews/markdownToolbar.css
new file mode 100644
index 0000000000..3a37406edd
--- /dev/null
+++ b/src/sql/workbench/contrib/notebook/browser/cellViews/markdownToolbar.css
@@ -0,0 +1,89 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the Source EULA. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+/* Resets */
+.markdown-toolbar .carbon-taskbar li a.action-label {
+ margin: 0;
+ padding: 0;
+}
+.markdown-toolbar .carbon-taskbar.monaco-toolbar .monaco-action-bar.animated .actions-container {
+ padding: 0;
+}
+
+
+.markdown-toolbar {
+ border-bottom-width: 1px;
+ border-bottom-style: solid;
+ display: block;
+ list-style: none;
+ margin: 0;
+ padding: 4px 16px;
+}
+.markdown-toolbar .carbon-taskbar li.action-item {
+ display: inline-block;
+ margin-right: 14px;
+}
+.markdown-toolbar .carbon-taskbar li:nth-child(1) {
+ margin-right: 9px;
+}
+.markdown-toolbar .carbon-taskbar li:nth-child(2) {
+ margin-right: 9px;
+}
+.markdown-toolbar .carbon-taskbar li a {
+ display: inline-block;
+ height: 20px;
+ width: 20px;
+ -webkit-mask-position: center;
+ -webkit-mask-repeat: no-repeat;
+ mask-position: center;
+ mask-repeat: no-repeat;
+}
+
+.markdown-toolbar li a.codicon.bold {
+ -webkit-mask-size: 50% 100%;
+ mask-size: 50% 100%;
+}
+.markdown-toolbar li a.codicon.italic {
+ -webkit-mask-size: 60% 100%;
+ mask-size: 60% 100%;
+}
+.markdown-toolbar li a.codicon.highlight {
+ -webkit-mask-size: 65% 100%;
+ mask-size: 65% 100%;
+}
+.markdown-toolbar li a.codicon.code {
+ -webkit-mask-size: 88% 100%;
+ mask-size: 88% 100%;
+}
+.markdown-toolbar li a.codicon.insert-link {
+ -webkit-mask-size: 80% 100%;
+ mask-size: 80% 100%;
+}
+.markdown-toolbar li a.codicon.list {
+ -webkit-mask-size: 80% 100%;
+ mask-size: 80% 100%;
+}
+.markdown-toolbar li a.codicon.ordered-list {
+ -webkit-mask-size: 86% 100%;
+ mask-size: 86% 100%;
+}
+.markdown-toolbar li a.codicon.insertimage {
+ -webkit-mask-size: 86% 100%;
+ mask-size: 86% 100%;
+}
+.markdown-toolbar li a.codicon.split-toggle-on {
+ -webkit-mask-size: 75% 100%;
+ mask-size: 75% 100%;
+}
+.markdown-toolbar li a.codicon.split-toggle-off {
+ -webkit-mask-size: 75% 100%;
+ mask-size: 75% 100%;
+}
+
+text-cell-component .offscreen {
+ height: 1px;
+ margin-top: -1px;
+ position: absolute;
+ text-indent: -999999px;
+}
diff --git a/src/sql/workbench/contrib/notebook/browser/cellViews/media/execute_cell_dark.svg b/src/sql/workbench/contrib/notebook/browser/cellViews/media/execute_cell_dark.svg
new file mode 100644
index 0000000000..b4d7cac08c
--- /dev/null
+++ b/src/sql/workbench/contrib/notebook/browser/cellViews/media/execute_cell_dark.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/sql/workbench/contrib/notebook/browser/cellViews/media/execute_cell_grey.svg b/src/sql/workbench/contrib/notebook/browser/cellViews/media/execute_cell_grey.svg
new file mode 100644
index 0000000000..6e4c627a97
--- /dev/null
+++ b/src/sql/workbench/contrib/notebook/browser/cellViews/media/execute_cell_grey.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/sql/workbench/contrib/notebook/browser/cellViews/media/execute_cell_hc.svg b/src/sql/workbench/contrib/notebook/browser/cellViews/media/execute_cell_hc.svg
new file mode 100644
index 0000000000..d1ffb0988f
--- /dev/null
+++ b/src/sql/workbench/contrib/notebook/browser/cellViews/media/execute_cell_hc.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/sql/workbench/contrib/notebook/browser/cellViews/media/light/execute_cell.svg b/src/sql/workbench/contrib/notebook/browser/cellViews/media/light/execute_cell.svg
index 2a8f889c1e..9b4c857179 100644
--- a/src/sql/workbench/contrib/notebook/browser/cellViews/media/light/execute_cell.svg
+++ b/src/sql/workbench/contrib/notebook/browser/cellViews/media/light/execute_cell.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
diff --git a/src/sql/workbench/contrib/notebook/browser/cellViews/media/light/execute_cell_grey.svg b/src/sql/workbench/contrib/notebook/browser/cellViews/media/light/execute_cell_grey.svg
new file mode 100644
index 0000000000..e1d4ff5773
--- /dev/null
+++ b/src/sql/workbench/contrib/notebook/browser/cellViews/media/light/execute_cell_grey.svg
@@ -0,0 +1 @@
+
diff --git a/src/sql/workbench/contrib/notebook/browser/cellViews/media/toolbar-bold.svg b/src/sql/workbench/contrib/notebook/browser/cellViews/media/toolbar-bold.svg
new file mode 100644
index 0000000000..8cb47f4d5b
--- /dev/null
+++ b/src/sql/workbench/contrib/notebook/browser/cellViews/media/toolbar-bold.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/sql/workbench/contrib/notebook/browser/cellViews/media/toolbar-code.svg b/src/sql/workbench/contrib/notebook/browser/cellViews/media/toolbar-code.svg
new file mode 100644
index 0000000000..44ede8af94
--- /dev/null
+++ b/src/sql/workbench/contrib/notebook/browser/cellViews/media/toolbar-code.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/sql/workbench/contrib/notebook/browser/cellViews/media/toolbar-highlight.svg b/src/sql/workbench/contrib/notebook/browser/cellViews/media/toolbar-highlight.svg
new file mode 100644
index 0000000000..e235385408
--- /dev/null
+++ b/src/sql/workbench/contrib/notebook/browser/cellViews/media/toolbar-highlight.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/sql/workbench/contrib/notebook/browser/cellViews/media/toolbar-image.svg b/src/sql/workbench/contrib/notebook/browser/cellViews/media/toolbar-image.svg
new file mode 100644
index 0000000000..df9c8276b0
--- /dev/null
+++ b/src/sql/workbench/contrib/notebook/browser/cellViews/media/toolbar-image.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/sql/workbench/contrib/notebook/browser/cellViews/media/toolbar-italic.svg b/src/sql/workbench/contrib/notebook/browser/cellViews/media/toolbar-italic.svg
new file mode 100644
index 0000000000..6cfd388eb5
--- /dev/null
+++ b/src/sql/workbench/contrib/notebook/browser/cellViews/media/toolbar-italic.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/sql/workbench/contrib/notebook/browser/cellViews/media/toolbar-link.svg b/src/sql/workbench/contrib/notebook/browser/cellViews/media/toolbar-link.svg
new file mode 100644
index 0000000000..d4da1b11e2
--- /dev/null
+++ b/src/sql/workbench/contrib/notebook/browser/cellViews/media/toolbar-link.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/sql/workbench/contrib/notebook/browser/cellViews/media/toolbar-list.svg b/src/sql/workbench/contrib/notebook/browser/cellViews/media/toolbar-list.svg
new file mode 100644
index 0000000000..59bb14a7ce
--- /dev/null
+++ b/src/sql/workbench/contrib/notebook/browser/cellViews/media/toolbar-list.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/sql/workbench/contrib/notebook/browser/cellViews/media/toolbar-ordered-list.svg b/src/sql/workbench/contrib/notebook/browser/cellViews/media/toolbar-ordered-list.svg
new file mode 100644
index 0000000000..60158dff0c
--- /dev/null
+++ b/src/sql/workbench/contrib/notebook/browser/cellViews/media/toolbar-ordered-list.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/sql/workbench/contrib/notebook/browser/cellViews/media/toolbar-preview-toggle-off.svg b/src/sql/workbench/contrib/notebook/browser/cellViews/media/toolbar-preview-toggle-off.svg
new file mode 100644
index 0000000000..b5a8538ea6
--- /dev/null
+++ b/src/sql/workbench/contrib/notebook/browser/cellViews/media/toolbar-preview-toggle-off.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/sql/workbench/contrib/notebook/browser/cellViews/media/toolbar-preview-toggle-on.svg b/src/sql/workbench/contrib/notebook/browser/cellViews/media/toolbar-preview-toggle-on.svg
new file mode 100644
index 0000000000..9294a58edc
--- /dev/null
+++ b/src/sql/workbench/contrib/notebook/browser/cellViews/media/toolbar-preview-toggle-on.svg
@@ -0,0 +1,5 @@
+
diff --git a/src/sql/workbench/contrib/notebook/browser/cellViews/textCell.component.html b/src/sql/workbench/contrib/notebook/browser/cellViews/textCell.component.html
index 72604439b1..9603ca29c3 100644
--- a/src/sql/workbench/contrib/notebook/browser/cellViews/textCell.component.html
+++ b/src/sql/workbench/contrib/notebook/browser/cellViews/textCell.component.html
@@ -5,6 +5,7 @@
*--------------------------------------------------------------------------------------------*/
-->
+
diff --git a/src/sql/workbench/contrib/notebook/browser/cellViews/textCell.component.ts b/src/sql/workbench/contrib/notebook/browser/cellViews/textCell.component.ts
index b58d8d8fee..081e78d255 100644
--- a/src/sql/workbench/contrib/notebook/browser/cellViews/textCell.component.ts
+++ b/src/sql/workbench/contrib/notebook/browser/cellViews/textCell.component.ts
@@ -31,7 +31,6 @@ import { IColorTheme } from 'vs/platform/theme/common/themeService';
export const TEXT_SELECTOR: string = 'text-cell-component';
const USER_SELECT_CLASS = 'actionselect';
-
@Component({
selector: TEXT_SELECTOR,
templateUrl: decodeURI(require.toUrl('./textCell.component.html'))
diff --git a/src/sql/workbench/contrib/notebook/browser/cellViews/textCell.css b/src/sql/workbench/contrib/notebook/browser/cellViews/textCell.css
index 236b314921..c03714eb82 100644
--- a/src/sql/workbench/contrib/notebook/browser/cellViews/textCell.css
+++ b/src/sql/workbench/contrib/notebook/browser/cellViews/textCell.css
@@ -7,6 +7,12 @@ text-cell-component {
display: block;
}
+.notebookEditor .notebook-cell.active text-cell-component code-component {
+ border-color: transparent;
+ border-bottom-width: 1px;
+ border-style: solid;
+}
+
text-cell-component .notebook-preview {
user-select: none;
padding-left: 8px;
@@ -17,6 +23,16 @@ text-cell-component .notebook-preview {
user-select: text;
}
+text-cell-component code-component .monaco-scrollable-element.editor-scrollable.vs {
+ left: 16px!important;
+}
+text-cell-component .monaco-editor .margin,
+text-cell-component code-component .monaco-editor,
+text-cell-component code-component .monaco-editor-background,
+text-cell-component code-component .monaco-editor .inputarea.ime-input {
+ background-color: transparent;
+}
+
.vs .notebook-preview .rangeHighlight {
background-color: rgba(255, 255, 0, 0.2)
}
diff --git a/src/sql/workbench/contrib/notebook/browser/markdownToolbarActions.ts b/src/sql/workbench/contrib/notebook/browser/markdownToolbarActions.ts
new file mode 100644
index 0000000000..257ed782e7
--- /dev/null
+++ b/src/sql/workbench/contrib/notebook/browser/markdownToolbarActions.ts
@@ -0,0 +1,344 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the Source EULA. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { Action } from 'vs/base/common/actions';
+
+import { INotebookEditor, INotebookService } from 'sql/workbench/services/notebook/browser/notebookService';
+import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
+import { IRange } from 'vs/editor/common/core/range';
+import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model';
+import { TextModel } from 'vs/editor/common/model/textModel';
+import { ICellModel } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
+import { QueryTextEditor } from 'sql/workbench/browser/modelComponents/queryTextEditor';
+import { Selection } from 'vs/editor/common/core/selection';
+
+
+
+// Action to decorate markdown
+export class TransformMarkdownAction extends Action {
+
+ constructor(
+ id: string,
+ label: string,
+ cssClass: string,
+ tooltip: string,
+ private _cellModel: ICellModel,
+ private _type: MarkdownButtonType,
+ @INotebookService private _notebookService: INotebookService
+ ) {
+ super(id, label, cssClass);
+ this._tooltip = tooltip;
+ }
+ public run(context: any): Promise {
+ return new Promise((resolve, reject) => {
+ try {
+ let markdownTextTransformer = new MarkdownTextTransformer(this._notebookService, this._cellModel);
+ markdownTextTransformer.transformText(this._type);
+ resolve(true);
+ } catch (e) {
+ reject(e);
+ }
+ });
+ }
+}
+
+export class MarkdownTextTransformer {
+
+ private _notebookEditor: INotebookEditor;
+ constructor(private _notebookService: INotebookService, private _cellModel: ICellModel) { }
+
+ public transformText(type: MarkdownButtonType): void {
+ let editorControl = this.getEditorControl();
+ if (editorControl) {
+ let selections = editorControl.getSelections();
+ // TODO: Support replacement for multiple selections
+ let selection = selections[0];
+ let nothingSelected = this.editorHasNoSelection(selection);
+ let startRange: IRange = {
+ startColumn: selection.startColumn,
+ endColumn: selection.startColumn,
+ startLineNumber: selection.startLineNumber,
+ endLineNumber: selection.startLineNumber
+ };
+
+ // Get text to insert before selection
+ let beginInsertedCode = this.getStartTextToInsert(type);
+ // Get text to insert after selection
+ let endInsertedCode = this.getEndTextToInsert(type);
+
+ // endInsertedCode can be an empty string (e.g. for unordered list), so no need to check for that as well
+ if (beginInsertedCode) {
+ let endRange: IRange = {
+ startColumn: selection.endColumn,
+ endColumn: selection.endColumn,
+ startLineNumber: selection.endLineNumber,
+ endLineNumber: selection.endLineNumber
+ };
+ let editorModel = editorControl.getModel() as TextModel;
+ let isUndo = false;
+ if (editorModel) {
+ let markdownLineType = this.getMarkdownLineType(type);
+ isUndo = this.isUndoOperation(selection, type, markdownLineType, editorModel);
+ if (isUndo) {
+ if (markdownLineType === MarkdownLineType.BEGIN_AND_END_LINES) {
+ startRange = this.getIRangeWithOffsets(startRange, -1 * beginInsertedCode.length, 0, 0, 0);
+ endRange = this.getIRangeWithOffsets(endRange, 0, 0, endInsertedCode.length, 0);
+ editorModel.pushEditOperations(selections, [{ range: endRange, text: '' }, { range: startRange, text: '' }], null);
+ } else {
+ let operations: IIdentifiedSingleEditOperation[] = [];
+ startRange = this.getIRangeWithOffsets(startRange, 0, 0, beginInsertedCode.length, 0);
+ for (let i = 0; i < selection.endLineNumber - selection.startLineNumber + 1; i++) {
+ operations.push({ range: this.transformRangeByLineOffset(startRange, i), text: '' });
+ }
+ editorModel.pushEditOperations(selections, operations, null);
+ }
+ } else {
+ // If the markdown we're inserting only needs to be added to the begin and end lines, add those edit operations directly
+ if (markdownLineType === MarkdownLineType.BEGIN_AND_END_LINES) {
+ editorModel.pushEditOperations(selections, [{ range: startRange, text: beginInsertedCode }, { range: endRange, text: endInsertedCode }], null);
+ } else { // Otherwise, add an operation per line (plus the operation at the last column + line)
+ let operations: IIdentifiedSingleEditOperation[] = [];
+ for (let i = 0; i < selection.endLineNumber - selection.startLineNumber + 1; i++) {
+ operations.push({ range: this.transformRangeByLineOffset(startRange, i), text: beginInsertedCode });
+ }
+ operations.push({ range: endRange, text: endInsertedCode });
+ editorModel.pushEditOperations(selections, operations, null);
+ }
+ }
+ }
+
+ // If selection end is on same line as beginning, need to add offset for number of characters inserted
+ // Otherwise, the selection will not be correct after the transformation
+ let offset = selection.startLineNumber === selection.endLineNumber ? beginInsertedCode.length : 0;
+ endRange = this.getIRangeWithOffsets(endRange, offset, 0, offset, 0);
+ this.setEndSelection(endRange, type, editorControl, nothingSelected, isUndo);
+ }
+ // Always give focus back to the editor after pressing the button
+ editorControl.focus();
+ }
+ }
+
+ // For items like lists (where we need to insert a character at the beginning of each line), create
+ // range object for that range
+ private transformRangeByLineOffset(range: IRange, lineOffset: number): IRange {
+ return {
+ startColumn: lineOffset === 0 ? range.startColumn : 1,
+ endColumn: range.endColumn,
+ startLineNumber: range.endLineNumber + lineOffset,
+ endLineNumber: range.endLineNumber + lineOffset
+ };
+ }
+
+ private getStartTextToInsert(type: MarkdownButtonType): string {
+ switch (type) {
+ case MarkdownButtonType.BOLD:
+ return '**';
+ case MarkdownButtonType.ITALIC:
+ return '_';
+ case MarkdownButtonType.CODE:
+ return '```\n';
+ case MarkdownButtonType.LINK:
+ return '[';
+ case MarkdownButtonType.UNORDERED_LIST:
+ return '- ';
+ case MarkdownButtonType.ORDERED_LIST:
+ return '1. ';
+ case MarkdownButtonType.IMAGE:
+ return '![';
+ case MarkdownButtonType.HIGHLIGHT:
+ return '';
+ default:
+ return '';
+ }
+ }
+
+ private getEndTextToInsert(type: MarkdownButtonType): string {
+ switch (type) {
+ case MarkdownButtonType.BOLD:
+ return '**';
+ case MarkdownButtonType.ITALIC:
+ return '_';
+ case MarkdownButtonType.CODE:
+ return '\n```';
+ case MarkdownButtonType.LINK:
+ case MarkdownButtonType.IMAGE:
+ return ']()';
+ case MarkdownButtonType.HIGHLIGHT:
+ return '';
+ case MarkdownButtonType.UNORDERED_LIST:
+ case MarkdownButtonType.ORDERED_LIST:
+ default:
+ return '';
+ }
+ }
+
+ private getMarkdownLineType(type: MarkdownButtonType): MarkdownLineType {
+ switch (type) {
+ case MarkdownButtonType.UNORDERED_LIST:
+ case MarkdownButtonType.ORDERED_LIST:
+ return MarkdownLineType.EVERY_LINE;
+ default:
+ return MarkdownLineType.BEGIN_AND_END_LINES;
+ }
+ }
+
+ // Get offset from the end column for editor selection
+ // For example, when inserting a link, we want to have the cursor be present in between the brackets
+ private getColumnOffsetForSelection(type: MarkdownButtonType, nothingSelected: boolean): number {
+ if (nothingSelected) {
+ return 0;
+ }
+ switch (type) {
+ case MarkdownButtonType.LINK:
+ return 2;
+ case MarkdownButtonType.IMAGE:
+ return 2;
+ // -1 is considered as having no explicit offset, so do not do anything with selection
+ default: return -1;
+ }
+ }
+
+ private getEditorControl(): CodeEditorWidget | undefined {
+ if (!this._notebookEditor) {
+ this._notebookEditor = this._notebookService.findNotebookEditor(this._cellModel.notebookModel.notebookUri);
+ }
+ if (this._notebookEditor?.cellEditors?.length > 0) {
+ // Find cell editor provider via cell guid
+ let cellEditorProvider = this._notebookEditor.cellEditors.find(e => e.cellGuid() === this._cellModel.cellGuid);
+ if (cellEditorProvider) {
+ let editor = cellEditorProvider.getEditor() as QueryTextEditor;
+ if (editor) {
+ let editorControl = editor.getControl() as CodeEditorWidget;
+ return editorControl;
+ }
+ }
+ }
+ return undefined;
+ }
+
+ private editorHasNoSelection(selection: Selection): boolean {
+ return !selection || (selection.startLineNumber === selection.endLineNumber && selection.startColumn === selection.endColumn);
+ }
+
+ /**
+ * Sets the end selection state after the transform has occurred
+ * @param endRange range for end text that was inserted
+ * @param type MarkdownButtonType
+ * @param editorControl code editor widget
+ * @param noSelection controls whether there was no previous selection in the editor
+ */
+ private setEndSelection(endRange: IRange, type: MarkdownButtonType, editorControl: CodeEditorWidget, noSelection: boolean, isUndo: boolean): void {
+ if (!endRange || !editorControl || isUndo) {
+ return;
+ }
+ let offset = this.getColumnOffsetForSelection(type, noSelection);
+ if (offset > -1) {
+ let newRange: IRange = {
+ startColumn: endRange.startColumn + offset,
+ startLineNumber: endRange.startLineNumber,
+ endColumn: endRange.startColumn + offset,
+ endLineNumber: endRange.endLineNumber
+ };
+ editorControl.setSelection(newRange);
+ } else {
+ if (this.getMarkdownLineType(type) === MarkdownLineType.BEGIN_AND_END_LINES) {
+ let currentSelection = editorControl.getSelection();
+ editorControl.setSelection({
+ startColumn: currentSelection.startColumn + this.getStartTextToInsert(type).length,
+ startLineNumber: currentSelection.startLineNumber,
+ endColumn: currentSelection.endColumn - this.getEndTextToInsert(type).length,
+ endLineNumber: currentSelection.endLineNumber
+ });
+ }
+ }
+ }
+
+ /**
+ * Determine if user wants to perform an undo operation
+ * @param selection current user selection
+ * @param type markdown button type
+ * @param lineType markdown line type
+ * @param editorModel text model for the cell
+ */
+ private isUndoOperation(selection: Selection, type: MarkdownButtonType, lineType: MarkdownLineType, editorModel: TextModel): boolean {
+ if (lineType === MarkdownLineType.BEGIN_AND_END_LINES) {
+ let selectedText = this.getExtendedSelectedText(selection, type, lineType, editorModel);
+ return selectedText && selectedText.startsWith(this.getStartTextToInsert(type)) && selectedText.endsWith(this.getEndTextToInsert(type));
+ } else {
+ return this.everyLineMatchesBeginString(selection, type, editorModel);
+ }
+ }
+
+ /**
+ * Gets the extended selected text (current selection + potential beginning + ending transformed text)
+ * @param selection Current selection in editor
+ * @param type Markdown Button Type
+ * @param lineType Markdown Line Type
+ * @param editorModel TextModel
+ */
+ private getExtendedSelectedText(selection: Selection, type: MarkdownButtonType, lineType: MarkdownLineType, editorModel: TextModel): string {
+ if (lineType === MarkdownLineType.BEGIN_AND_END_LINES) {
+ return editorModel.getValueInRange({
+ startColumn: selection.startColumn - this.getStartTextToInsert(type).length,
+ startLineNumber: selection.startLineNumber,
+ endColumn: selection.endColumn + this.getEndTextToInsert(type).length,
+ endLineNumber: selection.endLineNumber
+ });
+ }
+ return '';
+ }
+
+ /**
+ * Returns whether all lines start with the expected transformed text for actions that match the EVERY_LINE line type
+ * @param selection Current selection in editor
+ * @param type Markdown Button Type
+ * @param editorModel TextModel
+ */
+ private everyLineMatchesBeginString(selection: Selection, type: MarkdownButtonType, editorModel: TextModel): boolean {
+ if (this.getMarkdownLineType(type) !== MarkdownLineType.EVERY_LINE) {
+ return false;
+ }
+ for (let selectionLine = selection.startLineNumber; selectionLine <= selection.endLineNumber; selectionLine++) {
+ if (!editorModel.getLineContent(selectionLine).startsWith(this.getStartTextToInsert(type))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Create new IRange object with arbitrary offsets
+ * @param initialRange range object
+ * @param startColumnOffset
+ * @param startLineNumberOffset
+ * @param endColumnOffset
+ * @param endLineNumberOffset
+ */
+ private getIRangeWithOffsets(initialRange: IRange, startColumnOffset = 0, startLineNumberOffset = 0, endColumnOffset = 0, endLineNumberOffset = 0): IRange {
+ return {
+ startColumn: initialRange.startColumn + startColumnOffset,
+ startLineNumber: initialRange.startLineNumber + startLineNumberOffset,
+ endColumn: initialRange.endColumn + endColumnOffset,
+ endLineNumber: initialRange.endLineNumber + endLineNumberOffset
+ };
+ }
+}
+
+export enum MarkdownButtonType {
+ BOLD,
+ ITALIC,
+ CODE,
+ HIGHLIGHT,
+ LINK,
+ UNORDERED_LIST,
+ ORDERED_LIST,
+ IMAGE
+}
+
+// If ALL_LINES, we need to insert markdown at each line (e.g. lists)
+export enum MarkdownLineType {
+ BEGIN_AND_END_LINES,
+ EVERY_LINE
+}
diff --git a/src/sql/workbench/contrib/notebook/browser/notebook.component.html b/src/sql/workbench/contrib/notebook/browser/notebook.component.html
index bd3626aa07..723fe18a20 100644
--- a/src/sql/workbench/contrib/notebook/browser/notebook.component.html
+++ b/src/sql/workbench/contrib/notebook/browser/notebook.component.html
@@ -9,17 +9,6 @@