Merge from vscode a5cf1da01d5db3d2557132be8d30f89c38019f6c (#8525)

* Merge from vscode a5cf1da01d5db3d2557132be8d30f89c38019f6c

* remove files we don't want

* fix hygiene

* update distro

* update distro

* fix hygiene

* fix strict nulls

* distro

* distro

* fix tests

* fix tests

* add another edit

* fix viewlet icon

* fix azure dialog

* fix some padding

* fix more padding issues
This commit is contained in:
Anthony Dresser
2019-12-04 19:28:22 -08:00
committed by GitHub
parent a8818ab0df
commit f5ce7fb2a5
1507 changed files with 42813 additions and 27370 deletions

File diff suppressed because one or more lines are too long

View File

@@ -129,15 +129,6 @@ h1, h2, h3 {
font-weight: normal;
}
h1 code,
h2 code,
h3 code,
h4 code,
h5 code,
h6 code {
line-height: auto;
}
table {
border-collapse: collapse;
}

File diff suppressed because one or more lines are too long

View File

@@ -25,7 +25,9 @@
"onCommand:markdown.showSource",
"onCommand:markdown.showPreviewSecuritySelector",
"onCommand:markdown.api.render",
"onWebviewPanel:markdown.preview"
"onCommand:notebook.showPreview",
"onWebviewPanel:markdown.preview",
"onWebviewEditor:vscode.markdown.preview.editor"
],
"contributes": {
"commands": [
@@ -307,6 +309,18 @@
],
"markdown.previewScripts": [
"./media/index.js"
],
"webviewEditors": [
{
"viewType": "vscode.markdown.preview.editor",
"displayName": "(Experimental) VS Code Markdown Preview",
"priority": "option",
"selector": [
{
"filenamePattern": "*.md"
}
]
}
]
},
"scripts": {
@@ -327,14 +341,14 @@
"@types/highlight.js": "9.12.3",
"@types/lodash.throttle": "^4.1.3",
"@types/markdown-it": "0.0.2",
"@types/node": "^10.14.8",
"@types/node": "^12.11.7",
"lodash.throttle": "^4.1.1",
"mocha-junit-reporter": "^1.17.0",
"mocha-multi-reporters": "^1.1.7",
"ts-loader": "^4.0.1",
"typescript": "^3.3.1",
"ts-loader": "^6.2.1",
"typescript": "^3.7.2",
"vscode": "^1.1.10",
"webpack": "^4.1.0",
"webpack-cli": "^2.0.10"
"webpack": "^4.41.2",
"webpack-cli": "^3.3.0"
}
}

View File

