Align markdown preview slugifier with markdown LS's slugifier

For #280520
This commit is contained in:
Matt Bierner
2025-12-10 11:44:05 -08:00
parent 5eafa95b6d
commit b9c18c3652
2 changed files with 90 additions and 44 deletions

View File

@@ -9,7 +9,7 @@ import * as vscode from 'vscode';
import { ILogger } from './logging'; import { ILogger } from './logging';
import { MarkdownContributionProvider } from './markdownExtensions'; import { MarkdownContributionProvider } from './markdownExtensions';
import { MarkdownPreviewConfiguration } from './preview/previewConfig'; import { MarkdownPreviewConfiguration } from './preview/previewConfig';
import { Slugifier } from './slugify'; import { ISlugifier, SlugBuilder } from './slugify';
import { ITextDocument } from './types/textDocument'; import { ITextDocument } from './types/textDocument';
import { WebviewResourceProvider } from './util/resources'; import { WebviewResourceProvider } from './util/resources';
import { isOfScheme, Schemes } from './util/schemes'; import { isOfScheme, Schemes } from './util/schemes';
@@ -85,13 +85,14 @@ export interface RenderOutput {
} }
interface RenderEnv { interface RenderEnv {
containingImages: Set<string>; readonly containingImages: Set<string>;
currentDocument: vscode.Uri | undefined; readonly currentDocument: vscode.Uri | undefined;
resourceProvider: WebviewResourceProvider | undefined; readonly resourceProvider: WebviewResourceProvider | undefined;
readonly slugifier: SlugBuilder;
} }
export interface IMdParser { export interface IMdParser {
readonly slugifier: Slugifier; readonly slugifier: ISlugifier;
tokenize(document: ITextDocument): Promise<Token[]>; tokenize(document: ITextDocument): Promise<Token[]>;
} }
@@ -100,14 +101,13 @@ export class MarkdownItEngine implements IMdParser {
private _md?: Promise<MarkdownIt>; private _md?: Promise<MarkdownIt>;
private _slugCount = new Map<string, number>();
private readonly _tokenCache = new TokenCache(); private readonly _tokenCache = new TokenCache();
public readonly slugifier: Slugifier; public readonly slugifier: ISlugifier;
public constructor( public constructor(
private readonly _contributionProvider: MarkdownContributionProvider, private readonly _contributionProvider: MarkdownContributionProvider,
slugifier: Slugifier, slugifier: ISlugifier,
private readonly _logger: ILogger, private readonly _logger: ILogger,
) { ) {
this.slugifier = slugifier; this.slugifier = slugifier;
@@ -183,7 +183,6 @@ export class MarkdownItEngine implements IMdParser {
): Token[] { ): Token[] {
const cached = this._tokenCache.tryGetCached(document, config); const cached = this._tokenCache.tryGetCached(document, config);
if (cached) { if (cached) {
this._resetSlugCount();
return cached; return cached;
} }
@@ -194,13 +193,13 @@ export class MarkdownItEngine implements IMdParser {
} }
private _tokenizeString(text: string, engine: MarkdownIt) { private _tokenizeString(text: string, engine: MarkdownIt) {
this._resetSlugCount(); const env: RenderEnv = {
currentDocument: undefined,
return engine.parse(text, {}); containingImages: new Set<string>(),
} slugifier: this.slugifier.createBuilder(),
resourceProvider: undefined,
private _resetSlugCount(): void { };
this._slugCount = new Map<string, number>(); return engine.parse(text, env);
} }
public async render(input: ITextDocument | string, resourceProvider?: WebviewResourceProvider): Promise<RenderOutput> { public async render(input: ITextDocument | string, resourceProvider?: WebviewResourceProvider): Promise<RenderOutput> {
@@ -215,6 +214,7 @@ export class MarkdownItEngine implements IMdParser {
containingImages: new Set<string>(), containingImages: new Set<string>(),
currentDocument: typeof input === 'string' ? undefined : input.uri, currentDocument: typeof input === 'string' ? undefined : input.uri,
resourceProvider, resourceProvider,
slugifier: this.slugifier.createBuilder(),
}; };
const html = engine.renderer.render(tokens, { const html = engine.renderer.render(tokens, {
@@ -313,18 +313,9 @@ export class MarkdownItEngine implements IMdParser {
private _addNamedHeaders(md: MarkdownIt): void { private _addNamedHeaders(md: MarkdownIt): void {
const original = md.renderer.rules.heading_open; const original = md.renderer.rules.heading_open;
md.renderer.rules.heading_open = (tokens: Token[], idx: number, options, env, self) => { md.renderer.rules.heading_open = (tokens: Token[], idx: number, options, env: unknown, self) => {
const title = this._tokenToPlainText(tokens[idx + 1]); const title = this._tokenToPlainText(tokens[idx + 1]);
let slug = this.slugifier.fromHeading(title); const slug = (env as RenderEnv).slugifier ? (env as RenderEnv).slugifier.add(title) : this.slugifier.fromHeading(title);
if (this._slugCount.has(slug.value)) {
const count = this._slugCount.get(slug.value)!;
this._slugCount.set(slug.value, count + 1);
slug = this.slugifier.fromHeading(slug.value + '-' + (count + 1));
} else {
this._slugCount.set(slug.value, 0);
}
tokens[idx].attrSet('id', slug.value); tokens[idx].attrSet('id', slug.value);
if (original) { if (original) {

File diff suppressed because one or more lines are too long