mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-29 17:23:25 -05:00
Merge VS Code 1.31.1 (#4283)
This commit is contained in:
@@ -3,132 +3,158 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as crypto from 'crypto';
|
||||
import { MarkdownIt, Token } from 'markdown-it';
|
||||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
import * as crypto from 'crypto';
|
||||
import { MarkdownContributions } from './markdownExtensions';
|
||||
import { Slugifier } from './slugify';
|
||||
import { SkinnyTextDocument } from './tableOfContentsProvider';
|
||||
import { getUriForLinkWithKnownExternalScheme } from './util/links';
|
||||
|
||||
const FrontMatterRegex = /^---\s*[^]*?(-{3}|\.{3})\s*/;
|
||||
const UNICODE_NEWLINE_REGEX = /\u2028|\u2029/g;
|
||||
|
||||
interface MarkdownItConfig {
|
||||
readonly breaks: boolean;
|
||||
readonly linkify: boolean;
|
||||
}
|
||||
|
||||
class TokenCache {
|
||||
private cachedDocument?: {
|
||||
readonly uri: vscode.Uri;
|
||||
readonly version: number;
|
||||
readonly config: MarkdownItConfig;
|
||||
};
|
||||
private tokens?: Token[];
|
||||
|
||||
public tryGetCached(document: SkinnyTextDocument, config: MarkdownItConfig): Token[] | undefined {
|
||||
if (this.cachedDocument
|
||||
&& this.cachedDocument.uri.toString() === document.uri.toString()
|
||||
&& this.cachedDocument.version === document.version
|
||||
&& this.cachedDocument.config.breaks === config.breaks
|
||||
&& this.cachedDocument.config.linkify === config.linkify
|
||||
) {
|
||||
return this.tokens;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public update(document: SkinnyTextDocument, config: MarkdownItConfig, tokens: Token[]) {
|
||||
this.cachedDocument = {
|
||||
uri: document.uri,
|
||||
version: document.version,
|
||||
config,
|
||||
};
|
||||
this.tokens = tokens;
|
||||
}
|
||||
}
|
||||
|
||||
export class MarkdownEngine {
|
||||
private md?: MarkdownIt;
|
||||
private md?: Promise<MarkdownIt>;
|
||||
|
||||
private firstLine?: number;
|
||||
private currentDocument?: vscode.Uri;
|
||||
private _slugCount = new Map<string, number>();
|
||||
private _tokenCache = new TokenCache();
|
||||
|
||||
public constructor(
|
||||
private readonly extensionPreviewResourceProvider: MarkdownContributions,
|
||||
private readonly slugifier: Slugifier,
|
||||
) { }
|
||||
|
||||
private usePlugin(factory: (md: any) => any): void {
|
||||
try {
|
||||
this.md = factory(this.md);
|
||||
} catch (e) {
|
||||
// noop
|
||||
private async getEngine(config: MarkdownItConfig): Promise<MarkdownIt> {
|
||||
if (!this.md) {
|
||||
this.md = import('markdown-it').then(async markdownIt => {
|
||||
let md: MarkdownIt = markdownIt(await getMarkdownOptions(() => md));
|
||||
|
||||
for (const plugin of this.extensionPreviewResourceProvider.markdownItPlugins) {
|
||||
try {
|
||||
md = (await plugin)(md);
|
||||
} catch {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
const frontMatterPlugin = require('markdown-it-front-matter');
|
||||
// Extract rules from front matter plugin and apply at a lower precedence
|
||||
let fontMatterRule: any;
|
||||
frontMatterPlugin({
|
||||
block: {
|
||||
ruler: {
|
||||
before: (_id: any, _id2: any, rule: any) => { fontMatterRule = rule; }
|
||||
}
|
||||
}
|
||||
}, () => { /* noop */ });
|
||||
|
||||
md.block.ruler.before('fence', 'front_matter', fontMatterRule, {
|
||||
alt: ['paragraph', 'reference', 'blockquote', 'list']
|
||||
});
|
||||
|
||||
for (const renderName of ['paragraph_open', 'heading_open', 'image', 'code_block', 'fence', 'blockquote_open', 'list_item_open']) {
|
||||
this.addLineNumberRenderer(md, renderName);
|
||||
}
|
||||
|
||||
this.addImageStabilizer(md);
|
||||
this.addFencedRenderer(md);
|
||||
|
||||
this.addLinkNormalizer(md);
|
||||
this.addLinkValidator(md);
|
||||
this.addNamedHeaders(md);
|
||||
return md;
|
||||
});
|
||||
}
|
||||
|
||||
const md = await this.md!;
|
||||
md.set(config);
|
||||
return md;
|
||||
}
|
||||
|
||||
private async getEngine(resource: vscode.Uri): Promise<MarkdownIt> {
|
||||
if (!this.md) {
|
||||
const hljs = await import('highlight.js');
|
||||
this.md = (await import('markdown-it'))({
|
||||
html: true,
|
||||
highlight: (str: string, lang?: string) => {
|
||||
// Workaround for highlight not supporting tsx: https://github.com/isagalaev/highlight.js/issues/1155
|
||||
if (lang && ['tsx', 'typescriptreact'].indexOf(lang.toLocaleLowerCase()) >= 0) {
|
||||
lang = 'jsx';
|
||||
}
|
||||
if (lang && lang.toLocaleLowerCase() === 'json5') {
|
||||
lang = 'json';
|
||||
}
|
||||
if (lang && lang.toLocaleLowerCase() === 'c#') {
|
||||
lang = 'cs';
|
||||
}
|
||||
if (lang && hljs.getLanguage(lang)) {
|
||||
try {
|
||||
return `<div>${hljs.highlight(lang, str, true).value}</div>`;
|
||||
} catch (error) { }
|
||||
}
|
||||
return `<code><div>${this.md!.utils.escapeHtml(str)}</div></code>`;
|
||||
}
|
||||
});
|
||||
|
||||
for (const plugin of this.extensionPreviewResourceProvider.markdownItPlugins) {
|
||||
this.usePlugin(await plugin);
|
||||
}
|
||||
|
||||
for (const renderName of ['paragraph_open', 'heading_open', 'image', 'code_block', 'fence', 'blockquote_open', 'list_item_open']) {
|
||||
this.addLineNumberRenderer(this.md, renderName);
|
||||
}
|
||||
|
||||
this.addImageStabilizer(this.md);
|
||||
this.addFencedRenderer(this.md);
|
||||
|
||||
this.addLinkNormalizer(this.md);
|
||||
this.addLinkValidator(this.md);
|
||||
this.addNamedHeaders(this.md);
|
||||
private tokenize(
|
||||
document: SkinnyTextDocument,
|
||||
config: MarkdownItConfig,
|
||||
engine: MarkdownIt
|
||||
): Token[] {
|
||||
const cached = this._tokenCache.tryGetCached(document, config);
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
this.currentDocument = document.uri;
|
||||
this._slugCount = new Map<string, number>();
|
||||
|
||||
const text = document.getText();
|
||||
const tokens = engine.parse(text.replace(UNICODE_NEWLINE_REGEX, ''), {});
|
||||
this._tokenCache.update(document, config, tokens);
|
||||
return tokens;
|
||||
}
|
||||
|
||||
// {{SQL CARBON EDIT}} - Add renderText method
|
||||
public async renderText(document: vscode.Uri, text: string): Promise<string> {
|
||||
const engine = await this.getEngine(this.getConfig(document));
|
||||
return engine.render(text);
|
||||
}
|
||||
// {{SQL CARBON EDIT}} - End
|
||||
|
||||
public async render(document: SkinnyTextDocument): Promise<string> {
|
||||
const config = this.getConfig(document.uri);
|
||||
const engine = await this.getEngine(config);
|
||||
return engine.renderer.render(this.tokenize(document, config, engine), {
|
||||
...(engine as any).options,
|
||||
...config
|
||||
}, {});
|
||||
}
|
||||
|
||||
public async parse(document: SkinnyTextDocument): Promise<Token[]> {
|
||||
const config = this.getConfig(document.uri);
|
||||
const engine = await this.getEngine(config);
|
||||
return this.tokenize(document, config, engine);
|
||||
}
|
||||
|
||||
private getConfig(resource: vscode.Uri): MarkdownItConfig {
|
||||
const config = vscode.workspace.getConfiguration('markdown', resource);
|
||||
this.md.set({
|
||||
return {
|
||||
breaks: config.get<boolean>('preview.breaks', false),
|
||||
linkify: config.get<boolean>('preview.linkify', true)
|
||||
});
|
||||
return this.md;
|
||||
}
|
||||
|
||||
private stripFrontmatter(text: string): { text: string, offset: number } {
|
||||
let offset = 0;
|
||||
const frontMatterMatch = FrontMatterRegex.exec(text);
|
||||
if (frontMatterMatch) {
|
||||
const frontMatter = frontMatterMatch[0];
|
||||
offset = frontMatter.split(/\r\n|\n|\r/g).length - 1;
|
||||
text = text.substr(frontMatter.length);
|
||||
}
|
||||
return { text, offset };
|
||||
}
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
public async renderText(document: vscode.Uri, text: string): Promise<string> {
|
||||
const engine = await this.getEngine(document);
|
||||
return engine.render(text);
|
||||
}
|
||||
|
||||
public async render(document: vscode.Uri, stripFrontmatter: boolean, text: string): Promise<string> {
|
||||
let offset = 0;
|
||||
if (stripFrontmatter) {
|
||||
const markdownContent = this.stripFrontmatter(text);
|
||||
offset = markdownContent.offset;
|
||||
text = markdownContent.text;
|
||||
}
|
||||
this.currentDocument = document;
|
||||
this.firstLine = offset;
|
||||
this._slugCount = new Map<string, number>();
|
||||
|
||||
const engine = await this.getEngine(document);
|
||||
return engine.render(text);
|
||||
}
|
||||
|
||||
public async parse(document: vscode.Uri, source: string): Promise<Token[]> {
|
||||
const UNICODE_NEWLINE_REGEX = /\u2028|\u2029/g;
|
||||
const { text, offset } = this.stripFrontmatter(source);
|
||||
this.currentDocument = document;
|
||||
this._slugCount = new Map<string, number>();
|
||||
|
||||
const engine = await this.getEngine(document);
|
||||
|
||||
return engine.parse(text.replace(UNICODE_NEWLINE_REGEX, ''), {}).map(token => {
|
||||
if (token.map) {
|
||||
token.map[0] += offset;
|
||||
token.map[1] += offset;
|
||||
}
|
||||
return token;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
private addLineNumberRenderer(md: any, ruleName: string): void {
|
||||
@@ -136,7 +162,7 @@ export class MarkdownEngine {
|
||||
md.renderer.rules[ruleName] = (tokens: any, idx: number, options: any, env: any, self: any) => {
|
||||
const token = tokens[idx];
|
||||
if (token.map && token.map.length) {
|
||||
token.attrSet('data-line', this.firstLine + token.map[0]);
|
||||
token.attrSet('data-line', token.map[0]);
|
||||
token.attrJoin('class', 'code-line');
|
||||
}
|
||||
|
||||
@@ -257,4 +283,30 @@ export class MarkdownEngine {
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function getMarkdownOptions(md: () => MarkdownIt) {
|
||||
const hljs = await import('highlight.js');
|
||||
return {
|
||||
html: true,
|
||||
highlight: (str: string, lang?: string) => {
|
||||
// Workaround for highlight not supporting tsx: https://github.com/isagalaev/highlight.js/issues/1155
|
||||
if (lang && ['tsx', 'typescriptreact'].indexOf(lang.toLocaleLowerCase()) >= 0) {
|
||||
lang = 'jsx';
|
||||
}
|
||||
if (lang && lang.toLocaleLowerCase() === 'json5') {
|
||||
lang = 'json';
|
||||
}
|
||||
if (lang && lang.toLocaleLowerCase() === 'c#') {
|
||||
lang = 'cs';
|
||||
}
|
||||
if (lang && hljs.getLanguage(lang)) {
|
||||
try {
|
||||
return `<div>${hljs.highlight(lang, str, true).value}</div>`;
|
||||
}
|
||||
catch (error) { }
|
||||
}
|
||||
return `<code><div>${md().utils.escapeHtml(str)}</div></code>`;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user