@@ -129,7 +129,7 @@ document.addEventListener('dblclick', event => {
}
});
const passThroughLinkSchemes = ['http:', 'https:', 'mailto:', 'vscode:', 'vscode-insiders'];
const passThroughLinkSchemes = ['http:', 'https:', 'mailto:', 'vscode:', 'vscode-insiders:'];
document.addEventListener('click', event => {
if (!event) {

View File

@@ -5,6 +5,7 @@
import { getSettings } from './settings';
const codeLineClass = 'code-line';
function clamp(min: number, max: number, value: number) {
return Math.min(max, Math.max(min, value));
@@ -25,9 +26,17 @@ const getCodeLineElements = (() => {
return () => {
if (!elements) {
elements = [{ element: document.body, line: 0 }];
for (const element of document.getElementsByClassName('code-line')) {
for (const element of document.getElementsByClassName(codeLineClass)) {
const line = +element.getAttribute('data-line')!;
if (!isNaN(line)) {
if (isNaN(line)) {
continue;
}
if (element.tagName === 'CODE' && element.parentElement && element.parentElement.tagName === 'PRE') {
// Fenched code blocks are a special case since the `code-line` can only be marked on
// the `<code>` element and not the parent `<pre>` element.
elements.push({ element: element.parentElement as HTMLElement, line });
} else {
elements.push({ element: element as HTMLElement, line });
}
}
@@ -67,7 +76,7 @@ export function getLineElementsAtPageOffset(offset: number): { previous: CodeLin
let hi = lines.length - 1;
while (lo + 1 < hi) {
const mid = Math.floor((lo + hi) / 2);
const bounds = lines[mid].element.getBoundingClientRect();
const bounds = getElementBounds(lines[mid]);
if (bounds.top + bounds.height >= position) {
hi = mid;
}
@@ -76,14 +85,35 @@ export function getLineElementsAtPageOffset(offset: number): { previous: CodeLin
}
}
const hiElement = lines[hi];
const hiBounds = hiElement.element.getBoundingClientRect();
const hiBounds = getElementBounds(hiElement);
if (hi >= 1 && hiBounds.top > position) {
const loElement = lines[lo];
return { previous: loElement, next: hiElement };
}
if (hi > 1 && hi < lines.length && hiBounds.top + hiBounds.height > position) {
return { previous: hiElement, next: lines[hi + 1] };
}
return { previous: hiElement };
}
function getElementBounds({ element }: CodeLineElement): { top: number, height: number } {
const myBounds = element.getBoundingClientRect();
// Some code line elements may contain other code line elements.
// In those cases, only take the height up to that child.
const codeLineChild = element.querySelector(`.${codeLineClass}`);
if (codeLineChild) {
const childBounds = codeLineChild.getBoundingClientRect();
const height = Math.max(1, (childBounds.top - myBounds.top));
return {
top: myBounds.top,
height: height
};
}
return myBounds;
}
/**
* Attempt to reveal the element for a source line in the editor.
*/
@@ -102,7 +132,7 @@ export function scrollToRevealSourceLine(line: number) {
return;
}
let scrollTo = 0;
const rect = previous.element.getBoundingClientRect();
const rect = getElementBounds(previous);
const previousTop = rect.top;
if (next && next.line !== previous.line) {
// Between two elements. Go to percentage offset between them.
@@ -119,14 +149,13 @@ export function scrollToRevealSourceLine(line: number) {
export function getEditorLineNumberForPageOffset(offset: number) {
const { previous, next } = getLineElementsAtPageOffset(offset);
if (previous) {
const previousBounds = previous.element.getBoundingClientRect();
const previousBounds = getElementBounds(previous);
const offsetFromPrevious = (offset - window.scrollY - previousBounds.top);
if (next) {
const progressBetweenElements = offsetFromPrevious / (next.element.getBoundingClientRect().top - previousBounds.top);
const progressBetweenElements = offsetFromPrevious / (getElementBounds(next).top - previousBounds.top);
const line = previous.line + progressBetweenElements * (next.line - previous.line);
return clampLine(line);
}
else {
} else {
const progressWithinElement = offsetFromPrevious / (previousBounds.height);
const line = previous.line + progressWithinElement;
return clampLine(line);

View File

@@ -6,9 +6,8 @@
import * as vscode from 'vscode';
import { Command } from '../commandManager';
import { MarkdownPreviewManager } from '../features/previewManager';
import { MarkdownPreviewManager, DynamicPreviewSettings } from '../features/previewManager';
import { TelemetryReporter } from '../telemetryReporter';
import { PreviewSettings } from '../features/preview';
interface ShowPreviewSettings {
readonly sideBySide?: boolean;
@@ -39,7 +38,7 @@ async function showPreview(
}
const resourceColumn = (vscode.window.activeTextEditor && vscode.window.activeTextEditor.viewColumn) || vscode.ViewColumn.One;
webviewManager.preview(resource, {
webviewManager.openDynamicPreview(resource, {
resourceColumn: resourceColumn,
previewColumn: previewSettings.sideBySide ? resourceColumn + 1 : resourceColumn,
locked: !!previewSettings.locked
@@ -59,7 +58,7 @@ export class ShowPreviewCommand implements Command {
private readonly telemetryReporter: TelemetryReporter
) { }
public execute(mainUri?: vscode.Uri, allUris?: vscode.Uri[], previewSettings?: PreviewSettings) {
public execute(mainUri?: vscode.Uri, allUris?: vscode.Uri[], previewSettings?: DynamicPreviewSettings) {
for (const uri of Array.isArray(allUris) ? allUris : [mainUri]) {
showPreview(this.webviewManager, this.telemetryReporter, uri, {
sideBySide: false,
@@ -77,7 +76,7 @@ export class ShowPreviewToSideCommand implements Command {
private readonly telemetryReporter: TelemetryReporter
) { }
public execute(uri?: vscode.Uri, previewSettings?: PreviewSettings) {
public execute(uri?: vscode.Uri, previewSettings?: DynamicPreviewSettings) {
showPreview(this.webviewManager, this.telemetryReporter, uri, {
sideBySide: true,
locked: previewSettings && previewSettings.locked

View File

@@ -54,9 +54,11 @@ function registerMarkdownLanguageFeatures(
{ language: 'markdown', scheme: 'untitled' }
];
const charPattern = '(\\p{Alphabetic}|\\p{Number}|\\p{Nonspacing_Mark})';
return vscode.Disposable.from(
vscode.languages.setLanguageConfiguration('markdown', {
wordPattern: new RegExp('(\\p{Alphabetic}|\\p{Number}|\\p{Nonspacing_Mark})+', 'ug'),
wordPattern: new RegExp(`${charPattern}((${charPattern}|[_])?${charPattern})*`, 'ug'),
}),
vscode.languages.registerDocumentSymbolProvider(selector, symbolProvider),
vscode.languages.registerDocumentLinkProvider(selector, new LinkProvider()),

View File

@@ -7,7 +7,7 @@ import * as path from 'path';
import * as vscode from 'vscode';
import * as nls from 'vscode-nls';
import { OpenDocumentLinkCommand } from '../commands/openDocumentLink';
import { getUriForLinkWithKnownExternalScheme } from '../util/links';
import { getUriForLinkWithKnownExternalScheme, isOfScheme, Schemes } from '../util/links';
const localize = nls.loadMessageBundle();
@@ -18,6 +18,10 @@ function parseLink(
): { uri: vscode.Uri, tooltip?: string } {
const externalSchemeUri = getUriForLinkWithKnownExternalScheme(link);
if (externalSchemeUri) {
// Normalize VS Code links to target currently running version
if (isOfScheme(Schemes.vscode, link) || isOfScheme(Schemes['vscode-insiders'], link)) {
return { uri: vscode.Uri.parse(link).with({ scheme: vscode.env.uriScheme }) };
}
return { uri: externalSchemeUri };
}

View File

@@ -11,7 +11,7 @@ import { MarkdownContentProvider } from './previewContentProvider';
import { Disposable } from '../util/dispose';
import * as nls from 'vscode-nls';
import { getVisibleLine, MarkdownFileTopmostLineMonitor } from '../util/topmostLineMonitor';
import { getVisibleLine, TopmostLineMonitor } from '../util/topmostLineMonitor';
import { MarkdownPreviewConfigurationManager } from './previewConfig';
import { MarkdownContributionProvider, MarkdownContributions } from '../markdownExtensions';
import { isMarkdownFile } from '../util/file';
@@ -72,11 +72,22 @@ export class PreviewDocumentVersion {
}
}
export class MarkdownPreview extends Disposable {
interface DynamicPreviewInput {
readonly resource: vscode.Uri;
readonly resourceColumn: vscode.ViewColumn;
readonly locked: boolean;
readonly line?: number;
}
export class DynamicMarkdownPreview extends Disposable {
public static readonly viewType = 'markdown.preview';
private readonly delay = 300;
private _resource: vscode.Uri;
private readonly _resourceColumn: vscode.ViewColumn;
private _locked: boolean;
private readonly editor: vscode.WebviewPanel;
@@ -84,92 +95,65 @@ export class MarkdownPreview extends Disposable {
private line: number | undefined = undefined;
private firstUpdate = true;
private currentVersion?: PreviewDocumentVersion;
private forceUpdate = false;
private isScrolling = false;
private _disposed: boolean = false;
private imageInfo: { id: string, width: number, height: number; }[] = [];
private scrollToFragment: string | undefined;
public static async revive(
public static revive(
input: DynamicPreviewInput,
webview: vscode.WebviewPanel,
state: any,
contentProvider: MarkdownContentProvider,
previewConfigurations: MarkdownPreviewConfigurationManager,
logger: Logger,
topmostLineMonitor: MarkdownFileTopmostLineMonitor,
topmostLineMonitor: TopmostLineMonitor,
contributionProvider: MarkdownContributionProvider,
): Promise<MarkdownPreview> {
const resource = vscode.Uri.parse(state.resource);
const locked = state.locked;
const line = state.line;
const resourceColumn = state.resourceColumn;
): DynamicMarkdownPreview {
webview.webview.options = DynamicMarkdownPreview.getWebviewOptions(input.resource, contributionProvider.contributions);
webview.title = DynamicMarkdownPreview.getPreviewTitle(input.resource, input.locked);
const preview = new MarkdownPreview(
webview,
resource,
locked,
resourceColumn,
contentProvider,
previewConfigurations,
logger,
topmostLineMonitor,
contributionProvider);
preview.editor.webview.options = MarkdownPreview.getWebviewOptions(resource, contributionProvider.contributions);
if (!isNaN(line)) {
preview.line = line;
}
await preview.doUpdate();
return preview;
return new DynamicMarkdownPreview(webview, input,
contentProvider, previewConfigurations, logger, topmostLineMonitor, contributionProvider);
}
public static create(
resource: vscode.Uri,
input: DynamicPreviewInput,
previewColumn: vscode.ViewColumn,
resourceColumn: vscode.ViewColumn,
locked: boolean,
contentProvider: MarkdownContentProvider,
previewConfigurations: MarkdownPreviewConfigurationManager,
logger: Logger,
topmostLineMonitor: MarkdownFileTopmostLineMonitor,
topmostLineMonitor: TopmostLineMonitor,
contributionProvider: MarkdownContributionProvider
): MarkdownPreview {
): DynamicMarkdownPreview {
const webview = vscode.window.createWebviewPanel(
MarkdownPreview.viewType,
MarkdownPreview.getPreviewTitle(resource, locked),
DynamicMarkdownPreview.viewType,
DynamicMarkdownPreview.getPreviewTitle(input.resource, input.locked),
previewColumn, {
enableFindWidget: true,
...MarkdownPreview.getWebviewOptions(resource, contributionProvider.contributions)
...DynamicMarkdownPreview.getWebviewOptions(input.resource, contributionProvider.contributions)
});
return new MarkdownPreview(
webview,
resource,
locked,
resourceColumn,
contentProvider,
previewConfigurations,
logger,
topmostLineMonitor,
contributionProvider);
return new DynamicMarkdownPreview(webview, input,
contentProvider, previewConfigurations, logger, topmostLineMonitor, contributionProvider);
}
private constructor(
webview: vscode.WebviewPanel,
resource: vscode.Uri,
locked: boolean,
private readonly _resourceColumn: vscode.ViewColumn,
input: DynamicPreviewInput,
private readonly _contentProvider: MarkdownContentProvider,
private readonly _previewConfigurations: MarkdownPreviewConfigurationManager,
private readonly _logger: Logger,
topmostLineMonitor: MarkdownFileTopmostLineMonitor,
topmostLineMonitor: TopmostLineMonitor,
private readonly _contributionProvider: MarkdownContributionProvider,
) {
super();
this._resource = resource;
this._locked = locked;
this._resource = input.resource;
this._resourceColumn = input.resourceColumn;
this._locked = input.locked;
this.editor = webview;
if (!isNaN(input.line!)) {
this.line = input.line;
}
this._register(this.editor.onDidDispose(() => {
this.dispose();
@@ -190,7 +174,7 @@ export class MarkdownPreview extends Disposable {
switch (e.type) {
case 'cacheImageSizes':
this.onCacheImageSizes(e.body);
this.imageInfo = e.body;
break;
case 'revealLine':
@@ -221,7 +205,7 @@ export class MarkdownPreview extends Disposable {
}
}));
this._register(topmostLineMonitor.onDidChangeTopmostLine(event => {
this._register(topmostLineMonitor.onDidChanged(event => {
if (this.isPreviewOf(event.resource)) {
this.updateForView(event.resource, event.line);
}
@@ -239,9 +223,11 @@ export class MarkdownPreview extends Disposable {
this._register(vscode.window.onDidChangeActiveTextEditor(editor => {
if (editor && isMarkdownFile(editor.document) && !this._locked) {
this.update(editor.document.uri);
this.update(editor.document.uri, false);
}
}));
this.doUpdate();
}
private readonly _onDisposeEmitter = this._register(new vscode.EventEmitter<void>());
@@ -278,7 +264,6 @@ export class MarkdownPreview extends Disposable {
this._onDisposeEmitter.fire();
this._onDisposeEmitter.dispose();
this._onDidChangeViewStateEmitter.dispose();
this.editor.dispose();
super.dispose();
}
@@ -297,7 +282,6 @@ export class MarkdownPreview extends Disposable {
}
}
// If we have changed resources, cancel any pending updates
const isResourceChange = resource.fsPath !== this._resource.fsPath;
if (isResourceChange) {
@@ -310,9 +294,9 @@ export class MarkdownPreview extends Disposable {
// Schedule update if none is pending
if (!this.throttleTimer) {
if (isResourceChange || this.firstUpdate) {
this.doUpdate();
this.doUpdate(isRefresh);
} else {
this.throttleTimer = setTimeout(() => this.doUpdate(), 300);
this.throttleTimer = setTimeout(() => this.doUpdate(isRefresh), this.delay);
}
}
@@ -320,7 +304,6 @@ export class MarkdownPreview extends Disposable {
}
public refresh() {
this.forceUpdate = true;
this.update(this._resource, true);
}
@@ -350,7 +333,7 @@ export class MarkdownPreview extends Disposable {
}
}
public matches(otherPreview: MarkdownPreview): boolean {
public matches(otherPreview: DynamicMarkdownPreview): boolean {
return this.matchesResource(otherPreview._resource, otherPreview.position, otherPreview._locked);
}
@@ -360,7 +343,7 @@ export class MarkdownPreview extends Disposable {
public toggleLock() {
this._locked = !this._locked;
this.editor.title = MarkdownPreview.getPreviewTitle(this._resource, this._locked);
this.editor.title = DynamicMarkdownPreview.getPreviewTitle(this._resource, this._locked);
}
private get iconPath() {
@@ -408,7 +391,7 @@ export class MarkdownPreview extends Disposable {
}
}
private async doUpdate(): Promise<void> {
private async doUpdate(forceUpdate?: boolean): Promise<void> {
if (this._disposed) {
return;
}
@@ -431,13 +414,12 @@ export class MarkdownPreview extends Disposable {
}
const pendingVersion = new PreviewDocumentVersion(markdownResource, document.version);
if (!this.forceUpdate && this.currentVersion && this.currentVersion.equals(pendingVersion)) {
if (!forceUpdate && this.currentVersion?.equals(pendingVersion)) {
if (this.line) {
this.updateForView(markdownResource, this.line);
}
return;
}
this.forceUpdate = false;
this.currentVersion = pendingVersion;
if (this._resource === markdownResource) {
@@ -463,7 +445,7 @@ export class MarkdownPreview extends Disposable {
): vscode.WebviewOptions {
return {
enableScripts: true,
localResourceRoots: MarkdownPreview.getLocalResourceRoots(resource, contributions)
localResourceRoots: DynamicMarkdownPreview.getLocalResourceRoots(resource, contributions)
};
}
@@ -508,6 +490,9 @@ export class MarkdownPreview extends Disposable {
}
private async onDidClickPreview(line: number): Promise<void> {
// fix #82457, find currently opened but unfocused source tab
await vscode.commands.executeCommand('markdown.showSource');
for (const visibleEditor of vscode.window.visibleTextEditors) {
if (this.isPreviewOf(visibleEditor.document.uri)) {
const editor = await vscode.window.showTextDocument(visibleEditor.document, visibleEditor.viewColumn);
@@ -529,9 +514,9 @@ export class MarkdownPreview extends Disposable {
}
private setContent(html: string): void {
this.editor.title = MarkdownPreview.getPreviewTitle(this._resource, this._locked);
this.editor.title = DynamicMarkdownPreview.getPreviewTitle(this._resource, this._locked);
this.editor.iconPath = this.iconPath;
this.editor.webview.options = MarkdownPreview.getWebviewOptions(this._resource, this._contributionProvider.contributions);
this.editor.webview.options = DynamicMarkdownPreview.getWebviewOptions(this._resource, this._contributionProvider.contributions);
this.editor.webview.html = html;
}
@@ -559,14 +544,4 @@ export class MarkdownPreview extends Disposable {
vscode.commands.executeCommand('_markdown.openDocumentLink', { path: hrefPath, fragment, fromResource: this.resource });
}
private async onCacheImageSizes(imageInfo: { id: string, width: number, height: number; }[]) {
this.imageInfo = imageInfo;
}
}
export interface PreviewSettings {
readonly resourceColumn: vscode.ViewColumn;
readonly previewColumn: vscode.ViewColumn;
readonly locked: boolean;
}

View File

@@ -7,19 +7,61 @@ import * as vscode from 'vscode';
import { Logger } from '../logger';
import { MarkdownContributionProvider } from '../markdownExtensions';
import { disposeAll, Disposable } from '../util/dispose';
import { MarkdownFileTopmostLineMonitor } from '../util/topmostLineMonitor';
import { MarkdownPreview, PreviewSettings } from './preview';
import { TopmostLineMonitor } from '../util/topmostLineMonitor';
import { DynamicMarkdownPreview } from './preview';
import { MarkdownPreviewConfigurationManager } from './previewConfig';
import { MarkdownContentProvider } from './previewContentProvider';
export interface DynamicPreviewSettings {
readonly resourceColumn: vscode.ViewColumn;
readonly previewColumn: vscode.ViewColumn;
readonly locked: boolean;
}
export class MarkdownPreviewManager extends Disposable implements vscode.WebviewPanelSerializer {
class PreviewStore extends Disposable {
private readonly _previews = new Set<DynamicMarkdownPreview>();
public dispose(): void {
super.dispose();
for (const preview of this._previews) {
preview.dispose();
}
this._previews.clear();
}
[Symbol.iterator](): Iterator<DynamicMarkdownPreview> {
return this._previews[Symbol.iterator]();
}
public get(resource: vscode.Uri, previewSettings: DynamicPreviewSettings): DynamicMarkdownPreview | undefined {
for (const preview of this._previews) {
if (preview.matchesResource(resource, previewSettings.previewColumn, previewSettings.locked)) {
return preview;
}
}
return undefined;
}
public add(preview: DynamicMarkdownPreview) {
this._previews.add(preview);
}
public delete(preview: DynamicMarkdownPreview) {
this._previews.delete(preview);
}
}
export class MarkdownPreviewManager extends Disposable implements vscode.WebviewPanelSerializer, vscode.WebviewEditorProvider {
private static readonly markdownPreviewActiveContextKey = 'markdownPreviewFocus';
private readonly _topmostLineMonitor = new MarkdownFileTopmostLineMonitor();
private readonly _topmostLineMonitor = new TopmostLineMonitor();
private readonly _previewConfigurations = new MarkdownPreviewConfigurationManager();
private readonly _previews: MarkdownPreview[] = [];
private _activePreview: MarkdownPreview | undefined = undefined;
private readonly _dynamicPreviews = this._register(new PreviewStore());
private readonly _staticPreviews = this._register(new PreviewStore());
private _activePreview: DynamicMarkdownPreview | undefined = undefined;
public constructor(
private readonly _contentProvider: MarkdownContentProvider,
@@ -27,46 +69,48 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview
private readonly _contributions: MarkdownContributionProvider
) {
super();
this._register(vscode.window.registerWebviewPanelSerializer(MarkdownPreview.viewType, this));
}
public dispose(): void {
super.dispose();
disposeAll(this._previews);
this._register(vscode.window.registerWebviewPanelSerializer(DynamicMarkdownPreview.viewType, this));
this._register(vscode.window.registerWebviewEditorProvider('vscode.markdown.preview.editor', this));
}
public refresh() {
for (const preview of this._previews) {
for (const preview of this._dynamicPreviews) {
preview.refresh();
}
for (const preview of this._staticPreviews) {
preview.refresh();
}
}
public updateConfiguration() {
for (const preview of this._previews) {
for (const preview of this._dynamicPreviews) {
preview.updateConfiguration();
}
for (const preview of this._staticPreviews) {
preview.updateConfiguration();
}
}
public preview(
public openDynamicPreview(
resource: vscode.Uri,
previewSettings: PreviewSettings
settings: DynamicPreviewSettings
): void {
let preview = this.getExistingPreview(resource, previewSettings);
let preview = this._dynamicPreviews.get(resource, settings);
if (preview) {
preview.reveal(previewSettings.previewColumn);
preview.reveal(settings.previewColumn);
} else {
preview = this.createNewPreview(resource, previewSettings);
preview = this.createNewDynamicPreview(resource, settings);
}
preview.update(resource);
}
public get activePreviewResource() {
return this._activePreview && this._activePreview.resource;
return this._activePreview?.resource;
}
public get activePreviewResourceColumn() {
return this._activePreview && this._activePreview.resourceColumn;
return this._activePreview?.resourceColumn;
}
public toggleLock() {
@@ -75,7 +119,7 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview
preview.toggleLock();
// Close any previews that are now redundant, such as having two dynamic previews in the same editor group
for (const otherPreview of this._previews) {
for (const otherPreview of this._dynamicPreviews) {
if (otherPreview !== preview && preview.matches(otherPreview)) {
otherPreview.dispose();
}
@@ -87,35 +131,50 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview
webview: vscode.WebviewPanel,
state: any
): Promise<void> {
const preview = await MarkdownPreview.revive(
const resource = vscode.Uri.parse(state.resource);
const locked = state.locked;
const line = state.line;
const resourceColumn = state.resourceColumn;
const preview = await DynamicMarkdownPreview.revive(
{ resource, locked, line, resourceColumn },
webview,
state,
this._contentProvider,
this._previewConfigurations,
this._logger,
this._topmostLineMonitor,
this._contributions);
this.registerPreview(preview);
this.registerDynamicPreview(preview);
}
private getExistingPreview(
resource: vscode.Uri,
previewSettings: PreviewSettings
): MarkdownPreview | undefined {
return this._previews.find(preview =>
preview.matchesResource(resource, previewSettings.previewColumn, previewSettings.locked));
public async resolveWebviewEditor(
input: { readonly resource: vscode.Uri; },
webview: vscode.WebviewPanel
): Promise<vscode.WebviewEditorCapabilities> {
const preview = DynamicMarkdownPreview.revive(
{ resource: input.resource, locked: false, resourceColumn: vscode.ViewColumn.One },
webview,
this._contentProvider,
this._previewConfigurations,
this._logger,
this._topmostLineMonitor,
this._contributions);
this.registerStaticPreview(preview);
return {};
}
private createNewPreview(
private createNewDynamicPreview(
resource: vscode.Uri,
previewSettings: PreviewSettings
): MarkdownPreview {
const preview = MarkdownPreview.create(
resource,
previewSettings: DynamicPreviewSettings
): DynamicMarkdownPreview {
const preview = DynamicMarkdownPreview.create(
{
resource,
resourceColumn: previewSettings.resourceColumn,
locked: previewSettings.locked,
},
previewSettings.previewColumn,
previewSettings.resourceColumn,
previewSettings.locked,
this._contentProvider,
this._previewConfigurations,
this._logger,
@@ -124,34 +183,48 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview
this.setPreviewActiveContext(true);
this._activePreview = preview;
return this.registerPreview(preview);
return this.registerDynamicPreview(preview);
}
private registerPreview(
preview: MarkdownPreview
): MarkdownPreview {
this._previews.push(preview);
private registerDynamicPreview(preview: DynamicMarkdownPreview): DynamicMarkdownPreview {
this._dynamicPreviews.add(preview);
preview.onDispose(() => {
const existing = this._previews.indexOf(preview);
if (existing === -1) {
return;
}
this._dynamicPreviews.delete(preview);
});
this._previews.splice(existing, 1);
this.trackActive(preview);
preview.onDidChangeViewState(() => {
// Remove other dynamic previews in our column
disposeAll(Array.from(this._dynamicPreviews).filter(otherPreview => preview !== otherPreview && preview.matches(otherPreview)));
});
return preview;
}
private registerStaticPreview(preview: DynamicMarkdownPreview): DynamicMarkdownPreview {
this._staticPreviews.add(preview);
preview.onDispose(() => {
this._staticPreviews.delete(preview);
});
this.trackActive(preview);
return preview;
}
private trackActive(preview: DynamicMarkdownPreview): void {
preview.onDidChangeViewState(({ webviewPanel }) => {
this.setPreviewActiveContext(webviewPanel.active);
this._activePreview = webviewPanel.active ? preview : undefined;
});
preview.onDispose(() => {
if (this._activePreview === preview) {
this.setPreviewActiveContext(false);
this._activePreview = undefined;
}
});
preview.onDidChangeViewState(({ webviewPanel }) => {
disposeAll(this._previews.filter(otherPreview => preview !== otherPreview && preview!.matches(otherPreview)));
this.setPreviewActiveContext(webviewPanel.active);
this._activePreview = webviewPanel.active ? preview : undefined;
});
return preview;
}
private setPreviewActiveContext(value: boolean) {

View File

@@ -41,13 +41,21 @@ export class Logger {
public log(message: string, data?: any): void {
if (this.trace === Trace.Verbose) {
this.appendLine(`[Log - ${(new Date().toLocaleTimeString())}] ${message}`);
this.appendLine(`[Log - ${this.now()}] ${message}`);
if (data) {
this.appendLine(Logger.data2String(data));
}
}
}
private now(): string {
const now = new Date();
return padLeft(now.getUTCHours() + '', 2, '0')
+ ':' + padLeft(now.getMinutes() + '', 2, '0')
+ ':' + padLeft(now.getUTCSeconds() + '', 2, '0') + '.' + now.getMilliseconds();
}
public updateConfiguration() {
this.trace = this.readTrace();
}
@@ -73,3 +81,7 @@ export class Logger {
return JSON.stringify(data, undefined, 2);
}
}
function padLeft(s: string, n: number, pad = ' ') {
return pad.repeat(Math.max(0, n - s.length)) + s;
}

View File

@@ -234,6 +234,11 @@ export class MarkdownEngine {
const normalizeLink = md.normalizeLink;
md.normalizeLink = (link: string) => {
try {
// Normalize VS Code schemes to target the current version
if (isOfScheme(Schemes.vscode, link) || isOfScheme(Schemes['vscode-insiders'], link)) {
return normalizeLink(vscode.Uri.parse(link).with({ scheme: vscode.env.uriScheme }).toString());
}
// 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
@@ -268,7 +273,11 @@ export class MarkdownEngine {
const validateLink = md.validateLink;
md.validateLink = (link: string) => {
// support file:// links
return validateLink(link) || isOfScheme(Schemes.file, link) || /^data:image\/.*?;/.test(link);
return validateLink(link)
|| isOfScheme(Schemes.file, link)
|| isOfScheme(Schemes.vscode, link)
|| isOfScheme(Schemes['vscode-insiders'], link)
|| /^data:image\/.*?;/.test(link);
};
}

View File

@@ -10,7 +10,7 @@ const suite = 'Integration Markdown Tests';
const options: any = {
ui: 'tdd',
useColors: true,
useColors: (!process.env.BUILD_ARTIFACTSTAGINGDIRECTORY && process.platform !== 'win32'),
timeout: 60000
};

View File

@@ -13,7 +13,7 @@ export const Schemes = {
data: 'data:',
vscode: 'vscode:',
'vscode-insiders': 'vscode-insiders:',
'vscode-resource': 'vscode-resource',
'vscode-resource': 'vscode-resource:',
};
const knownSchemes = [

View File

@@ -4,33 +4,28 @@
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import { disposeAll } from '../util/dispose';
import { Disposable } from '../util/dispose';
import { isMarkdownFile } from './file';
export class MarkdownFileTopmostLineMonitor {
private readonly disposables: vscode.Disposable[] = [];
export class TopmostLineMonitor extends Disposable {
private readonly pendingUpdates = new Map<string, number>();
private readonly throttle = 50;
constructor() {
vscode.window.onDidChangeTextEditorVisibleRanges(event => {
super();
this._register(vscode.window.onDidChangeTextEditorVisibleRanges(event => {
if (isMarkdownFile(event.textEditor.document)) {
const line = getVisibleLine(event.textEditor);
if (typeof line === 'number') {
this.updateLine(event.textEditor.document.uri, line);
}
}
}, null, this.disposables);
}));
}
dispose() {
disposeAll(this.disposables);
}
private readonly _onDidChangeTopmostLineEmitter = new vscode.EventEmitter<{ resource: vscode.Uri, line: number }>();
public readonly onDidChangeTopmostLine = this._onDidChangeTopmostLineEmitter.event;
private readonly _onChanged = this._register(new vscode.EventEmitter<{ readonly resource: vscode.Uri, readonly line: number }>());
public readonly onDidChanged = this._onChanged.event;
private updateLine(
resource: vscode.Uri,
@@ -41,7 +36,7 @@ export class MarkdownFileTopmostLineMonitor {
// schedule update
setTimeout(() => {
if (this.pendingUpdates.has(key)) {
this._onDidChangeTopmostLineEmitter.fire({
this._onChanged.fire({
resource,
line: this.pendingUpdates.get(key) as number
});

File diff suppressed because it is too large Load Diff