@@ -144,13 +144,18 @@ function renderResults(entries, query) {
144144 return ;
145145 }
146146
147+ // Detect overloaded IDL entries that would produce identical citations.
148+ // Build a map of citation key -> list of entries to find duplicates.
149+ const overloadedURIs = detectOverloadedEntries ( entries , term ) ;
150+
147151 let html = '' ;
148152 for ( const entry of entries ) {
149153 const specInfo = metadata . specs [ entry . status ] [ entry . spec ] ;
150154 const link = new URL ( entry . uri , specInfo . url ) . href ;
151155 const title = escapeHTML ( specInfo . title ) ;
156+ const isOverloaded = overloadedURIs . has ( entry . uri ) ;
152157 const cite = metadata . types . idl . has ( entry . type )
153- ? howToCiteIDL ( term , entry )
158+ ? howToCiteIDL ( term , entry , isOverloaded )
154159 : metadata . types . markup . has ( entry . type )
155160 ? howToCiteMarkup ( term , entry )
156161 : metadata . types . css . has ( entry . type ) ||
@@ -169,24 +174,104 @@ function renderResults(entries, query) {
169174 output . innerHTML = html ;
170175}
171176
172- function howToCiteIDL ( term , entry ) {
177+ /**
178+ * Detects IDL entries that would produce identical citations (overloaded
179+ * methods/constructors). Returns a Set of URIs that need disambiguation.
180+ */
181+ function detectOverloadedEntries ( entries , term ) {
182+ const citationGroups = new Map ( ) ;
183+ for ( const entry of entries ) {
184+ if ( ! metadata . types . idl . has ( entry . type ) ) continue ;
185+ const forKey = ( entry . for || [ ] ) . join ( ',' ) ;
186+ const key = `${ entry . type } |${ forKey } |${ term } ` ;
187+ const group = citationGroups . get ( key ) ?? [ ] ;
188+ if ( ! group . length ) citationGroups . set ( key , group ) ;
189+ group . push ( entry ) ;
190+ }
191+ const overloadedURIs = new Set ( ) ;
192+ for ( const group of citationGroups . values ( ) ) {
193+ if ( group . length > 1 ) {
194+ group . forEach ( entry => overloadedURIs . add ( entry . uri ) ) ;
195+ }
196+ }
197+ return overloadedURIs ;
198+ }
199+
200+ function howToCiteIDL ( term , entry , isOverloaded = false ) {
173201 const { type, for : forList } = entry ;
174202 if ( forList ) {
175203 return forList
176204 . map ( f => {
177205 const termPart = type === 'enum-value' ? `"${ term } "` : term ;
178- return `{{${ f } /${ term ? termPart : '""' } }}` ;
206+ let cite = `{{${ f } /${ term ? termPart : '""' } }}` ;
207+ if ( isOverloaded ) {
208+ const hint = extractOverloadHint ( entry . uri , f , term ) ;
209+ if ( hint ) {
210+ cite += ` <small>(${ escapeHTML ( hint ) } )</small>` ;
211+ }
212+ }
213+ return cite ;
179214 } )
180215 . join ( '<br>' ) ;
181216 }
217+ let cite ;
182218 switch ( type ) {
183219 case 'exception' :
184220 if ( ! exceptionExceptions . has ( term ) ) {
185- return `{{"${ term } "}}` ;
221+ cite = `{{"${ term } "}}` ;
222+ break ;
186223 }
187224 default :
188- return `{{${ term } }}` ;
225+ cite = `{{${ term } }}` ;
226+ }
227+ if ( isOverloaded ) {
228+ const hint = extractOverloadHint ( entry . uri , null , term ) ;
229+ if ( hint ) {
230+ cite += ` <small>(${ escapeHTML ( hint ) } )</small>` ;
231+ }
232+ }
233+ return cite ;
234+ }
235+
236+ /**
237+ * Extracts a human-readable overload hint from a URI fragment.
238+ *
239+ * URI fragments for WebIDL definitions follow the pattern:
240+ * #dom-<interface>-<method>-<param1>-<param2>-...
241+ *
242+ * For example:
243+ * #dom-window-postmessage-message-targetorigin-transfer
244+ * #dom-window-postmessage-message-options
245+ *
246+ * This function strips the known prefix (interface + method) and returns
247+ * the remaining parts as a parameter list, e.g. "message, targetorigin, transfer".
248+ */
249+ function extractOverloadHint ( uri , forContext , term ) {
250+ if ( ! uri ) return '' ;
251+ const hash = uri . includes ( '#' ) ? uri . split ( '#' ) [ 1 ] : uri ;
252+ if ( ! hash ) return '' ;
253+
254+ const parts = hash . toLowerCase ( ) . split ( '-' ) ;
255+
256+ // Build the prefix to strip: typically "dom", interface, method
257+ const prefixParts = [ 'dom' ] ;
258+ if ( forContext ) {
259+ prefixParts . push ( ...forContext . toLowerCase ( ) . split ( '-' ) ) ;
260+ }
261+ const cleanTerm = term . replace ( / \( .* \) $ / , '' ) . toLowerCase ( ) ;
262+ if ( cleanTerm ) {
263+ prefixParts . push ( ...cleanTerm . split ( '-' ) ) ;
189264 }
265+
266+ const matches =
267+ prefixParts . length <= parts . length &&
268+ prefixParts . every ( ( p , i ) => parts [ i ] === p ) ;
269+
270+ if ( matches && prefixParts . length < parts . length ) {
271+ return parts . slice ( prefixParts . length ) . join ( ', ' ) ;
272+ }
273+
274+ return '' ;
190275}
191276
192277function howToCiteMarkup ( term , entry ) {
0 commit comments