mirror of
https://github.com/Microsoft/vscode
synced 2024-08-27 21:09:43 +00:00
Scroll sync markdown editor with markdown preview (#44454)
Fixes #19459 Syncs the markdown preview's viewport with the markdown editor's using the proposed visible ranges API
This commit is contained in:
parent
5f25d3c167
commit
2279b4d252
|
@ -75,12 +75,13 @@
|
|||
* @returns {{ previous: CodeLineElement, next?: CodeLineElement }}
|
||||
*/
|
||||
function getElementsForSourceLine(targetLine) {
|
||||
const lineNumber = Math.floor(targetLine)
|
||||
const lines = getCodeLineElements();
|
||||
let previous = lines[0] || null;
|
||||
for (const entry of lines) {
|
||||
if (entry.line === targetLine) {
|
||||
if (entry.line === lineNumber) {
|
||||
return { previous: entry, next: null };
|
||||
} else if (entry.line > targetLine) {
|
||||
} else if (entry.line > lineNumber) {
|
||||
return { previous, next: entry };
|
||||
}
|
||||
previous = entry;
|
||||
|
@ -124,10 +125,6 @@
|
|||
return { previous };
|
||||
}
|
||||
|
||||
function getSourceRevealAddedOffset() {
|
||||
return -(window.innerHeight * 1 / 5);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to reveal the element for a source line in the editor.
|
||||
*
|
||||
|
@ -136,17 +133,21 @@
|
|||
function scrollToRevealSourceLine(line) {
|
||||
const { previous, next } = getElementsForSourceLine(line);
|
||||
marker.update(previous && previous.element);
|
||||
if (previous && settings.scrollPreviewWithEditorSelection) {
|
||||
if (previous && settings.scrollPreviewWithEditor) {
|
||||
let scrollTo = 0;
|
||||
if (next) {
|
||||
const rect = previous.element.getBoundingClientRect();
|
||||
const previousTop = rect.top;
|
||||
|
||||
if (next && next.line !== previous.line) {
|
||||
// Between two elements. Go to percentage offset between them.
|
||||
const betweenProgress = (line - previous.line) / (next.line - previous.line);
|
||||
const elementOffset = next.element.getBoundingClientRect().top - previous.element.getBoundingClientRect().top;
|
||||
scrollTo = previous.element.getBoundingClientRect().top + betweenProgress * elementOffset;
|
||||
const elementOffset = next.element.getBoundingClientRect().top - previousTop;
|
||||
scrollTo = previousTop + betweenProgress * elementOffset;
|
||||
} else {
|
||||
scrollTo = previous.element.getBoundingClientRect().top;
|
||||
scrollTo = previousTop;
|
||||
}
|
||||
window.scroll(0, Math.max(1, window.scrollY + scrollTo + getSourceRevealAddedOffset()));
|
||||
|
||||
window.scroll(0, Math.max(1, window.scrollY + scrollTo));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -192,7 +193,7 @@
|
|||
const settings = JSON.parse(document.getElementById('vscode-markdown-preview-data').getAttribute('data-settings'));
|
||||
|
||||
function onLoad() {
|
||||
if (settings.scrollPreviewWithEditorSelection) {
|
||||
if (settings.scrollPreviewWithEditor) {
|
||||
setTimeout(() => {
|
||||
const initialLine = +settings.line;
|
||||
if (!isNaN(initialLine)) {
|
||||
|
|
|
@ -272,10 +272,10 @@
|
|||
"description": "%markdown.preview.lineHeight.desc%",
|
||||
"scope": "resource"
|
||||
},
|
||||
"markdown.preview.scrollPreviewWithEditorSelection": {
|
||||
"markdown.preview.scrollPreviewWithEditor": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "%markdown.preview.scrollPreviewWithEditorSelection.desc%",
|
||||
"description": "%markdown.preview.scrollPreviewWithEditor.desc%",
|
||||
"scope": "resource"
|
||||
},
|
||||
"markdown.preview.markEditorSelection": {
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
"markdown.preview.fontSize.desc": "Controls the font size in pixels used in the markdown preview.",
|
||||
"markdown.preview.lineHeight.desc": "Controls the line height used in the markdown preview. This number is relative to the font size.",
|
||||
"markdown.preview.markEditorSelection.desc": "Mark the current editor selection in the markdown preview.",
|
||||
"markdown.preview.scrollEditorWithPreview.desc": "When the markdown preview is scrolled, update the view of the editor.",
|
||||
"markdown.preview.scrollPreviewWithEditorSelection.desc": "Scrolls the markdown preview to reveal the currently selected line from the editor.",
|
||||
"markdown.preview.scrollEditorWithPreview.desc": "When a markdown preview is scrolled, update the view of the editor.",
|
||||
"markdown.preview.scrollPreviewWithEditor.desc": "When a markdown editor preview is scrolled, update the view of the preview.",
|
||||
"markdown.preview.title" : "Open Preview",
|
||||
"markdown.previewFrontMatter.dec": "Sets how YAML front matter should be rendered in the markdown preview. 'hide' removes the front matter. Otherwise, the front matter is treated as markdown content.",
|
||||
"markdown.previewSide.title" : "Open Preview to the Side",
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { Command } from '../commandManager';
|
||||
|
||||
export class DidClickCommand implements Command {
|
||||
public readonly id = '_markdown.didClick';
|
||||
|
||||
public execute(uri: string, line: number) {
|
||||
const sourceUri = vscode.Uri.parse(decodeURIComponent(uri));
|
||||
return vscode.workspace.openTextDocument(sourceUri)
|
||||
.then(document => vscode.window.showTextDocument(document))
|
||||
.then(editor =>
|
||||
vscode.commands.executeCommand('revealLine', { lineNumber: Math.floor(line), at: 'center' })
|
||||
.then(() => editor))
|
||||
.then(editor => {
|
||||
if (editor) {
|
||||
editor.selection = new vscode.Selection(
|
||||
new vscode.Position(Math.floor(line), 0),
|
||||
new vscode.Position(Math.floor(line), 0));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -10,5 +10,4 @@ export { ShowSourceCommand } from './showSource';
|
|||
export { RefreshPreviewCommand } from './refreshPreview';
|
||||
export { ShowPreviewSecuritySelectorCommand } from './showPreviewSecuritySelector';
|
||||
export { RevealLineCommand } from './revealLine';
|
||||
export { DidClickCommand } from './didClick';
|
||||
export { MoveCursorToPositionCommand } from './moveCursorToPosition';
|
||||
|
|
|
@ -6,29 +6,19 @@
|
|||
import * as vscode from 'vscode';
|
||||
import { Command } from '../commandManager';
|
||||
import { Logger } from '../logger';
|
||||
import { isMarkdownFile } from '../features/previewContentProvider';
|
||||
import { MarkdownPreviewManager } from '../features/previewContentProvider';
|
||||
|
||||
export class RevealLineCommand implements Command {
|
||||
public readonly id = '_markdown.revealLine';
|
||||
|
||||
public constructor(
|
||||
private readonly logger: Logger
|
||||
private readonly logger: Logger,
|
||||
private readonly previewManager: MarkdownPreviewManager
|
||||
) { }
|
||||
|
||||
public execute(uri: string, line: number) {
|
||||
const sourceUri = vscode.Uri.parse(decodeURIComponent(uri));
|
||||
this.logger.log('revealLine', { uri, sourceUri: sourceUri.toString(), line });
|
||||
|
||||
vscode.window.visibleTextEditors
|
||||
.filter(editor => isMarkdownFile(editor.document) && editor.document.uri.fsPath === sourceUri.fsPath)
|
||||
.forEach(editor => {
|
||||
const sourceLine = Math.floor(line);
|
||||
const fraction = line - sourceLine;
|
||||
const text = editor.document.lineAt(sourceLine).text;
|
||||
const start = Math.floor(fraction * text.length);
|
||||
editor.revealRange(
|
||||
new vscode.Range(sourceLine, start, sourceLine + 1, 0),
|
||||
vscode.TextEditorRevealType.AtTop);
|
||||
});
|
||||
this.previewManager.revealLine(sourceUri, line);
|
||||
}
|
||||
}
|
|
@ -45,11 +45,10 @@ export function activate(context: vscode.ExtensionContext) {
|
|||
commandManager.register(new commands.ShowPinnedPreviewToSideCommand(previewManager, telemetryReporter));
|
||||
commandManager.register(new commands.ShowSourceCommand());
|
||||
commandManager.register(new commands.RefreshPreviewCommand(previewManager));
|
||||
commandManager.register(new commands.RevealLineCommand(logger));
|
||||
commandManager.register(new commands.RevealLineCommand(logger, previewManager));
|
||||
commandManager.register(new commands.MoveCursorToPositionCommand());
|
||||
commandManager.register(new commands.ShowPreviewSecuritySelectorCommand(previewSecuritySelector));
|
||||
commandManager.register(new commands.OnPreviewStyleLoadErrorCommand());
|
||||
commandManager.register(new commands.DidClickCommand());
|
||||
commandManager.register(new commands.OpenDocumentLinkCommand(engine));
|
||||
|
||||
context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(() => {
|
||||
|
|
|
@ -33,7 +33,7 @@ export class MarkdownPreviewConfig {
|
|||
public readonly lineBreaks: boolean;
|
||||
public readonly doubleClickToSwitchToEditor: boolean;
|
||||
public readonly scrollEditorWithPreview: boolean;
|
||||
public readonly scrollPreviewWithEditorSelection: boolean;
|
||||
public readonly scrollPreviewWithEditor: boolean;
|
||||
public readonly markEditorSelection: boolean;
|
||||
|
||||
public readonly lineHeight: number;
|
||||
|
@ -54,7 +54,7 @@ export class MarkdownPreviewConfig {
|
|||
}
|
||||
|
||||
this.previewFrontMatter = markdownConfig.get<string>('previewFrontMatter', 'hide');
|
||||
this.scrollPreviewWithEditorSelection = !!markdownConfig.get<boolean>('preview.scrollPreviewWithEditorSelection', true);
|
||||
this.scrollPreviewWithEditor = !!markdownConfig.get<boolean>('preview.scrollPreviewWithEditor', true);
|
||||
this.scrollEditorWithPreview = !!markdownConfig.get<boolean>('preview.scrollEditorWithPreview', true);
|
||||
this.lineBreaks = !!markdownConfig.get<boolean>('preview.breaks', false);
|
||||
this.doubleClickToSwitchToEditor = !!markdownConfig.get<boolean>('preview.doubleClickToSwitchToEditor', true);
|
||||
|
@ -227,7 +227,7 @@ export class MarkdownContentProvider {
|
|||
const initialData = {
|
||||
source: sourceUri.toString(),
|
||||
line: initialLine,
|
||||
scrollPreviewWithEditorSelection: config.scrollPreviewWithEditorSelection,
|
||||
scrollPreviewWithEditor: config.scrollPreviewWithEditor,
|
||||
scrollEditorWithPreview: config.scrollEditorWithPreview,
|
||||
doubleClickToSwitchToEditor: config.doubleClickToSwitchToEditor,
|
||||
disableSecurityWarnings: this.cspArbiter.shouldDisableSecurityWarnings()
|
||||
|
@ -279,11 +279,16 @@ class MarkdownPreview {
|
|||
public static previewScheme = 'vscode-markdown-preview';
|
||||
private static previewCount = 0;
|
||||
|
||||
public isScrolling = false;
|
||||
|
||||
private readonly webview: vscode.Webview;
|
||||
private throttleTimer: any;
|
||||
private initialLine: number | undefined = undefined;
|
||||
private readonly disposables: vscode.Disposable[] = [];
|
||||
<<<<<<< HEAD
|
||||
private firstUpdate = true;
|
||||
=======
|
||||
>>>>>>> Scroll sync markdown editor with markdown preview
|
||||
private currentVersion?: { resource: vscode.Uri, version: number };
|
||||
|
||||
constructor(
|
||||
|
@ -320,10 +325,11 @@ class MarkdownPreview {
|
|||
}
|
||||
}, null, this.disposables);
|
||||
|
||||
vscode.window.onDidChangeTextEditorSelection(event => {
|
||||
vscode.window.onDidChangeTextEditorVisibleRanges(event => {
|
||||
if (isMarkdownFile(event.textEditor.document) && this.isPreviewOf(event.textEditor.document.uri)) {
|
||||
const resource = event.textEditor.document.uri;
|
||||
this.updateForSelection(resource, event.selections[0].active.line);
|
||||
const line = getVisibleLine(event.textEditor);
|
||||
this.updateForView(resource, line);
|
||||
}
|
||||
}, null, this.disposables);
|
||||
}
|
||||
|
@ -347,7 +353,7 @@ class MarkdownPreview {
|
|||
public update(resource: vscode.Uri) {
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
if (editor && editor.document.uri.fsPath === resource.fsPath) {
|
||||
this.initialLine = editor.selection.active.line;
|
||||
this.initialLine = getVisibleLine(editor);
|
||||
} else {
|
||||
this.initialLine = undefined;
|
||||
}
|
||||
|
@ -409,14 +415,19 @@ class MarkdownPreview {
|
|||
: localize('previewTitle', 'Preview {0}', path.basename(resource.fsPath));
|
||||
}
|
||||
|
||||
private updateForSelection(resource: vscode.Uri, line: number) {
|
||||
private updateForView(resource: vscode.Uri, topLine: number) {
|
||||
if (!this.isPreviewOf(resource)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.logger.log('updatePreviewForSelection', { markdownFile: resource });
|
||||
this.initialLine = line;
|
||||
this.webview.postMessage({ line, source: resource.toString() });
|
||||
if (this.isScrolling) {
|
||||
this.isScrolling = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this.logger.log('updateForView', { markdownFile: resource });
|
||||
this.initialLine = topLine;
|
||||
this.webview.postMessage({ line: topLine, source: resource.toString() });
|
||||
}
|
||||
|
||||
private async doUpdate(): Promise<void> {
|
||||
|
@ -426,7 +437,7 @@ class MarkdownPreview {
|
|||
const document = await vscode.workspace.openTextDocument(resource);
|
||||
if (this.currentVersion && this.currentVersion.resource.fsPath === resource.fsPath && this.currentVersion.version === document.version) {
|
||||
if (this.initialLine) {
|
||||
this.updateForSelection(resource, this.initialLine);
|
||||
this.updateForView(resource, this.initialLine);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -528,6 +539,31 @@ export class MarkdownPreviewManager {
|
|||
preview.update(resource);
|
||||
}
|
||||
|
||||
public revealLine(
|
||||
resource: vscode.Uri,
|
||||
line: number
|
||||
) {
|
||||
for (const editor of vscode.window.visibleTextEditors) {
|
||||
if (!isMarkdownFile(editor.document) || editor.document.uri.fsPath !== resource.fsPath) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const sourceLine = Math.floor(line);
|
||||
const fraction = line - sourceLine;
|
||||
const text = editor.document.lineAt(sourceLine).text;
|
||||
const start = Math.floor(fraction * text.length);
|
||||
editor.revealRange(
|
||||
new vscode.Range(sourceLine, start, sourceLine + 1, 0),
|
||||
vscode.TextEditorRevealType.AtTop);
|
||||
}
|
||||
|
||||
for (const preview of this.previews) {
|
||||
if (preview.isPreviewOf(resource)) {
|
||||
preview.isScrolling = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private getExistingPreview(
|
||||
resource: vscode.Uri,
|
||||
previewSettings: PreviewSettings
|
||||
|
@ -545,4 +581,11 @@ function disposeAll(disposables: vscode.Disposable[]) {
|
|||
item.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getVisibleLine(editor: vscode.TextEditor): number {
|
||||
const lineNumber = editor.visibleRanges[0].start.line;
|
||||
const line = editor.document.lineAt(lineNumber);
|
||||
const progress = Math.min(0.999, editor.visibleRanges[0].start.character / (line.text.length + 1));
|
||||
return lineNumber + progress;
|
||||
}
|
|
@ -122,7 +122,7 @@ export class MainThreadTextEditorProperties {
|
|||
delta.visibleRanges = this.visibleRanges;
|
||||
}
|
||||
|
||||
if (delta.selections || delta.options) {
|
||||
if (delta.selections || delta.options || delta.visibleRanges) {
|
||||
// something changed
|
||||
return delta;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue