Skip to content

Commit 15c2d1f

Browse files
authored
Move markdown preview content provider to own file (microsoft#19592)
1 parent 9dc95af commit 15c2d1f

3 files changed

Lines changed: 149 additions & 133 deletions

File tree

extensions/markdown/src/documentLinkProvider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export default class MarkdownDocumentLinkProvider implements vscode.DocumentLink
2929
while ((match = this._linkPattern.exec(text))) {
3030
const pre = match[1];
3131
const link = match[2];
32-
const offset = match.index + pre.length;
32+
const offset = (match.index || 0) + pre.length;
3333
const linkStart = document.positionAt(offset);
3434
const linkEnd = document.positionAt(offset + link.length);
3535
try {

extensions/markdown/src/extension.ts

Lines changed: 2 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import TelemetryReporter from 'vscode-extension-telemetry';
1111
import { MarkdownEngine } from './markdownEngine';
1212
import DocumentLinkProvider from './documentLinkProvider';
1313
import MDDocumentSymbolProvider from './documentSymbolProvider';
14+
import { MDDocumentContentProvider, getMarkdownUri, isMarkdownFile } from './previewContentProvider';
15+
1416

1517
interface IPackageInfo {
1618
name: string;
@@ -102,14 +104,7 @@ export function activate(context: vscode.ExtensionContext) {
102104
}));
103105
}
104106

105-
function isMarkdownFile(document: vscode.TextDocument) {
106-
return document.languageId === 'markdown'
107-
&& document.uri.scheme !== 'markdown'; // prevent processing of own documents
108-
}
109107

110-
function getMarkdownUri(uri: vscode.Uri) {
111-
return uri.with({ scheme: 'markdown', path: uri.path + '.rendered', query: uri.toString() });
112-
}
113108

114109
function showPreview(uri?: vscode.Uri, sideBySide: boolean = false) {
115110

@@ -195,129 +190,4 @@ function getPackageInfo(): IPackageInfo | null {
195190
return null;
196191
}
197192

198-
class MDDocumentContentProvider implements vscode.TextDocumentContentProvider {
199-
private _onDidChange = new vscode.EventEmitter<vscode.Uri>();
200-
private _waiting: boolean;
201-
202-
constructor(
203-
private engine: MarkdownEngine,
204-
private context: vscode.ExtensionContext
205-
) {
206-
this._waiting = false;
207-
}
208-
209-
private getMediaPath(mediaFile: string): string {
210-
return this.context.asAbsolutePath(path.join('media', mediaFile));
211-
}
212193

213-
private isAbsolute(p: string): boolean {
214-
return path.normalize(p + '/') === path.normalize(path.resolve(p) + '/');
215-
}
216-
217-
private fixHref(resource: vscode.Uri, href: string): string {
218-
if (href) {
219-
// Use href if it is already an URL
220-
if (vscode.Uri.parse(href).scheme) {
221-
return href;
222-
}
223-
224-
// Use href as file URI if it is absolute
225-
if (this.isAbsolute(href)) {
226-
return vscode.Uri.file(href).toString();
227-
}
228-
229-
// use a workspace relative path if there is a workspace
230-
let rootPath = vscode.workspace.rootPath;
231-
if (rootPath) {
232-
return vscode.Uri.file(path.join(rootPath, href)).toString();
233-
}
234-
235-
// otherwise look relative to the markdown file
236-
return vscode.Uri.file(path.join(path.dirname(resource.fsPath), href)).toString();
237-
}
238-
return href;
239-
}
240-
241-
private computeCustomStyleSheetIncludes(uri: vscode.Uri): string {
242-
const styles = vscode.workspace.getConfiguration('markdown')['styles'];
243-
if (styles && Array.isArray(styles) && styles.length > 0) {
244-
return styles.map((style) => {
245-
return `<link rel="stylesheet" href="${this.fixHref(uri, style)}" type="text/css" media="screen">`;
246-
}).join('\n');
247-
}
248-
return '';
249-
}
250-
251-
private getSettingsOverrideStyles(): string {
252-
const previewSettings = vscode.workspace.getConfiguration('markdown')['preview'];
253-
if (!previewSettings) {
254-
return '';
255-
}
256-
const {fontFamily, fontSize, lineHeight} = previewSettings;
257-
return `<style>
258-
body {
259-
${fontFamily ? `font-family: ${fontFamily};` : ''}
260-
${+fontSize > 0 ? `font-size: ${fontSize}px;` : ''}
261-
${+lineHeight > 0 ? `line-height: ${lineHeight};` : ''}
262-
}
263-
</style>`;
264-
}
265-
266-
public provideTextDocumentContent(uri: vscode.Uri): Thenable<string> {
267-
const sourceUri = vscode.Uri.parse(uri.query);
268-
return vscode.workspace.openTextDocument(sourceUri).then(document => {
269-
const scrollBeyondLastLine = vscode.workspace.getConfiguration('editor')['scrollBeyondLastLine'];
270-
const wordWrap = vscode.workspace.getConfiguration('editor')['wordWrap'];
271-
272-
const markdownConfig = vscode.workspace.getConfiguration('markdown');
273-
const previewFrontMatter = markdownConfig.get('previewFrontMatter', 'hide');
274-
275-
let initialLine = 0;
276-
const editor = vscode.window.activeTextEditor;
277-
if (editor && editor.document.uri.path === sourceUri.path) {
278-
initialLine = editor.selection.active.line;
279-
}
280-
281-
const body = this.engine.render(sourceUri, previewFrontMatter === 'hide', document.getText());
282-
283-
return `<!DOCTYPE html>
284-
<html>
285-
<head>
286-
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
287-
<link rel="stylesheet" type="text/css" href="${this.getMediaPath('markdown.css')}">
288-
<link rel="stylesheet" type="text/css" href="${this.getMediaPath('tomorrow.css')}">
289-
${this.getSettingsOverrideStyles()}
290-
${this.computeCustomStyleSheetIncludes(uri)}
291-
<base href="${document.uri.toString(true)}">
292-
</head>
293-
<body class="${scrollBeyondLastLine ? 'scrollBeyondLastLine' : ''} ${wordWrap ? 'wordWrap' : ''} ${!!markdownConfig.get('preview.markEditorSelection') ? 'showEditorSelection' : ''}">
294-
${body}
295-
<script>
296-
window.initialData = {
297-
source: "${encodeURIComponent(sourceUri.scheme + '://' + sourceUri.path)}",
298-
line: ${initialLine},
299-
scrollPreviewWithEditorSelection: ${!!markdownConfig.get('preview.scrollPreviewWithEditorSelection', true)},
300-
scrollEditorWithPreview: ${!!markdownConfig.get('preview.scrollEditorWithPreview', true)},
301-
doubleClickToSwitchToEditor: ${!!markdownConfig.get('preview.doubleClickToSwitchToEditor', true)},
302-
};
303-
</script>
304-
<script src="${this.getMediaPath('main.js')}"></script>
305-
</body>
306-
</html>`;
307-
});
308-
}
309-
310-
get onDidChange(): vscode.Event<vscode.Uri> {
311-
return this._onDidChange.event;
312-
}
313-
314-
public update(uri: vscode.Uri) {
315-
if (!this._waiting) {
316-
this._waiting = true;
317-
setTimeout(() => {
318-
this._waiting = false;
319-
this._onDidChange.fire(uri);
320-
}, 300);
321-
}
322-
}
323-
}
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
'use strict';
7+
8+
import * as vscode from 'vscode';
9+
import * as path from 'path';
10+
import { MarkdownEngine } from './markdownEngine';
11+
12+
export function isMarkdownFile(document: vscode.TextDocument) {
13+
return document.languageId === 'markdown'
14+
&& document.uri.scheme !== 'markdown'; // prevent processing of own documents
15+
}
16+
17+
export function getMarkdownUri(uri: vscode.Uri) {
18+
return uri.with({ scheme: 'markdown', path: uri.path + '.rendered', query: uri.toString() });
19+
}
20+
21+
export class MDDocumentContentProvider implements vscode.TextDocumentContentProvider {
22+
private _onDidChange = new vscode.EventEmitter<vscode.Uri>();
23+
private _waiting: boolean;
24+
25+
constructor(
26+
private engine: MarkdownEngine,
27+
private context: vscode.ExtensionContext
28+
) {
29+
this._waiting = false;
30+
}
31+
32+
private getMediaPath(mediaFile: string): string {
33+
return this.context.asAbsolutePath(path.join('media', mediaFile));
34+
}
35+
36+
private isAbsolute(p: string): boolean {
37+
return path.normalize(p + '/') === path.normalize(path.resolve(p) + '/');
38+
}
39+
40+
private fixHref(resource: vscode.Uri, href: string): string {
41+
if (href) {
42+
// Use href if it is already an URL
43+
if (vscode.Uri.parse(href).scheme) {
44+
return href;
45+
}
46+
47+
// Use href as file URI if it is absolute
48+
if (this.isAbsolute(href)) {
49+
return vscode.Uri.file(href).toString();
50+
}
51+
52+
// use a workspace relative path if there is a workspace
53+
let rootPath = vscode.workspace.rootPath;
54+
if (rootPath) {
55+
return vscode.Uri.file(path.join(rootPath, href)).toString();
56+
}
57+
58+
// otherwise look relative to the markdown file
59+
return vscode.Uri.file(path.join(path.dirname(resource.fsPath), href)).toString();
60+
}
61+
return href;
62+
}
63+
64+
private computeCustomStyleSheetIncludes(uri: vscode.Uri): string {
65+
const styles = vscode.workspace.getConfiguration('markdown')['styles'];
66+
if (styles && Array.isArray(styles) && styles.length > 0) {
67+
return styles.map((style) => {
68+
return `<link rel="stylesheet" href="${this.fixHref(uri, style)}" type="text/css" media="screen">`;
69+
}).join('\n');
70+
}
71+
return '';
72+
}
73+
74+
private getSettingsOverrideStyles(): string {
75+
const previewSettings = vscode.workspace.getConfiguration('markdown')['preview'];
76+
if (!previewSettings) {
77+
return '';
78+
}
79+
const { fontFamily, fontSize, lineHeight } = previewSettings;
80+
return `<style>
81+
body {
82+
${fontFamily ? `font-family: ${fontFamily};` : ''}
83+
${+fontSize > 0 ? `font-size: ${fontSize}px;` : ''}
84+
${+lineHeight > 0 ? `line-height: ${lineHeight};` : ''}
85+
}
86+
</style>`;
87+
}
88+
89+
public provideTextDocumentContent(uri: vscode.Uri): Thenable<string> {
90+
const sourceUri = vscode.Uri.parse(uri.query);
91+
return vscode.workspace.openTextDocument(sourceUri).then(document => {
92+
const scrollBeyondLastLine = vscode.workspace.getConfiguration('editor')['scrollBeyondLastLine'];
93+
const wordWrap = vscode.workspace.getConfiguration('editor')['wordWrap'];
94+
95+
const markdownConfig = vscode.workspace.getConfiguration('markdown');
96+
const previewFrontMatter = markdownConfig.get('previewFrontMatter', 'hide');
97+
98+
let initialLine = 0;
99+
const editor = vscode.window.activeTextEditor;
100+
if (editor && editor.document.uri.path === sourceUri.path) {
101+
initialLine = editor.selection.active.line;
102+
}
103+
104+
const body = this.engine.render(sourceUri, previewFrontMatter === 'hide', document.getText());
105+
106+
return `<!DOCTYPE html>
107+
<html>
108+
<head>
109+
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
110+
<link rel="stylesheet" type="text/css" href="${this.getMediaPath('markdown.css')}">
111+
<link rel="stylesheet" type="text/css" href="${this.getMediaPath('tomorrow.css')}">
112+
${this.getSettingsOverrideStyles()}
113+
${this.computeCustomStyleSheetIncludes(uri)}
114+
<base href="${document.uri.toString(true)}">
115+
</head>
116+
<body class="${scrollBeyondLastLine ? 'scrollBeyondLastLine' : ''} ${wordWrap ? 'wordWrap' : ''} ${!!markdownConfig.get('preview.markEditorSelection') ? 'showEditorSelection' : ''}">
117+
${body}
118+
<script>
119+
window.initialData = {
120+
source: "${encodeURIComponent(sourceUri.scheme + '://' + sourceUri.path)}",
121+
line: ${initialLine},
122+
scrollPreviewWithEditorSelection: ${!!markdownConfig.get('preview.scrollPreviewWithEditorSelection', true)},
123+
scrollEditorWithPreview: ${!!markdownConfig.get('preview.scrollEditorWithPreview', true)},
124+
doubleClickToSwitchToEditor: ${!!markdownConfig.get('preview.doubleClickToSwitchToEditor', true)},
125+
};
126+
</script>
127+
<script src="${this.getMediaPath('main.js')}"></script>
128+
</body>
129+
</html>`;
130+
});
131+
}
132+
133+
get onDidChange(): vscode.Event<vscode.Uri> {
134+
return this._onDidChange.event;
135+
}
136+
137+
public update(uri: vscode.Uri) {
138+
if (!this._waiting) {
139+
this._waiting = true;
140+
setTimeout(() => {
141+
this._waiting = false;
142+
this._onDidChange.fire(uri);
143+
}, 300);
144+
}
145+
}
146+
}

0 commit comments

Comments
 (0)