-
Notifications
You must be signed in to change notification settings - Fork 38.9k
Add source map for every block-level element in the Markdown preview #133376
Description
Issue Type: Bug
Does this issue occur when all extensions are disabled?: Yes
- VS Code Version:
1.61.0-insider (system setup)(a0af581) - OS Version:
Windows_NT x64 10.0.19043
I searched the Issues by https://github.com/microsoft/vscode/issues?q=is%3Aissue+label%3Amarkdown+scroll, found some cases with similar behavior, but couldn't recognize one as the same. So, I open this issue in case a root cause of bugs has't been revealed.
Steps to Reproduce
-
Create a Markdown editor, and fill it with one of the samples below.
-
Adjust the window size, so that no more than 30 lines are visible in the editor.
-
"Open Preview to the Side".
-
Scroll the preview.
Actual behavior:
The editor is scrolled strangely, or jumps to an unexpected position, or whatever.
Expected behavior:
The editor and the preview should be synchronized, all or most of the time.
Problem
The Markdown preview only sets source map for a few kinds of elements, which can lead to severely inaccurate scrolling:
vscode/extensions/markdown-language-features/src/markdownEngine.ts
Lines 114 to 116 in a0af581
| for (const renderName of ['paragraph_open', 'heading_open', 'image', 'code_block', 'fence', 'blockquote_open', 'list_item_open']) { | |
| this.addLineNumberRenderer(md, renderName); | |
| } |
It can be observed in many conditions, such as:
-
Scrolling the preview in order to find something in the editor.
-
Editing a long document when the preview is open.
Solution
Add source map for every element where possible.
Here is a solution. I've tested it by adding a Markdown extension.
First, internally create a markdown-it plugin in the markdownEngine.ts:
import type { PluginSimple } from "markdown-it";
/**
* Adds begin line index to the output via the "data-line" data attribute.
*/
const pluginSourceMap: PluginSimple = (md): void => {
// Set the attribute on every possible token.
md.core.ruler.push("data_attribute_source_map", (state): any => {
for (const token of state.tokens) {
if (token.map && token.type !== "inline") {
token.attrSet("data-line", String(token.map[0]));
token.attrJoin("class", "code-line");
}
}
});
// The "html_block" renderer doesn't respect `attrs`. We need to insert a marker.
const originalHtmlBlockRenderer = md.renderer.rules["html_block"];
if (originalHtmlBlockRenderer) {
md.renderer.rules["html_block"] = (tokens, idx, options, env, self) => {
return (
`<div ${self.renderAttrs(tokens[idx])} ></div>\n` +
originalHtmlBlockRenderer(tokens, idx, options, env, self)
);
};
}
};Then, load it just before the return:
md.use(pluginSourceMap);Known limitations:
Some markdown-it plugins do not respect attrs, thus, their output won't get source map.
Sample 1
From:
Repeat this piece 10 times:
<section>
a<br>
a
a
a
a
a
a
a
</section>Sample 2
From:
Begin the document with:
#Then, add 99 blank lines.
Next, add:
|a|b|
| --- | --- |Finally, repeat this line 99 times:
|c|d|