Path: blob/main/extensions/markdown-language-features/src/preview/topmostLineMonitor.ts
3292 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import * as vscode from 'vscode';6import { Disposable } from '../util/dispose';7import { isMarkdownFile } from '../util/file';8import { ResourceMap } from '../util/resourceMap';910export interface LastScrollLocation {11readonly line: number;12readonly uri: vscode.Uri;13}1415export class TopmostLineMonitor extends Disposable {1617private readonly _pendingUpdates = new ResourceMap<number>();18private readonly _throttle = 50;19private readonly _previousTextEditorInfo = new ResourceMap<LastScrollLocation>();20private readonly _previousStaticEditorInfo = new ResourceMap<LastScrollLocation>();2122constructor() {23super();2425if (vscode.window.activeTextEditor) {26const line = getVisibleLine(vscode.window.activeTextEditor);27this.setPreviousTextEditorLine({ uri: vscode.window.activeTextEditor.document.uri, line: line ?? 0 });28}2930this._register(vscode.window.onDidChangeTextEditorVisibleRanges(event => {31if (isMarkdownFile(event.textEditor.document)) {32const line = getVisibleLine(event.textEditor);33if (typeof line === 'number') {34this.updateLine(event.textEditor.document.uri, line);35this.setPreviousTextEditorLine({ uri: event.textEditor.document.uri, line: line });36}37}38}));39}4041private readonly _onChanged = this._register(new vscode.EventEmitter<{ readonly resource: vscode.Uri; readonly line: number }>());42public readonly onDidChanged = this._onChanged.event;4344public setPreviousStaticEditorLine(scrollLocation: LastScrollLocation): void {45this._previousStaticEditorInfo.set(scrollLocation.uri, scrollLocation);46}4748public getPreviousStaticEditorLineByUri(resource: vscode.Uri): number | undefined {49const scrollLoc = this._previousStaticEditorInfo.get(resource);50this._previousStaticEditorInfo.delete(resource);51return scrollLoc?.line;52}535455public setPreviousTextEditorLine(scrollLocation: LastScrollLocation): void {56this._previousTextEditorInfo.set(scrollLocation.uri, scrollLocation);57}5859public getPreviousTextEditorLineByUri(resource: vscode.Uri): number | undefined {60const scrollLoc = this._previousTextEditorInfo.get(resource);61this._previousTextEditorInfo.delete(resource);62return scrollLoc?.line;63}6465public getPreviousStaticTextEditorLineByUri(resource: vscode.Uri): number | undefined {66const state = this._previousStaticEditorInfo.get(resource);67return state?.line;68}6970public updateLine(71resource: vscode.Uri,72line: number73) {74if (!this._pendingUpdates.has(resource)) {75// schedule update76setTimeout(() => {77if (this._pendingUpdates.has(resource)) {78this._onChanged.fire({79resource,80line: this._pendingUpdates.get(resource) as number81});82this._pendingUpdates.delete(resource);83}84}, this._throttle);85}8687this._pendingUpdates.set(resource, line);88}89}9091/**92* Get the top-most visible range of `editor`.93*94* Returns a fractional line number based the visible character within the line.95* Floor to get real line number96*/97export function getVisibleLine(98editor: vscode.TextEditor99): number | undefined {100if (!editor.visibleRanges.length) {101return undefined;102}103104const firstVisiblePosition = editor.visibleRanges[0].start;105const lineNumber = firstVisiblePosition.line;106const line = editor.document.lineAt(lineNumber);107const progress = firstVisiblePosition.character / (line.text.length + 2);108return lineNumber + progress;109}110111112