Skip to content

Commit

Permalink
Merge pull request #123727 from microsoft/dev/t-andreamah/markdown-st…
Browse files Browse the repository at this point in the history
…atic-preview-scroll-state

Markdown Static Preview Scrolling Fixes
  • Loading branch information
andreamah authored May 20, 2021
2 parents 62bbbcc + 18c2549 commit ab5df44
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 15 deletions.
49 changes: 37 additions & 12 deletions extensions/markdown-language-features/src/features/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { MarkdownContributionProvider } from '../markdownExtensions';
import { Disposable } from '../util/dispose';
import { isMarkdownFile } from '../util/file';
import { normalizeResource, WebviewResourceProvider } from '../util/resources';
import { getVisibleLine, TopmostLineMonitor } from '../util/topmostLineMonitor';
import { getVisibleLine, LastScrollLocation, TopmostLineMonitor } from '../util/topmostLineMonitor';
import { MarkdownPreviewConfigurationManager } from './previewConfig';
import { MarkdownContentProvider, MarkdownContentProviderOutput } from './previewContentProvider';
import { MarkdownEngine } from '../markdownEngine';
Expand Down Expand Up @@ -120,6 +120,8 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider {
private imageInfo: { readonly id: string, readonly width: number, readonly height: number; }[] = [];

private readonly _fileWatchersBySrc = new Map</* src: */ string, vscode.FileSystemWatcher>();
private readonly _onScrollEmitter = this._register(new vscode.EventEmitter<LastScrollLocation>());
public readonly onScroll = this._onScrollEmitter.event;

constructor(
webview: vscode.WebviewPanel,
Expand Down Expand Up @@ -324,7 +326,7 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider {

private onDidScrollPreview(line: number) {
this.line = line;

this._onScrollEmitter.fire({ line: this.line, uri: this._resource });
const config = this._previewConfigurations.loadAndCacheConfiguration(this._resource);
if (!config.scrollEditorWithPreview) {
return;
Expand All @@ -336,13 +338,7 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider {
}

this.isScrolling = true;
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);
scrollEditorToLine(line, editor);
}
}

Expand Down Expand Up @@ -497,11 +493,13 @@ export class StaticMarkdownPreview extends Disposable implements ManagedMarkdown
webview: vscode.WebviewPanel,
contentProvider: MarkdownContentProvider,
previewConfigurations: MarkdownPreviewConfigurationManager,
topmostLineMonitor: TopmostLineMonitor,
logger: Logger,
contributionProvider: MarkdownContributionProvider,
engine: MarkdownEngine,
scrollLine?: number,
): StaticMarkdownPreview {
return new StaticMarkdownPreview(webview, resource, contentProvider, previewConfigurations, logger, contributionProvider, engine);
return new StaticMarkdownPreview(webview, resource, contentProvider, previewConfigurations, topmostLineMonitor, logger, contributionProvider, engine, scrollLine);
}

private readonly preview: MarkdownPreview;
Expand All @@ -511,13 +509,15 @@ export class StaticMarkdownPreview extends Disposable implements ManagedMarkdown
resource: vscode.Uri,
contentProvider: MarkdownContentProvider,
private readonly _previewConfigurations: MarkdownPreviewConfigurationManager,
topmostLineMonitor: TopmostLineMonitor,
logger: Logger,
contributionProvider: MarkdownContributionProvider,
engine: MarkdownEngine,
scrollLine?: number,
) {
super();

this.preview = this._register(new MarkdownPreview(this._webviewPanel, resource, undefined, {
const topScrollLocation = scrollLine ? new StartingScrollLine(scrollLine) : undefined;
this.preview = this._register(new MarkdownPreview(this._webviewPanel, resource, topScrollLocation, {
getAdditionalState: () => { return {}; },
openPreviewLinkToMarkdownFile: () => { /* todo */ }
}, engine, contentProvider, _previewConfigurations, logger, contributionProvider));
Expand All @@ -529,6 +529,16 @@ export class StaticMarkdownPreview extends Disposable implements ManagedMarkdown
this._register(this._webviewPanel.onDidChangeViewState(e => {
this._onDidChangeViewState.fire(e);
}));

this._register(this.preview.onScroll((scrollInfo) => {
topmostLineMonitor.setPreviousEditorLine(scrollInfo);
}));

this._register(topmostLineMonitor.onDidChanged(event => {
if (this.preview.isPreviewOf(event.resource)) {
this.preview.scrollTo(event.line);
}
}));
}

private readonly _onDispose = this._register(new vscode.EventEmitter<void>());
Expand Down Expand Up @@ -789,3 +799,18 @@ export class DynamicMarkdownPreview extends Disposable implements ManagedMarkdow
}
}

/**
* Change the top-most visible line of `editor` to be at `line`
*/
export function scrollEditorToLine(
line: number,
editor: vscode.TextEditor
) {
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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ import { MarkdownEngine } from '../markdownEngine';
import { MarkdownContributionProvider } from '../markdownExtensions';
import { Disposable, disposeAll } from '../util/dispose';
import { TopmostLineMonitor } from '../util/topmostLineMonitor';
import { DynamicMarkdownPreview, ManagedMarkdownPreview, StartingScrollFragment, StaticMarkdownPreview } from './preview';
import { DynamicMarkdownPreview, ManagedMarkdownPreview, StartingScrollFragment, StaticMarkdownPreview, scrollEditorToLine } from './preview';
import { MarkdownPreviewConfigurationManager } from './previewConfig';
import { MarkdownContentProvider } from './previewContentProvider';
import { isMarkdownFile } from '../util/file';

export interface DynamicPreviewSettings {
readonly resourceColumn: vscode.ViewColumn;
Expand Down Expand Up @@ -75,6 +76,17 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview
super();
this._register(vscode.window.registerWebviewPanelSerializer(DynamicMarkdownPreview.viewType, this));
this._register(vscode.window.registerCustomEditorProvider(this.customEditorViewType, this));

this._register(vscode.window.onDidChangeActiveTextEditor(textEditor => {

// When at a markdown file, apply existing scroll settings
if (textEditor && textEditor.document && isMarkdownFile(textEditor.document)) {
const line = this._topmostLineMonitor.getPreviousEditorLineByUri(textEditor.document.uri);
if (line) {
scrollEditorToLine(line, textEditor);
}
}
}));
}

public refresh() {
Expand Down Expand Up @@ -160,14 +172,18 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview
document: vscode.TextDocument,
webview: vscode.WebviewPanel
): Promise<void> {
const lineNumber = this._topmostLineMonitor.getPreviousEditorLineByUri(document.uri);
const preview = StaticMarkdownPreview.revive(
document.uri,
webview,
this._contentProvider,
this._previewConfigurations,
this._topmostLineMonitor,
this._logger,
this._contributions,
this._engine);
this._engine,
lineNumber
);
this.registerStaticPreview(preview);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,32 @@ import * as vscode from 'vscode';
import { Disposable } from '../util/dispose';
import { isMarkdownFile } from './file';

export interface LastScrollLocation {
readonly line: number;
readonly uri: vscode.Uri;
}

export class TopmostLineMonitor extends Disposable {

private readonly pendingUpdates = new Map<string, number>();
private readonly throttle = 50;
private previousEditorInfo = new Map<string, LastScrollLocation>();
public isPrevEditorCustom = false;

constructor() {
super();

if (vscode.window.activeTextEditor) {
const line = getVisibleLine(vscode.window.activeTextEditor);
this.setPreviousEditorLine({ uri: vscode.window.activeTextEditor.document.uri, line: line ?? 0 });
}

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);
this.setPreviousEditorLine({ uri: event.textEditor.document.uri, line: line });
}
}
}));
Expand All @@ -27,7 +41,16 @@ export class TopmostLineMonitor extends Disposable {
private readonly _onChanged = this._register(new vscode.EventEmitter<{ readonly resource: vscode.Uri, readonly line: number }>());
public readonly onDidChanged = this._onChanged.event;

private updateLine(
public setPreviousEditorLine(scrollLocation: LastScrollLocation): void {
this.previousEditorInfo.set(scrollLocation.uri.toString(), scrollLocation);
}

public getPreviousEditorLineByUri(resource: vscode.Uri): number | undefined {
const scrollLoc = this.previousEditorInfo.get(resource.toString());
return scrollLoc?.line;
}

public updateLine(
resource: vscode.Uri,
line: number
) {
Expand Down

0 comments on commit ab5df44

Please sign in to comment.