diff --git a/extensions/notebook/package.json b/extensions/notebook/package.json index 1863ef7489..47fc5bd545 100644 --- a/extensions/notebook/package.json +++ b/extensions/notebook/package.json @@ -307,6 +307,10 @@ { "command": "notebook.command.codeBlock", "title": "%notebook.command.codeBlock%" + }, + { + "command": "notebook.command.highlightText", + "title": "%notebook.command.highlightText%" } ], "languages": [ @@ -457,6 +461,10 @@ { "command": "notebook.command.codeBlock", "when": "activeEditor == workbench.editor.notebookEditor && editorLangId == markdown" + }, + { + "command": "notebook.command.highlightText", + "when": "activeEditor == workbench.editor.notebookEditor && editorLangId == markdown" } ], "touchBar": [ @@ -639,6 +647,11 @@ "command": "notebook.command.codeBlock", "key": "Ctrl+Shift+K", "when": "activeEditor == workbench.editor.notebookEditor && editorLangId == markdown" + }, + { + "command": "notebook.command.highlightText", + "key": "Ctrl+Shift+H", + "when": "activeEditor == workbench.editor.notebookEditor && editorLangId == markdown" } ], "notebook.languagemagics": [ diff --git a/extensions/notebook/package.nls.json b/extensions/notebook/package.nls.json index 7a41d4576d..c010198e92 100644 --- a/extensions/notebook/package.nls.json +++ b/extensions/notebook/package.nls.json @@ -27,6 +27,7 @@ "notebook.command.italicizeText": "Italicize Markdown Text", "notebook.command.underlineText": "Underline Markdown Text", "notebook.command.codeBlock": "Add Code Block", + "notebook.command.highlightText": "Highlight Markdown Text", "title.analyzeJupyterNotebook": "Analyze in Notebook", "title.newJupyterNotebook": "New Notebook", "title.openJupyterNotebook": "Open Notebook", diff --git a/src/sql/workbench/contrib/notebook/browser/cellViews/markdownToolbar.component.ts b/src/sql/workbench/contrib/notebook/browser/cellViews/markdownToolbar.component.ts index b905430a52..2fb8b721cb 100644 --- a/src/sql/workbench/contrib/notebook/browser/cellViews/markdownToolbar.component.ts +++ b/src/sql/workbench/contrib/notebook/browser/cellViews/markdownToolbar.component.ts @@ -5,7 +5,7 @@ import 'vs/css!./markdownToolbar'; import * as DOM from 'vs/base/browser/dom'; import { Button, IButtonStyles } from 'sql/base/browser/ui/button/button'; -import { Component, Input, Inject, ViewChild, ElementRef } from '@angular/core'; +import { Component, Input, Inject, ViewChild, ElementRef, HostListener } from '@angular/core'; import { localize } from 'vs/nls'; import { CellEditModes, ICellModel } from 'sql/workbench/services/notebook/browser/models/modelInterfaces'; import { ITaskbarContent, Taskbar } from 'sql/base/browser/ui/taskbar/taskbar'; @@ -36,6 +36,34 @@ const linksRegex = /\[(?.+)\]\((?[^ ]+)(?: "(?.+)")?\)/; export class MarkdownToolbarComponent extends AngularDisposable { @ViewChild('mdtoolbar', { read: ElementRef }) private mdtoolbar: ElementRef; + @HostListener('document:keydown', ['$event']) + async onkeydown(e: KeyboardEvent) { + if (this.cellModel?.currentMode === CellEditModes.SPLIT || this.cellModel?.currentMode === CellEditModes.MARKDOWN) { + let markdownTextTransformer = new MarkdownTextTransformer(this._notebookService, this.cellModel); + if ((e.ctrlKey || e.metaKey) && e.key === 'b') { + // Bold Text + DOM.EventHelper.stop(e, true); + await markdownTextTransformer.transformText(MarkdownButtonType.BOLD); + } else if ((e.ctrlKey || e.metaKey) && e.key === 'i') { + // Italicize text + DOM.EventHelper.stop(e, true); + await markdownTextTransformer.transformText(MarkdownButtonType.ITALIC); + } else if ((e.ctrlKey || e.metaKey) && e.key === 'u') { + // Underline text + DOM.EventHelper.stop(e, true); + await markdownTextTransformer.transformText(MarkdownButtonType.UNDERLINE); + } else if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === 'k') { + // Code Block + DOM.EventHelper.stop(e, true); + await markdownTextTransformer.transformText(MarkdownButtonType.CODE); + } else if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === 'h') { + // Highlight Text + DOM.EventHelper.stop(e, true); + await markdownTextTransformer.transformText(MarkdownButtonType.HIGHLIGHT); + } + } + } + public previewFeaturesEnabled: boolean = false; public buttonBold = localize('buttonBold', "Bold"); 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 866f0a857c..24fb69ea4e 100644 --- a/src/sql/workbench/contrib/notebook/browser/cellViews/textCell.component.ts +++ b/src/sql/workbench/contrib/notebook/browser/cellViews/textCell.component.ts @@ -30,6 +30,7 @@ import { CodeComponent } from 'sql/workbench/contrib/notebook/browser/cellViews/ import { NotebookRange, ICellEditorProvider, INotebookService } from 'sql/workbench/services/notebook/browser/notebookService'; import { HTMLMarkdownConverter } from 'sql/workbench/contrib/notebook/browser/htmlMarkdownConverter'; import { NotebookInput } from 'sql/workbench/contrib/notebook/browser/models/notebookInput'; +import { highlightSelectedText } from 'sql/workbench/contrib/notebook/browser/utils'; export const TEXT_SELECTOR: string = 'text-cell-component'; const USER_SELECT_CLASS = 'actionselect'; @@ -73,30 +74,34 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges { // Select all text if ((e.ctrlKey || e.metaKey) && e.key === 'a') { preventDefaultAndExecCommand(e, 'selectAll'); - // Redo text } else if ((e.metaKey && e.shiftKey && e.key === 'z') || (e.ctrlKey && e.key === 'y') && !this.markdownMode) { + // Redo text this.redoRichTextChange(); - // Undo text } else if ((e.ctrlKey || e.metaKey) && e.key === 'z') { + // Undo text this.undoRichTextChange(); - // Outdent text } else if (e.shiftKey && e.key === 'Tab') { + // Outdent text preventDefaultAndExecCommand(e, 'outdent'); - // Indent text } else if (e.key === 'Tab') { + // Indent text preventDefaultAndExecCommand(e, 'indent'); - // Bold text } else if ((e.ctrlKey || e.metaKey) && e.key === 'b') { + // Bold text preventDefaultAndExecCommand(e, 'bold'); - // Italicize text } else if ((e.ctrlKey || e.metaKey) && e.key === 'i') { + // Italicize text preventDefaultAndExecCommand(e, 'italic'); - // Underline text } else if ((e.ctrlKey || e.metaKey) && e.key === 'u') { + // Underline text preventDefaultAndExecCommand(e, 'underline'); - // Code Block } else if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === 'k') { + // Code Block preventDefaultAndExecCommand(e, 'formatBlock', false, 'pre'); + } else if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === 'h') { + // Highlight Text + DOM.EventHelper.stop(e, true); + highlightSelectedText(); } } } diff --git a/src/sql/workbench/contrib/notebook/browser/markdownToolbarActions.ts b/src/sql/workbench/contrib/notebook/browser/markdownToolbarActions.ts index fbd62f0810..8d1328be0c 100644 --- a/src/sql/workbench/contrib/notebook/browser/markdownToolbarActions.ts +++ b/src/sql/workbench/contrib/notebook/browser/markdownToolbarActions.ts @@ -16,6 +16,7 @@ import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Position } from 'vs/editor/common/core/position'; import { MarkdownToolbarComponent } from 'sql/workbench/contrib/notebook/browser/cellViews/markdownToolbar.component'; import { IEditor } from 'vs/editor/common/editorCommon'; +import { highlightSelectedText } from 'sql/workbench/contrib/notebook/browser/utils'; export class TransformMarkdownAction extends Action { @@ -58,44 +59,7 @@ export class TransformMarkdownAction extends Action { document.execCommand('formatBlock', false, 'H3'); break; case MarkdownButtonType.HIGHLIGHT: - let selectionFocusNode = document.getSelection()?.focusNode; - // Find if element is wrapped in <mark></mark> - while (selectionFocusNode?.parentNode?.nodeName?.toLowerCase() && selectionFocusNode?.parentNode?.nodeName?.toLowerCase() !== 'mark') { - selectionFocusNode = selectionFocusNode.parentNode; - } - // Find if element is wrapped in <span background-color="yellow"> - if (selectionFocusNode?.parentNode?.nodeName?.toLowerCase() !== 'mark') { - selectionFocusNode = document.getSelection()?.focusNode; - while (selectionFocusNode?.parentNode?.nodeName?.toLowerCase() && selectionFocusNode?.parentNode?.nodeName?.toLowerCase() !== 'span' && selectionFocusNode?.parentElement?.style?.backgroundColor !== 'yellow') { - selectionFocusNode = selectionFocusNode.parentNode; - } - } - let nodeName = selectionFocusNode?.parentNode?.nodeName?.toLowerCase(); - let backgroundColor = selectionFocusNode?.parentElement?.style?.backgroundColor; - if (nodeName === 'mark') { - let oldParent = selectionFocusNode.parentNode; - let newParent = selectionFocusNode.parentNode.parentNode; - let oldParentNextSibling = oldParent.nextSibling; - // Remove mark element, reparent - while (oldParent.childNodes.length > 0) { - // If no next sibling, then old parent was the final child node, so we can append - if (!oldParentNextSibling) { - newParent.appendChild(oldParent.firstChild); - } else { - newParent.insertBefore(oldParent.firstChild, oldParentNextSibling); - } - } - // Empty span required to force an input so that HTML change is seen from text cell component - // This span doesn't have any effect on the markdown generated. - document.execCommand('formatBlock', false, 'span'); - } else if (selectionFocusNode?.parentNode?.nodeName?.toLowerCase() === 'span' && backgroundColor === 'yellow') { - selectionFocusNode.parentElement.style.backgroundColor = ''; - // Empty span required to force an input so that HTML change is seen from text cell component - // This span doesn't have any effect on the markdown generated. - document.execCommand('formatBlock', false, 'span'); - } else { - document.execCommand('hiliteColor', false, 'Yellow'); - } + highlightSelectedText(); break; case MarkdownButtonType.IMAGE: case MarkdownButtonType.IMAGE_PREVIEW: diff --git a/src/sql/workbench/contrib/notebook/browser/utils.ts b/src/sql/workbench/contrib/notebook/browser/utils.ts new file mode 100644 index 0000000000..284265894a --- /dev/null +++ b/src/sql/workbench/contrib/notebook/browser/utils.ts @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export function highlightSelectedText(): void { + let selectionFocusNode = document.getSelection()?.focusNode; + // Find if element is wrapped in <mark></mark> + while (selectionFocusNode?.parentNode?.nodeName?.toLowerCase() && selectionFocusNode?.parentNode?.nodeName?.toLowerCase() !== 'mark') { + selectionFocusNode = selectionFocusNode.parentNode; + } + // Find if element is wrapped in <span background-color="yellow"> + if (selectionFocusNode?.parentNode?.nodeName?.toLowerCase() !== 'mark') { + selectionFocusNode = document.getSelection()?.focusNode; + while (selectionFocusNode?.parentNode?.nodeName?.toLowerCase() && selectionFocusNode?.parentNode?.nodeName?.toLowerCase() !== 'span' && selectionFocusNode?.parentElement?.style?.backgroundColor !== 'yellow') { + selectionFocusNode = selectionFocusNode.parentNode; + } + } + let nodeName = selectionFocusNode?.parentNode?.nodeName?.toLowerCase(); + let backgroundColor = selectionFocusNode?.parentElement?.style?.backgroundColor; + if (nodeName === 'mark') { + let oldParent = selectionFocusNode.parentNode; + let newParent = selectionFocusNode.parentNode.parentNode; + let oldParentNextSibling = oldParent.nextSibling; + // Remove mark element, reparent + while (oldParent.childNodes.length > 0) { + // If no next sibling, then old parent was the final child node, so we can append + if (!oldParentNextSibling) { + newParent.appendChild(oldParent.firstChild); + } else { + newParent.insertBefore(oldParent.firstChild, oldParentNextSibling); + } + } + // Empty span required to force an input so that HTML change is seen from text cell component + // This span doesn't have any effect on the markdown generated. + document.execCommand('formatBlock', false, 'span'); + } else if (selectionFocusNode?.parentNode?.nodeName?.toLowerCase() === 'span' && backgroundColor === 'yellow') { + selectionFocusNode.parentElement.style.backgroundColor = ''; + // Empty span required to force an input so that HTML change is seen from text cell component + // This span doesn't have any effect on the markdown generated. + document.execCommand('formatBlock', false, 'span'); + } else { + document.execCommand('hiliteColor', false, 'Yellow'); + } +} +