-
Notifications
You must be signed in to change notification settings - Fork 36
Add delimiterResolvers hook for custom delimiter handling #60
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Include documentation from 4d5b25c.
FEATURE: Add a hook that allows extensions to run custom delimiter resolution logic before the standard resolution process. Resolvers receive the InlineContext and can inspect and modify its parts array.
FEATURE: Make the parts array accessible so that delimiter resolvers can inspect and modify the collected elements and delimiters.
|
I'm not happy about exporting a weird data structure like In general, this feels like too messy and ad-hoc an API for me to commit to. If you can formulate it in some narrower way, for example by providing some way for unclosed delimiters to be auto-closed (rather than turned into plain text) somehow, that would probably be more attractive. |
FEATURE: Replace delimiterResolvers hook with preResolveDelimiters API that provides controlled delimiter access without exposing internal data structures. Extensions can mark resolved delimiters and add custom elements.
|
Thanks for the feedback! Let me clear up a couple things first. The README was auto-generated with The core issue is overlapping emphasis ranges that can't be a tree. For
Standard resolution picks the Auto-closing unclosed delimiters wouldn't help here either. That only extends unmatched openers to block end, but can't apply multiple styles to the same text region. The overlapping case needs both styles, not just extension. Making this a built-in I've implemented a tighter API that doesn't expose internals: interface PreResolveContext {
readonly delimiters: ReadonlyArray<{
readonly type: DelimiterType
readonly from: number
readonly to: number
readonly side: number // 1=open, 2=close, 3=both
}>
readonly blockEnd: number
markResolved(index: number): void
addElement(element: Element): void
elt(type: string, from: number, to: number, children?: readonly Element[]): Element
slice(from: number, to: number): string
}
interface MarkdownConfig {
preResolveDelimiters?: readonly ((ctx: PreResolveContext) => void)[]
}Resolvers get an immutable delimiter list and explicit methods to mark resolved or add elements. No raw arrays, no class exposure. Zero overhead when unused. Would this narrower approach work for you? |
|
Hey @marijnh, just wanted to gently bump this when you have a moment. I know you're busy, no pressure at all. |
|
I have been looking back at this periodically trying to like it or to find an acceptable alternative for you, but I have been successful at neither so far. |
|
Really appreciate that :) |
Summary
This adds a
delimiterResolvershook that lets extensions customize delimiter resolution before the standard CommonMark algorithm runs. It also makesInlineContext.partspublic as it's required for resolvers to function.The Problem
Extensions that need to handle delimiters differently from CommonMark have no clean way to do it. The current
resolveMarkersfunction enforces a strict tree structure: find a closer, look backward for an opener, build a node. Unmatched openers become plain text.For my use case, I needed to:
*hellobecomes emphasis immediately)The only way to achieve this was monkey-patching
resolveMarkersand accessing the@internalpartsarray, which is fragile and breaks with updates.I've built an extension that demonstrates this use case: lezer-markdown-partial-emphasis. It creates "live" emphasis that appears as you type, using the new API.
The Solution
Add a
delimiterResolversarray toMarkdownConfig. Each resolver receives theInlineContextand can inspect or modifycx.partsbefore standard resolution.Resolvers can replace
InlineDelimiterobjects withElementobjects or null them out. The standard algorithm skips already-resolved positions.Making
partspublic is necessary for this API to work. Resolvers need to read delimiter positions, types, and side flags, then modify the array in place.Why This Design
I considered alternatives but they all had issues:
This approach follows the existing
wrappattern: an array of processors that compose without conflicts. It's opt-in with zero overhead when unused.Testing
Added
test/test-delimiter-resolvers.tsdemonstrating the API. The test extension shows how to access and modify delimiters added by the built-in Emphasis parser (clearing asterisks while preserving underscores).README Changes
Regenerated the README to include documentation for the new API. This also picked up docs from an earlier commit (4d5b25c) about block context nodes and close tokens that had been missed.