diff --git a/extensions/markdown/media/main.js b/extensions/markdown/media/main.js index 1c3414b843c..fb99d8043f2 100644 --- a/extensions/markdown/media/main.js +++ b/extensions/markdown/media/main.js @@ -77,23 +77,17 @@ } } - function didUpdateScrollPosition(offset) { + function getEditorLineNumberForPageOffset(offset) { const {previous, next} = getLineElementsAtPageOffset(offset); if (previous) { - let line = 0; if (next) { const betweenProgress = (offset - window.scrollY - previous.element.getBoundingClientRect().top) / (next.element.getBoundingClientRect().top - previous.element.getBoundingClientRect().top); - line = previous.line + Math.floor(betweenProgress * (next.line - previous.line)); + return previous.line + betweenProgress * (next.line - previous.line); } else { - line = previous.line; + return previous.line; } - - const args = [window.initialData.source, line]; - window.parent.postMessage({ - command: "did-click-link", - data: `command:_markdown.didClick?${encodeURIComponent(JSON.stringify(args))}` - }, "file://"); } + return null; } @@ -119,6 +113,7 @@ } } + var scrollDisabled = false; var pageHeight = 0; var marker = new ActiveLineMarker(); @@ -127,6 +122,7 @@ if (window.initialData.enablePreviewSync) { const initialLine = +window.initialData.line || 0; + scrollDisabled = true; scrollToRevealSourceLine(initialLine); } }; @@ -144,19 +140,38 @@ window.addEventListener('message', event => { const line = +event.data.line; if (!isNaN(line)) { + scrollDisabled = true; scrollToRevealSourceLine(line); } }, false); document.ondblclick = (e) => { const offset = e.pageY; - didUpdateScrollPosition(offset); + const line = getEditorLineNumberForPageOffset(offset); + if (!isNaN(line)) { + const args = [window.initialData.source, line]; + window.parent.postMessage({ + command: "did-click-link", + data: `command:_markdown.didClick?${encodeURIComponent(JSON.stringify(args))}` + }, "file://"); + } }; - /** - window.onscroll = () => { - didUpdateScrollPosition(window.scrollY); - }; - */ + if (window.initialData.enableScrollSync) { + window.onscroll = () => { + if (scrollDisabled) { + scrollDisabled = false; + } else { + const line = getEditorLineNumberForPageOffset(window.scrollY); + if (!isNaN(line)) { + const args = [window.initialData.source, line]; + window.parent.postMessage({ + command: "did-click-link", + data: `command:_markdown.revealLine?${encodeURIComponent(JSON.stringify(args))}` + }, "file://"); + } + } + }; + } } }()); \ No newline at end of file diff --git a/extensions/markdown/package.json b/extensions/markdown/package.json index fcec5143c1f..d04d8c15efa 100644 --- a/extensions/markdown/package.json +++ b/extensions/markdown/package.json @@ -152,6 +152,11 @@ "type": "boolean", "default": true, "description": "%markdown.preview.experimentalSyncronizationEnabled.desc%" + }, + "markdown.preview.synchronizePreviewScrollingToEditor": { + "type": "boolean", + "default": true, + "description": "%markdown.preview.synchronizePreviewScrollingToEditor.desc%" } } } diff --git a/extensions/markdown/package.nls.json b/extensions/markdown/package.nls.json index be901187647..3525054ac59 100644 --- a/extensions/markdown/package.nls.json +++ b/extensions/markdown/package.nls.json @@ -7,5 +7,6 @@ "markdown.preview.fontFamily.desc": "Controls the font family used in the markdown preview.", "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.experimentalSyncronizationEnabled.desc": "Enable experimental syncronization between the markdown preview and the editor" + "markdown.preview.experimentalSyncronizationEnabled.desc": "Enable experimental syncronization between the markdown preview and the editor", + "markdown.preview.synchronizePreviewScrollingToEditor.desc": "When the preview is scrolled, update the view of the editor" } \ No newline at end of file diff --git a/extensions/markdown/src/extension.ts b/extensions/markdown/src/extension.ts index a0ec0c1bbf2..231b42f5a64 100644 --- a/extensions/markdown/src/extension.ts +++ b/extensions/markdown/src/extension.ts @@ -40,10 +40,26 @@ export function activate(context: vscode.ExtensionContext) { context.subscriptions.push(vscode.commands.registerCommand('markdown.showPreviewToSide', uri => showPreview(uri, true))); context.subscriptions.push(vscode.commands.registerCommand('markdown.showSource', showSource)); + context.subscriptions.push(vscode.commands.registerCommand('_markdown.revealLine', (uri, line) => { + const sourceUri = vscode.Uri.parse(decodeURIComponent(uri)); + vscode.window.visibleTextEditors + .filter(editor => editor.document.uri.path === sourceUri.path) + .forEach(editor => { + const sourceLine = Math.floor(line); + const text = editor.document.getText(new vscode.Range(sourceLine, 0, sourceLine + 1, 0)); + const fraction = line - Math.floor(line); + const start = fraction * text.length; + editor.revealRange( + new vscode.Range(sourceLine, start, sourceLine + 1, 0), + vscode.TextEditorRevealType.AtTop); + }); + })); + context.subscriptions.push(vscode.commands.registerCommand('_markdown.didClick', (uri, line) => { - return vscode.workspace.openTextDocument(vscode.Uri.parse(decodeURIComponent(uri))) + 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: line, at: 'top' })); + .then(editor => vscode.commands.executeCommand('revealLine', { lineNumber: Math.floor(line), at: 'top' })); })); context.subscriptions.push(vscode.workspace.onDidSaveTextDocument(document => { @@ -246,8 +262,9 @@ class MDDocumentContentProvider implements vscode.TextDocumentContentProvider { return vscode.workspace.openTextDocument(sourceUri).then(document => { const scrollBeyondLastLine = vscode.workspace.getConfiguration('editor')['scrollBeyondLastLine']; const wordWrap = vscode.workspace.getConfiguration('editor')['wordWrap']; - const enablePreviewSync = vscode.workspace.getConfiguration('markdown').get('preview.experimentalSyncronizationEnabled', true); - const previewFrontMatter = vscode.workspace.getConfiguration('markdown')['previewFrontMatter']; + + const markdownConfig = vscode.workspace.getConfiguration('markdown'); + const previewFrontMatter = markdownConfig.get('previewFrontMatter', 'hide'); let initialLine = 0; const editor = vscode.window.activeTextEditor; @@ -273,7 +290,8 @@ class MDDocumentContentProvider implements vscode.TextDocumentContentProvider { window.initialData = { source: "${encodeURIComponent(sourceUri.scheme + '://' + sourceUri.path)}", line: ${initialLine}, - enablePreviewSync: ${!!enablePreviewSync} + enablePreviewSync: ${!!markdownConfig.get('preview.experimentalSyncronizationEnabled', true)}, + enableScrollSync: ${!!markdownConfig.get('preview.synchronizePreviewScrollingToEditor', true)} }; diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index 1480b724903..6a82ca683b3 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -612,6 +612,10 @@ export class DiffEditorWidget extends EventEmitter implements editorBrowser.IDif this.modifiedEditor.revealRangeInCenterIfOutsideViewport(range); } + public revealRangeAtTop(range: editorCommon.IRange): void { + this.modifiedEditor.revealRangeAtTop(range); + } + public _addAction(descriptor: editorCommon.IActionDescriptor): IAddedAction { return this.modifiedEditor._addAction(descriptor); } diff --git a/src/vs/editor/common/commonCodeEditor.ts b/src/vs/editor/common/commonCodeEditor.ts index 250b2ae7c71..a0b1987a033 100644 --- a/src/vs/editor/common/commonCodeEditor.ts +++ b/src/vs/editor/common/commonCodeEditor.ts @@ -460,6 +460,14 @@ export abstract class CommonCodeEditor extends EventEmitter implements editorCom ); } + public revealRangeAtTop(range: editorCommon.IRange): void { + this._revealRange( + range, + editorCommon.VerticalRevealType.Top, + true + ); + } + private _revealRange(range: editorCommon.IRange, verticalType: editorCommon.VerticalRevealType, revealHorizontal: boolean): void { if (!Range.isIRange(range)) { throw new Error('Invalid arguments'); diff --git a/src/vs/editor/common/editorCommon.ts b/src/vs/editor/common/editorCommon.ts index 99ffbe79021..01614b958ce 100644 --- a/src/vs/editor/common/editorCommon.ts +++ b/src/vs/editor/common/editorCommon.ts @@ -3619,6 +3619,11 @@ export interface IEditor { */ revealRangeInCenter(range: IRange): void; + /** + * Scroll vertically or horizontally as necessary and reveal a range at the top of the viewport. + */ + revealRangeAtTop(range: IRange): void; + /** * Scroll vertically or horizontally as necessary and reveal a range centered vertically only if it lies outside the viewport. */ diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index ec1104105b3..3a6894fa563 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -3007,6 +3007,10 @@ declare module monaco.editor { * Scroll vertically or horizontally as necessary and reveal a range centered vertically. */ revealRangeInCenter(range: IRange): void; + /** + * Scroll vertically or horizontally as necessary and reveal a range at the top of the viewport. + */ + revealRangeAtTop(range: IRange): void; /** * Scroll vertically or horizontally as necessary and reveal a range centered vertically only if it lies outside the viewport. */ diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index e51b03adac2..63312799d8c 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -651,7 +651,11 @@ declare module 'vscode' { * If the range is outside the viewport, it will be revealed in the center of the viewport. * Otherwise, it will be revealed with as little scrolling as possible. */ - InCenterIfOutsideViewport = 2 + InCenterIfOutsideViewport = 2, + /** + * The range will always be revealed at the top of the viewport. + */ + AtTop = 3 } /** diff --git a/src/vs/workbench/api/node/extHostTypes.ts b/src/vs/workbench/api/node/extHostTypes.ts index b74137ff36e..0913f814145 100644 --- a/src/vs/workbench/api/node/extHostTypes.ts +++ b/src/vs/workbench/api/node/extHostTypes.ts @@ -920,7 +920,8 @@ export enum TextDocumentSaveReason { export enum TextEditorRevealType { Default = 0, InCenter = 1, - InCenterIfOutsideViewport = 2 + InCenterIfOutsideViewport = 2, + AtTop = 3 } export enum TextEditorSelectionChangeKind { diff --git a/src/vs/workbench/api/node/mainThreadEditorsTracker.ts b/src/vs/workbench/api/node/mainThreadEditorsTracker.ts index 127ff1c9373..137d43b0ee4 100644 --- a/src/vs/workbench/api/node/mainThreadEditorsTracker.ts +++ b/src/vs/workbench/api/node/mainThreadEditorsTracker.ts @@ -56,7 +56,8 @@ export interface IFocusTracker { export enum TextEditorRevealType { Default = 0, InCenter = 1, - InCenterIfOutsideViewport = 2 + InCenterIfOutsideViewport = 2, + AtTop = 3 } export interface IUndoStopOptions { @@ -288,14 +289,22 @@ export class MainThreadTextEditor { console.warn('revealRange on invisible editor'); return; } - if (revealType === TextEditorRevealType.Default) { - this._codeEditor.revealRange(range); - } else if (revealType === TextEditorRevealType.InCenter) { - this._codeEditor.revealRangeInCenter(range); - } else if (revealType === TextEditorRevealType.InCenterIfOutsideViewport) { - this._codeEditor.revealRangeInCenterIfOutsideViewport(range); - } else { - console.warn('Unknown revealType'); + switch (revealType) { + case TextEditorRevealType.Default: + this._codeEditor.revealRange(range); + break; + case TextEditorRevealType.InCenter: + this._codeEditor.revealRangeInCenter(range); + break;; + case TextEditorRevealType.InCenterIfOutsideViewport: + this._codeEditor.revealRangeInCenterIfOutsideViewport(range); + break; + case TextEditorRevealType.AtTop: + this._codeEditor.revealRangeAtTop(range); + break; + default: + console.warn('Unknown revealType'); + break; } }