831 字
4 分钟
不经意发现的vscode对于md格式链接识别一个小特性
vscode对于md格式链接识别存在误判原因分析
前言
- 有一阵没发博客,于是水一篇,最近在学习java反序列化的几个cc链路,感觉一时半会发不出来,在编写markdown文件时发现了一个有趣的现象(
并不有趣,我差点以为是我电脑bug了,排查了半天),md中附图片的代码按住ctrl悬停在图片名的时候竟无法点击打开图片,也不展示为链接,经过尝试,初步发现了一个规律,当紧贴着的上个代码块中有空行时,该链接识别会失效,而以预览模式打开图片仍能够正常加载,于是在ai的协助下找到了vscode对于判断内联代码的逻辑。 - 因水平有限,文章主要用于个人学习记录,内容仅供参考,若有错误或补充欢迎通过主页联系方式告知。
发现过程及源码分析
- 如图,当上段代码块存在空行,且到下个代码块中无空行,ctrl悬停是无法点开图像链接的,大致判断是反引号代码块对链接的影响。

- 在github一些讨论贴中,发现一些讨论帖,其中有人提到具有检测是否为代码块内链接的代码文件,我用ai找了一下实际实现链接判断的部分,如下面所示。
- 在vscode的gituhb仓库’https://github.com/microsoft/vscode-markdown-languageservice/blob/main/src/util/noLinkRanges.ts’也展示了该检测内联代码区域的代码,用一个反引号配对正则判断链接是否处于代码内,但这个正则不懂围栏代码块,且配对时穿不过空行。当代码块含空行时,配对被打乱,代码块收尾的 ``` 被当成新的代码块开头,配到下一个代码块的开头反引号,把中间的链接误判为在内联代码里,于是链接被过滤掉。不过,markdown解析时,能正确识别围栏,所以vscode所带预览功能不受影响。以下是noLinkRanges.ts的代码:
import * as lsp from 'vscode-languageserver-protocol';import { Token } from '../parser.js';import { rangeContains } from '../types/range.js';import { ITextDocument } from '../types/textDocument.js';
const inlineCodePattern = /(?<!`)(`+)((?:.+?|.*?(?:(?:\r?\n).+?)*?)(?:\r?\n)?\1)(?!`)/gm;
class InlineRanges {
public static create() { return new InlineRanges(); }
readonly #map: Map</* line number */ number, lsp.Range[]>;
private constructor(data?: ReadonlyMap<number, lsp.Range[]>) { this.#map = new Map(data); }
public get(line: number): lsp.Range[] { return this.#map.get(line) || []; }
public add(range: lsp.Range): void { // Register the range for all lines that it covers for (let line = range.start.line; line <= range.end.line; line++) { let ranges = this.#map.get(line); if (!ranges) { ranges = []; this.#map.set(line, ranges); } ranges.push(range); } }
public concat(newRanges: Iterable<lsp.Range>): InlineRanges { const result = new InlineRanges(this.#map); for (const range of newRanges) { result.add(range); } return result; }}
export class NoLinkRanges { public static compute(tokens: readonly Token[], document: ITextDocument): NoLinkRanges { const multiline = tokens .filter(t => (t.type === 'code_block' || t.type === 'fence' || t.type === 'html_block') && !!t.map) .map(t => ({ type: t.type, range: t.map as [number, number] }));
const inlineRanges = InlineRanges.create(); const text = document.getText(); for (const match of text.matchAll(inlineCodePattern)) { const startOffset = match.index ?? 0; const startPosition = document.positionAt(startOffset); inlineRanges.add(lsp.Range.create(startPosition, document.positionAt(startOffset + match[0].length))); }
return new NoLinkRanges(multiline, inlineRanges); }
private constructor( /** * Block element ranges, such as code blocks. Represented by [line_start, line_end). */ public readonly multiline: ReadonlyArray<{ type: string, range: [number, number] }>,
/** * Inline code spans where links should not be detected */ public readonly inline: InlineRanges, ) { }
contains(position: lsp.Position, excludeType = ''): boolean { return this.multiline.some(({ type, range }) => type !== excludeType && position.line >= range[0] && position.line < range[1]) || !!this.inline.get(position.line)?.some(inlineRange => rangeContains(inlineRange, position)); }
concatInline(inlineRanges: Iterable<lsp.Range>): NoLinkRanges { return new NoLinkRanges(this.multiline, this.inline.concat(inlineRanges)); }} 不经意发现的vscode对于md格式链接识别一个小特性
https://www.dxowo8.top/posts/post-35/35/ 部分信息可能已经过时







