mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-06 09:35:41 -05:00
WYSIWYG Span Style Fixes, Refactor, and Tests (#13011)
* Refactor into own class, add tests * Add more tests * Test fixes * Test fix hopefully * Tests D vs C drive
This commit is contained in:
@@ -7,6 +7,7 @@ import 'vs/css!./media/markdown';
|
||||
import 'vs/css!./media/highlight';
|
||||
|
||||
import { OnInit, Component, Input, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, OnChanges, SimpleChange, HostListener, ViewChildren, QueryList } from '@angular/core';
|
||||
import * as Mark from 'mark.js';
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
@@ -15,9 +16,11 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
|
||||
import { IColorTheme } from 'vs/platform/theme/common/themeService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IMarkdownRenderResult } from 'vs/editor/contrib/markdown/markdownRenderer';
|
||||
|
||||
import { NotebookMarkdownRenderer } from 'sql/workbench/contrib/notebook/browser/outputs/notebookMarkdown';
|
||||
import { CellView } from 'sql/workbench/contrib/notebook/browser/cellViews/interfaces';
|
||||
import { ICellModel } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
|
||||
@@ -25,13 +28,8 @@ import { NotebookModel } from 'sql/workbench/services/notebook/browser/models/no
|
||||
import { ISanitizer, defaultSanitizer } from 'sql/workbench/services/notebook/browser/outputs/sanitizer';
|
||||
import { CodeComponent } from 'sql/workbench/contrib/notebook/browser/cellViews/code.component';
|
||||
import { NotebookRange, ICellEditorProvider, INotebookService } from 'sql/workbench/services/notebook/browser/notebookService';
|
||||
import { IColorTheme } from 'vs/platform/theme/common/themeService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import * as turndownPluginGfm from '../turndownPluginGfm';
|
||||
import TurndownService = require('turndown');
|
||||
import * as Mark from 'mark.js';
|
||||
import { HTMLMarkdownConverter } from 'sql/workbench/contrib/notebook/browser/htmlMarkdownConverter';
|
||||
import { NotebookInput } from 'sql/workbench/contrib/notebook/browser/models/notebookInput';
|
||||
import * as path from 'vs/base/common/path';
|
||||
|
||||
export const TEXT_SELECTOR: string = 'text-cell-component';
|
||||
const USER_SELECT_CLASS = 'actionselect';
|
||||
@@ -95,11 +93,11 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges {
|
||||
private _model: NotebookModel;
|
||||
private _activeCellId: string;
|
||||
private readonly _onDidClickLink = this._register(new Emitter<URI>());
|
||||
public readonly onDidClickLink = this._onDidClickLink.event;
|
||||
private markdownRenderer: NotebookMarkdownRenderer;
|
||||
private markdownResult: IMarkdownRenderResult;
|
||||
private _htmlMarkdownConverter: HTMLMarkdownConverter;
|
||||
public readonly onDidClickLink = this._onDidClickLink.event;
|
||||
public previewFeaturesEnabled: boolean = false;
|
||||
private turndownService;
|
||||
public doubleClickEditEnabled: boolean;
|
||||
|
||||
constructor(
|
||||
@@ -111,7 +109,6 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges {
|
||||
|
||||
) {
|
||||
super();
|
||||
this.setTurndownOptions();
|
||||
this.markdownRenderer = this._instantiationService.createInstance(NotebookMarkdownRenderer);
|
||||
this.doubleClickEditEnabled = this._configurationService.getValue('notebook.enableDoubleClickEdit');
|
||||
this._register(toDisposable(() => {
|
||||
@@ -162,6 +159,7 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges {
|
||||
this.updateTheme(this.themeService.getColorTheme());
|
||||
this.setFocusAndScroll();
|
||||
this.cellModel.isEditMode = false;
|
||||
this._htmlMarkdownConverter = new HTMLMarkdownConverter(this.notebookUri);
|
||||
this._register(this.cellModel.onOutputsChanged(e => {
|
||||
this.updatePreview();
|
||||
}));
|
||||
@@ -248,7 +246,7 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges {
|
||||
|
||||
private updateCellSource(): void {
|
||||
let textOutputElement = <HTMLElement>this.output.nativeElement;
|
||||
let newCellSource: string = this.turndownService.turndown(textOutputElement.innerHTML, { gfm: true });
|
||||
let newCellSource: string = this._htmlMarkdownConverter.convert(textOutputElement.innerHTML);
|
||||
this.cellModel.source = newCellSource;
|
||||
this._changeRef.detectChanges();
|
||||
}
|
||||
@@ -437,68 +435,6 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges {
|
||||
return textOutput;
|
||||
}
|
||||
|
||||
private setTurndownOptions() {
|
||||
this.turndownService = new TurndownService({ 'emDelimiter': '_', 'bulletListMarker': '-', 'headingStyle': 'atx' });
|
||||
this.turndownService.keep(['u', 'mark']);
|
||||
this.turndownService.use(turndownPluginGfm.gfm);
|
||||
this.turndownService.addRule('pre', {
|
||||
filter: 'pre',
|
||||
replacement: function (content, node) {
|
||||
return '\n```\n' + node.textContent + '\n```\n';
|
||||
}
|
||||
});
|
||||
this.turndownService.addRule('caption', {
|
||||
filter: 'caption',
|
||||
replacement: function (content, node) {
|
||||
return `${node.outerHTML}
|
||||
`;
|
||||
}
|
||||
});
|
||||
this.turndownService.addRule('span', {
|
||||
filter: function (node, options) {
|
||||
return (
|
||||
node.nodeName === 'MARK' ||
|
||||
(node.nodeName === 'SPAN' &&
|
||||
node.getAttribute('style') === 'background-color: yellow;')
|
||||
);
|
||||
},
|
||||
replacement: function (content, node) {
|
||||
if (node.nodeName === 'SPAN') {
|
||||
return '<mark>' + node.textContent + '</mark>';
|
||||
}
|
||||
return node.textContent;
|
||||
}
|
||||
});
|
||||
this.turndownService.addRule('img', {
|
||||
filter: 'img',
|
||||
replacement: (content, node) => {
|
||||
if (node?.src) {
|
||||
let imgPath = URI.parse(node.src);
|
||||
const notebookFolder: string = this.notebookUri ? path.join(path.dirname(this.notebookUri.fsPath), path.sep) : '';
|
||||
let relativePath = findPathRelativeToContent(notebookFolder, imgPath);
|
||||
if (relativePath) {
|
||||
return ``;
|
||||
}
|
||||
}
|
||||
return ``;
|
||||
}
|
||||
});
|
||||
this.turndownService.addRule('a', {
|
||||
filter: 'a',
|
||||
replacement: (content, node) => {
|
||||
//On Windows, if notebook is not trusted then the href attr is removed for all non-web URL links
|
||||
// href contains either a hyperlink or a URI-encoded absolute path. (See resolveUrls method in notebookMarkdown.ts)
|
||||
const notebookLink = node.href ? URI.parse(node.href) : URI.file(node.title);
|
||||
const notebookFolder = this.notebookUri ? path.join(path.dirname(this.notebookUri.fsPath), path.sep) : '';
|
||||
let relativePath = findPathRelativeToContent(notebookFolder, notebookLink);
|
||||
if (relativePath) {
|
||||
return `[${node.innerText}](${relativePath})`;
|
||||
}
|
||||
return `[${node.innerText}](${node.href})`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Enables edit mode on double clicking active cell
|
||||
private enableActiveCellEditOnDoubleClick() {
|
||||
if (!this.isEditMode && this.doubleClickEditEnabled) {
|
||||
@@ -509,23 +445,6 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges {
|
||||
}
|
||||
}
|
||||
|
||||
export function findPathRelativeToContent(notebookFolder: string, contentPath: URI | undefined): string {
|
||||
if (notebookFolder) {
|
||||
if (contentPath?.scheme === 'file') {
|
||||
let relativePath = path.relative(notebookFolder, contentPath.fsPath);
|
||||
//if path contains whitespaces then it's not identified as a link
|
||||
relativePath = relativePath.replace(/\s/g, '%20');
|
||||
if (relativePath.startsWith(path.join('..', path.sep) || path.join('.', path.sep))) {
|
||||
return relativePath;
|
||||
} else {
|
||||
// if the relative path does not contain ./ at the beginning, we need to add it so it's recognized as a link
|
||||
return `.${path.join(path.sep, relativePath)}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function preventDefaultAndExecCommand(e: KeyboardEvent, commandId: string) {
|
||||
// use preventDefault() to avoid invoking the editor's select all
|
||||
e.preventDefault();
|
||||
|
||||
Reference in New Issue
Block a user