@@ -18,57 +18,57 @@ function replacer(state) {
1818 let pos ;
1919
2020 const labelStart = state . pos + 1 ;
21- const labelEnd = parseHovercard ( state , state . pos + 1 ) ;
21+ const labelEnd = parseHovercard ( state , labelStart ) ;
2222 if ( labelEnd < 0 ) { return false }
2323
2424 pos = labelEnd + 1 ;
2525 const label = state . src . slice ( labelStart , labelEnd ) ;
2626
27- let normalizedLabel = label ;
28- if ( state . env ?. type === 'api' ) {
29- if ( label . startsWith ( '::' ) ) {
30- // Disambiguate this label name by including the name of the page we're
31- // on.
32- normalizedLabel = `${ state . env . name } ${ label } ` ;
33- }
34- }
35- let simpleLabel = simplifyLabel ( normalizedLabel ) ;
27+ let labelData = parseHovercardLabel ( label , state ) ;
3628
3729 state . pos = labelStart ;
3830 state . posMax = labelEnd ;
3931
4032 let href ;
41- if ( STATIC_HOVERCARD_URLS . has ( label ) ) {
42- href = STATIC_HOVERCARD_URLS . get ( label ) ;
33+ if ( STATIC_HOVERCARD_URLS . has ( labelData . token ) ) {
34+ href = STATIC_HOVERCARD_URLS . get ( labelData . token ) ;
4335 } else {
44- href = inferHrefFromHovercardText ( label , state . env ?. type === 'api' ) ;
36+ href = inferHrefFromHovercardText ( labelData . token , state . env ?. type === 'api' ) ;
4537 }
4638
4739 const token_o = state . push ( "a_open" , "a" , 1 ) ;
4840 const attrs = [
49- [ "data-hovercard" , simpleLabel ] ,
50- [ "data-hovercard-full" , normalizedLabel ] ,
41+ [ "data-hovercard" , labelData . slug ] ,
42+ [ "data-hovercard-full" , labelData . normalized ] ,
43+ [ "data-hovercard-text" , labelData . text ] ,
5144 [ "href" , href ]
5245 ] ;
5346 token_o . attrs = attrs ;
5447
5548 state . linkLevel ++ ;
5649 state . md . inline . tokenize ( state ) ;
50+ if ( label !== labelData . text ) {
51+ // This is a bit hacky; there might be a better approach. If we need to
52+ // alter the link text, we do so by forcing the link text to be tokenized,
53+ // then changing the token's content after the fact.
54+ state . tokens [ state . tokens . length - 1 ] . content = labelData . text ;
55+ }
5756 state . linkLevel -- ;
5857
5958 state . push ( "a_close" , "a" , - 1 ) ;
6059
6160 // Sometimes we need to transform hovercard syntax without any filesystem
6261 // side-effects.
6362 if ( ! state . env ?. skip_hovercard ) {
64- storeHovercard ( simpleLabel , normalizedLabel ) ;
63+ storeHovercard ( labelData . slug , labelData . normalized ) ;
6564 }
6665
6766 state . pos = pos ;
6867 state . posMax = max ;
6968 return true ;
7069}
7170
71+ // Finds the ending position of a hovercard.
7272function parseHovercard ( state , start ) {
7373 const max = state . posMax ;
7474 const oldPos = state . pos ;
@@ -162,3 +162,71 @@ function writeHovercardStoreToDisk () {
162162 let json = JSON . stringify ( obj , null , 2 ) ;
163163 FS . writeFileSync ( 'hovercard_list.json' , json ) ;
164164}
165+
166+ // Given the text between `{` and `}`, returns various formats of the text
167+ // within.
168+ //
169+ // An ordinary hovercard reference looks like `{TextEditor}`, but it's also
170+ // possible to use different link text in a hovercard link: `{TextEditor "the
171+ // text editor" }`. To use this format, you must wrap your link text in a
172+ // single- or double-quoted string and separate it from the token by a space.
173+ //
174+ // This method therefore returns an object with four keys. Consider the
175+ // following example, presumed to be part of a comment block inside
176+ // `src/text-editor.js`:
177+ //
178+ // It's a good idea {::save "to save the document"} before calling
179+ // this method.
180+ //
181+ // This function would return the following keys:
182+ //
183+ // * `token`: The original token (`::save` — which, if not for the presence of
184+ // the explicit link text, would be used as the anchor text)
185+ // * `slug`: The slugified version for internal use (`texteditor__save`)
186+ // * `normalized`: The expanded version of the token used for symbol resolution
187+ // (`TextEditor::save`)
188+ // * `text`: The text to be used for the anchor tag (`to save the document`)
189+ //
190+ function parseHovercardLabel ( label , state ) {
191+ let token = label , normalized , text ;
192+
193+ if ( label . includes ( ' ' ) ) {
194+ let index = label . indexOf ( ' ' ) ;
195+ token = label . substring ( 0 , index ) ;
196+ let rawText = label . substring ( index + 1 ) ;
197+ // Must start with a quote character.
198+ if ( ! ( / [ ' " ] / ) . test ( rawText . charAt ( 0 ) ) ) {
199+ console . error ( label , JSON . stringify ( rawText . charAt ( 0 ) ) , state . env )
200+ throw new Error ( `Illegal hovercard text!` )
201+ }
202+ // Must end with a quote character.
203+ if ( rawText . charAt ( 0 ) !== rawText . charAt ( rawText . length - 1 ) ) {
204+ console . error ( label , state . env )
205+ throw new Error ( `Illegal hovercard text!` )
206+ }
207+ // We expect `rawText` to be a string.
208+ text = JSON . parse ( rawText ) ;
209+ if ( typeof text !== 'string' ) {
210+ console . error ( label , state . env )
211+ throw new Error ( `Illegal hovercard text!` )
212+ }
213+ } else {
214+ text = token ;
215+ }
216+
217+ normalized = token ;
218+
219+ if ( state . env ?. type === 'api' ) {
220+ if ( label . startsWith ( '::' ) || label . startsWith ( '.' ) ) {
221+ // Disambiguate this label name by including the name of the page we're
222+ // on.
223+ normalized = `${ state . env . name } ${ token } ` ;
224+ if ( text === token ) {
225+ text = normalized ;
226+ }
227+ }
228+ }
229+
230+ let result = { token, normalized, text, slug : simplifyLabel ( normalized ) } ;
231+ return result ;
232+ }
0 commit comments