Change:
The current behaviour of Code's Markdown previewer when clicking a relative link is actually a bit cryptic: it attempts to open the file relative to the user's home path with the system's application of choice. This will commonly lead to errors being dispatched because the file cannot be found. If I have the following Markdown: Clicking the link will make my system attempt to open . Given that even , I strongly believe that they should be supported here. Such links should instead be relative to the markdown file's directory and open the linked file using the Code editor.
+1; opening markdown files by clicking a (relative) link in the preview would be a very powerful feature both for writing developer documentation (split into individual files by topic) for a project, but also for viewing that documentation.
Related: Is this behavior something vscode could change for its markdown preview?

Discussion:
} const entry = { line: lineNumber, element: element }; if (lineNumber === targetLine) { <del> return { before: entry, next: null }; </del> <ins> return { previous: entry, next: null }; </ins> } else if (lineNumber > targetLine) { return { previous, next: entry }; }

position: relative; } <del> .code-active-line:before { </del> <ins> .code-active-line:before, .code-line:hover:before { </ins> content: ""; display: block; position: absolute; top: 0; left: -12px; height: 100%; <ins> } .code-active-line:before { </ins> border-left: 3px solid rgba(0, 122, 204, 0.5); } .code-line:hover:before { <del> content: ""; display: block; position: absolute; top: 0; left: -12px; height: 100%; </del> border-left: 3px solid #4080D0; }

import * as vscode from 'vscode'; import * as path from 'path'; import TelemetryReporter from 'vscode-extension-telemetry'; <ins> import { MarkdownEngine } from './markdownEngine'; </ins> import DocumentLinkProvider from './documentLinkProvider'; interface IPackageInfo {

var telemetryReporter: TelemetryReporter | null; <del> interface IToken { type: string; map: [number, number]; } interface MarkdownIt { render(text: string): string; parse(text: string): IToken[]; renderer: { rules: any }; } class MarkdownEngine { private _markdownIt: MarkdownIt | undefined; private get markdownIt(): MarkdownIt { if (!this._markdownIt) { const hljs = require('highlight.js'); const mdnh = require('markdown-it-named-headers'); const md = require('markdown-it')({ html: true, highlight: (str: string, lang: string) => { if (lang && hljs.getLanguage(lang)) { try { return `<pre class="hljs"><code><div>${hljs.highlight(lang, str, true).value}</div></code></pre>`; } catch (error) { } } return `<pre class="hljs"><code><div>${md.utils.escapeHtml(str)}</div></code></pre>`; } }).use(mdnh, {}); const createLineNumberRenderer = (ruleName: string) => { const original = md.renderer.rules[ruleName]; return (tokens: any, idx: number, options: any, env: any, self: any) => { const token = tokens[idx]; if (token.level === 0 && token.map && token.map.length) { token.attrSet('data-line', token.map[0]); token.attrJoin('class', 'code-line'); } if (original) { return original(tokens, idx, options, env, self); } else { return self.renderToken(tokens, idx, options, env, self); } }; }; md.renderer.rules.paragraph_open = createLineNumberRenderer('paragraph_open'); md.renderer.rules.heading_open = createLineNumberRenderer('heading_open'); md.renderer.rules.image = createLineNumberRenderer('image'); md.renderer.rules.code_block = createLineNumberRenderer('code_block'); this._markdownIt = md as MarkdownIt; } return this._markdownIt; } get rules(): any { return this.markdownIt.renderer.rules; } render(text: string): string { return this.markdownIt.render(text); } parse(text: string): IToken[] { return this.markdownIt.parse(text); } } </del> const FrontMatterRegex = /^---s*(.|s)*?---s*/; export function activate(context: vscode.ExtensionContext) {

private _onDidChange = new vscode.EventEmitter<vscode.Uri>(); private _waiting: boolean; <del> constructor(private engine: MarkdownEngine, private context: vscode.ExtensionContext) { </del> <ins> constructor( private engine: MarkdownEngine, private context: vscode.ExtensionContext ) { </ins> this._waiting = false; }

const previewFrontMatter = vscode.workspace.getConfiguration('markdown')['previewFrontMatter']; const text = document.getText(); const contents = previewFrontMatter === 'hide' ? text.replace(FrontMatterRegex, '') : text; <del> const body = this.engine.render(contents); </del> <ins> const body = this.engine.render(sourceUri, contents); </ins> return `<!DOCTYPE html> <html>

const tokens = this.engine.parse(text); const headings = tokens.filter(token => token.type === 'heading_open'); <del> const symbols = headings.map(heading => { </del> <ins> return headings.map(heading => { </ins> const lineNumber = heading.map[0]; const line = document.lineAt(lineNumber + offset); const location = new vscode.Location(document.uri, line.range);

return new vscode.SymbolInformation(text, vscode.SymbolKind.Module, '', location); }); <del> return symbols; </del> } }  No newline at end of file

<ins> /*--------------------------------------------------------------------------------------------- *  Copyright (c) Microsoft Corporation. All rights reserved. *  Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ 'use strict'; import * as vscode from 'vscode'; import * as path from 'path'; export interface IToken { type: string; map: [number, number]; } interface MarkdownIt { render(text: string): string; parse(text: string): IToken[]; utils: any; } export class MarkdownEngine { private md: MarkdownIt; private currentDocument: vscode.Uri; private get engine(): MarkdownIt { if (!this.md) { const hljs = require('highlight.js'); const mdnh = require('markdown-it-named-headers'); this.md = require('markdown-it')({ html: true, highlight: (str: string, lang: string) => { if (lang && hljs.getLanguage(lang)) { try { return `<pre class="hljs"><code><div>${hljs.highlight(lang, str, true).value}</div></code></pre>`; } catch (error) { } } return `<pre class="hljs"><code><div>${this.engine.utils.escapeHtml(str)}</div></code></pre>`; } }).use(mdnh, {}); this.addLineNumberRenderer(this.md, 'paragraph_open'); this.addLineNumberRenderer(this.md, 'heading_open'); this.addLineNumberRenderer(this.md, 'image'); this.addLineNumberRenderer(this.md, 'code_block'); this.addLinkNormalizer(this.md); this.addLinkValidator(this.md); } return this.md; } public render(document: vscode.Uri, text: string): string { this.currentDocument = document; return this.engine.render(text); } public parse(text: string): IToken[] { return this.engine.parse(text); } private addLineNumberRenderer(md: any, ruleName: string): void { const original = md.renderer.rules[ruleName]; md.renderer.rules[ruleName] = (tokens: any, idx: number, options: any, env: any, self: any) => { const token = tokens[idx]; if (token.level === 0 && token.map && token.map.length) { token.attrSet('data-line', token.map[0]); token.attrJoin('class', 'code-line'); } if (original) { return original(tokens, idx, options, env, self); } else { return self.renderToken(tokens, idx, options, env, self); } }; } private addLinkNormalizer(md: any): void { const normalizeLink = md.normalizeLink; md.normalizeLink = (link: string) => { try { let uri = vscode.Uri.parse(link); if (!uri.scheme) { // Assume it must be a file if (uri.path[0] === '/') { uri = vscode.Uri.file(path.join(vscode.workspace.rootPath, uri.path)); } else { uri = vscode.Uri.file(path.join(path.dirname(this.currentDocument.path), uri.path)); } return normalizeLink(uri.toString(true)); } } catch (e) { // noop } return normalizeLink(link); }; } private addLinkValidator(md: any): void { const validateLink = md.validateLink; md.validateLink = (link: string) => { if (validateLink(link)) { return true; } // support file:// links return link.indexOf('file:') === 0; }; } } </ins>  No newline at end of file