Merge from vscode c58aaab8a1cc22a7139b761166a0d4f37d41e998 (#7880)

* Merge from vscode c58aaab8a1cc22a7139b761166a0d4f37d41e998

* fix pipelines

* fix strict-null-checks

* add missing files
This commit is contained in:
Anthony Dresser
2019-10-21 22:12:22 -07:00
committed by GitHub
parent 7c9be74970
commit 1e22f47304
913 changed files with 18898 additions and 16536 deletions

View File

@@ -56,7 +56,7 @@ function registerMarkdownLanguageFeatures(
return vscode.Disposable.from(
vscode.languages.setLanguageConfiguration('markdown', {
wordPattern: new RegExp('(\\p{Alphabetic}|\\p{Number})+', 'ug'),
wordPattern: new RegExp('(\\p{Alphabetic}|\\p{Number}|\\p{Nonspacing_Mark})+', 'ug'),
}),
vscode.languages.registerDocumentSymbolProvider(selector, symbolProvider),
vscode.languages.registerDocumentLinkProvider(selector, new LinkProvider()),

View File

@@ -25,7 +25,7 @@ interface WebviewMessage {
interface CacheImageSizesMessage extends WebviewMessage {
readonly type: 'cacheImageSizes';
readonly body: { id: string, width: number, height: number }[];
readonly body: { id: string, width: number, height: number; }[];
}
interface RevealLineMessage extends WebviewMessage {
@@ -43,10 +43,9 @@ interface DidClickMessage extends WebviewMessage {
}
interface ClickLinkMessage extends WebviewMessage {
readonly type: 'clickLink';
readonly type: 'openLink';
readonly body: {
readonly path: string;
readonly fragment?: string;
readonly href: string;
};
}
@@ -88,7 +87,7 @@ export class MarkdownPreview extends Disposable {
private forceUpdate = false;
private isScrolling = false;
private _disposed: boolean = false;
private imageInfo: { id: string, width: number, height: number }[] = [];
private imageInfo: { id: string, width: number, height: number; }[] = [];
private scrollToFragment: string | undefined;
public static async revive(
@@ -202,8 +201,8 @@ export class MarkdownPreview extends Disposable {
this.onDidClickPreview(e.body.line);
break;
case 'clickLink':
this.onDidClickPreviewLink(e.body.path, e.body.fragment);
case 'openLink':
this.onDidClickPreviewLink(e.body.href);
break;
case 'showPreviewSecuritySelector':
@@ -284,15 +283,17 @@ export class MarkdownPreview extends Disposable {
super.dispose();
}
public update(resource: vscode.Uri) {
const editor = vscode.window.activeTextEditor;
public update(resource: vscode.Uri, isRefresh = true) {
// Reposition scroll preview, position scroll to the top if active text editor
// doesn't corresponds with preview
const editor = vscode.window.activeTextEditor;
if (editor) {
if (editor.document.uri.fsPath === resource.fsPath) {
this.line = getVisibleLine(editor);
} else {
this.line = 0;
if (!isRefresh || this._previewConfigurations.loadAndCacheConfiguration(this._resource).scrollEditorWithPreview) {
if (editor.document.uri.fsPath === resource.fsPath) {
this.line = getVisibleLine(editor);
} else {
this.line = 0;
}
}
}
@@ -320,7 +321,7 @@ export class MarkdownPreview extends Disposable {
public refresh() {
this.forceUpdate = true;
this.update(this._resource);
this.update(this._resource, true);
}
public updateConfiguration() {
@@ -484,6 +485,12 @@ export class MarkdownPreview extends Disposable {
private onDidScrollPreview(line: number) {
this.line = line;
const config = this._previewConfigurations.loadAndCacheConfiguration(this._resource);
if (!config.scrollEditorWithPreview) {
return;
}
for (const editor of vscode.window.visibleTextEditors) {
if (!this.isPreviewOf(editor.document.uri)) {
continue;
@@ -528,12 +535,19 @@ export class MarkdownPreview extends Disposable {
this.editor.webview.html = html;
}
private async onDidClickPreviewLink(path: string, fragment: string | undefined) {
this.scrollToFragment = undefined;
private async onDidClickPreviewLink(href: string) {
let [hrefPath, fragment] = decodeURIComponent(href).split('#');
// We perviously already resolve absolute paths.
// Now make sure we handle relative file paths
if (hrefPath[0] !== '/') {
hrefPath = path.join(path.dirname(this.resource.path), hrefPath);
}
const config = vscode.workspace.getConfiguration('markdown', this.resource);
const openLinks = config.get<string>('preview.openMarkdownLinks', 'inPreview');
if (openLinks === 'inPreview') {
const markdownLink = await resolveLinkToMarkdownFile(path);
const markdownLink = await resolveLinkToMarkdownFile(hrefPath);
if (markdownLink) {
if (fragment) {
this.scrollToFragment = fragment;
@@ -543,10 +557,10 @@ export class MarkdownPreview extends Disposable {
}
}
vscode.commands.executeCommand('_markdown.openDocumentLink', { path, fragment, fromResource: this.resource });
vscode.commands.executeCommand('_markdown.openDocumentLink', { path: hrefPath, fragment, fromResource: this.resource });
}
private async onCacheImageSizes(imageInfo: { id: string, width: number, height: number }[]) {
private async onCacheImageSizes(imageInfo: { id: string, width: number, height: number; }[]) {
this.imageInfo = imageInfo;
}
}

View File

@@ -4,13 +4,13 @@
*--------------------------------------------------------------------------------------------*/
import * as crypto from 'crypto';
import { MarkdownIt, Token } from 'markdown-it';
import * as path from 'path';
import { MarkdownIt, Token } from 'markdown-it';
import * as vscode from 'vscode';
import { MarkdownContributionProvider as MarkdownContributionProvider } from './markdownExtensions';
import { Slugifier } from './slugify';
import { SkinnyTextDocument } from './tableOfContentsProvider';
import { getUriForLinkWithKnownExternalScheme } from './util/links';
import { Schemes, isOfScheme } from './util/links';
const UNICODE_NEWLINE_REGEX = /\u2028|\u2029/g;
@@ -105,10 +105,10 @@ export class MarkdownEngine {
this.addImageStabilizer(md);
this.addFencedRenderer(md);
this.addLinkNormalizer(md);
this.addLinkValidator(md);
this.addNamedHeaders(md);
this.addLinkRenderer(md);
return md;
});
}
@@ -150,6 +150,7 @@ export class MarkdownEngine {
public async render(input: SkinnyTextDocument | string): Promise<string> {
const config = this.getConfig(typeof input === 'string' ? undefined : input.uri);
const engine = await this.getEngine(config);
const tokens = typeof input === 'string'
? this.tokenizeString(input, engine)
: this.tokenizeDocument(input, config, engine);
@@ -233,36 +234,28 @@ export class MarkdownEngine {
const normalizeLink = md.normalizeLink;
md.normalizeLink = (link: string) => {
try {
const externalSchemeUri = getUriForLinkWithKnownExternalScheme(link);
if (externalSchemeUri) {
// set true to skip encoding
return normalizeLink(externalSchemeUri.toString(true));
}
// If original link doesn't look like a url with a scheme, assume it must be a link to a file in workspace
if (!/^[a-z\-]+:/i.test(link)) {
// Use a fake scheme for parsing
let uri = vscode.Uri.parse('markdown-link:' + link);
// Assume it must be an relative or absolute file path
// Use a fake scheme to avoid parse warnings
let uri = vscode.Uri.parse(`vscode-resource:${link}`);
if (uri.path) {
// Assume it must be a file
const fragment = uri.fragment;
// Relative paths should be resolved correctly inside the preview but we need to
// handle absolute paths specially (for images) to resolve them relative to the workspace root
if (uri.path[0] === '/') {
const root = vscode.workspace.getWorkspaceFolder(this.currentDocument!);
if (root) {
uri = vscode.Uri.file(path.join(root.uri.fsPath, uri.path));
uri = uri.with({
path: path.join(root.uri.fsPath, uri.path),
});
}
} else {
uri = vscode.Uri.file(path.join(path.dirname(this.currentDocument!.path), uri.path));
}
if (fragment) {
if (uri.fragment) {
uri = uri.with({
fragment: this.slugifier.fromHeading(fragment).value
fragment: this.slugifier.fromHeading(uri.fragment).value
});
}
return normalizeLink(uri.with({ scheme: 'vscode-resource' }).toString(true));
} else if (!uri.path && uri.fragment) {
return `#${this.slugifier.fromHeading(uri.fragment).value}`;
return normalizeLink(uri.toString(true).replace(/^markdown-link:/, ''));
}
} catch (e) {
// noop
@@ -275,7 +268,7 @@ export class MarkdownEngine {
const validateLink = md.validateLink;
md.validateLink = (link: string) => {
// support file:// links
return validateLink(link) || link.startsWith('file:') || /^data:image\/.*?;/.test(link);
return validateLink(link) || isOfScheme(Schemes.file, link) || /^data:image\/.*?;/.test(link);
};
}
@@ -303,6 +296,22 @@ export class MarkdownEngine {
}
};
}
private addLinkRenderer(md: any): void {
const old_render = md.renderer.rules.link_open || ((tokens: any, idx: number, options: any, _env: any, self: any) => {
return self.renderToken(tokens, idx, options);
});
md.renderer.rules.link_open = (tokens: any, idx: number, options: any, env: any, self: any) => {
const token = tokens[idx];
const hrefIndex = token.attrIndex('href');
if (hrefIndex >= 0) {
const href = token.attrs[hrefIndex][1];
token.attrPush(['data-href', href]);
}
return old_render(tokens, idx, options, env, self);
};
}
}
async function getMarkdownOptions(md: () => MarkdownIt) {
@@ -310,16 +319,7 @@ async function getMarkdownOptions(md: () => MarkdownIt) {
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'].includes(lang.toLocaleLowerCase())) {
lang = 'jsx';
}
if (lang && lang.toLocaleLowerCase() === 'json5') {
lang = 'json';
}
if (lang && ['c#', 'csharp'].includes(lang.toLocaleLowerCase())) {
lang = 'cs';
}
lang = normalizeHighlightLang(lang);
if (lang && hljs.getLanguage(lang)) {
try {
return `<div>${hljs.highlight(lang, str, true).value}</div>`;
@@ -330,3 +330,24 @@ async function getMarkdownOptions(md: () => MarkdownIt) {
}
};
}
function normalizeHighlightLang(lang: string | undefined) {
switch (lang && lang.toLowerCase()) {
case 'tsx':
case 'typescriptreact':
// Workaround for highlight not supporting tsx: https://github.com/isagalaev/highlight.js/issues/1155
return 'jsx';
case 'json5':
case 'jsonc':
return 'json';
case 'c#':
case 'csharp':
return 'cs';
default:
return lang;
}
}

View File

@@ -5,14 +5,30 @@
import * as vscode from 'vscode';
const knownSchemes = ['http:', 'https:', 'file:', 'mailto:', 'data:', `${vscode.env.uriScheme}:`, 'vscode:', 'vscode-insiders:', 'vscode-resource:'];
export const Schemes = {
http: 'http:',
https: 'https:',
file: 'file:',
mailto: 'mailto:',
data: 'data:',
vscode: 'vscode:',
'vscode-insiders': 'vscode-insiders:',
'vscode-resource': 'vscode-resource',
};
export function getUriForLinkWithKnownExternalScheme(
link: string,
): vscode.Uri | undefined {
if (knownSchemes.some(knownScheme => link.toLowerCase().startsWith(knownScheme))) {
const knownSchemes = [
...Object.values(Schemes),
`${vscode.env.uriScheme}:`
];
export function getUriForLinkWithKnownExternalScheme(link: string): vscode.Uri | undefined {
if (knownSchemes.some(knownScheme => isOfScheme(knownScheme, link))) {
return vscode.Uri.parse(link);
}
return undefined;
}
export function isOfScheme(scheme: string, link: string): boolean {
return link.toLowerCase().startsWith(scheme);
}