@@ -207,7 +207,7 @@ private static void ProcessInternalLink(LinkInline link, InlineProcessor process
207207 var linkMarkdown = SetLinkData ( link , processor , context , file , url ) ;
208208
209209 ProcessLinkText ( processor , link , linkMarkdown , anchor , url , file ) ;
210- UpdateLinkUrl ( link , url , context , anchor ) ;
210+ UpdateLinkUrl ( link , linkMarkdown , url , context , anchor ) ;
211211 }
212212
213213 private static MarkdownFile ? SetLinkData ( LinkInline link , InlineProcessor processor , ParserContext context ,
@@ -292,61 +292,72 @@ private static void ValidateAnchor(InlineProcessor processor, MarkdownFile markd
292292 processor . EmitError ( link , $ "`{ anchor } ` does not exist in { markdown . RelativePath } .") ;
293293 }
294294
295- private static void UpdateLinkUrl ( LinkInline link , string url , ParserContext context , string ? anchor )
295+ private static void UpdateLinkUrl ( LinkInline link , MarkdownFile ? linkMarkdown , string url , ParserContext context , string ? anchor )
296296 {
297- // TODO revisit when we refactor our documentation set graph
298- // This method grew too complex, we need to revisit our documentation set graph generation so we can ask these questions
299- // on `DocumentationFile` that are mostly precomputed
300- var urlPathPrefix = context . Build . UrlPathPrefix ?? string . Empty ;
301-
302- if ( ! url . StartsWith ( '/' ) && ! string . IsNullOrEmpty ( url ) )
297+ var newUrl = url ;
298+ if ( linkMarkdown is not null )
303299 {
304- // eat overall path prefix since its gets appended later
305- var subPrefix = context . CurrentUrlPath . Length >= urlPathPrefix . Length
306- ? context . CurrentUrlPath [ urlPathPrefix . Length ..]
307- : urlPathPrefix ;
308-
309- // if we are trying to resolve a relative url from a _snippet folder ensure we eat the _snippet folder
310- // as it's not part of url by chopping of the extra parent navigation
311- if ( url . StartsWith ( "../" ) && context . DocumentationFileLookup ( context . MarkdownSourcePath ) is SnippetFile snippetFile )
312- url = url . Substring ( 3 ) ;
313-
314- // TODO check through context.DocumentationFileLookup if file is index vs "index.md" check
315- var markdownPath = context . MarkdownSourcePath ;
316- // if the current path is an index e.g /reference/cloud-k8s/
317- // './' current path lookups should be relative to sub-path.
318- // If it's not e.g /reference/cloud-k8s/api-docs/ these links should resolve on folder up.
319- var lastIndexPath = subPrefix . LastIndexOf ( '/' ) ;
320- if ( lastIndexPath >= 0 && markdownPath . Name != "index.md" )
321- subPrefix = subPrefix [ ..lastIndexPath ] ;
322- var combined = '/' + Path . Combine ( subPrefix , url ) . TrimStart ( '/' ) ;
323- url = Path . GetFullPath ( combined ) ;
300+ // if url is null it's an anchor link
301+ if ( ! string . IsNullOrEmpty ( url ) )
302+ newUrl = linkMarkdown . Url ;
324303 }
325-
326- if ( url . EndsWith ( ".md" ) )
304+ else
327305 {
328- url = url . EndsWith ( $ "{ Path . DirectorySeparatorChar } index.md")
329- ? url . Remove ( url . LastIndexOf ( "index.md" , StringComparison . Ordinal ) , "index.md" . Length )
330- : url . Remove ( url . LastIndexOf ( ".md" , StringComparison . Ordinal ) , ".md" . Length ) ;
306+ // TODO revisit when we refactor our documentation set graph
307+ // This method grew too complex, we need to revisit our documentation set graph generation so we can ask these questions
308+ // on `DocumentationFile` that are mostly precomputed
309+ var urlPathPrefix = context . Build . UrlPathPrefix ?? string . Empty ;
310+
311+ if ( ! newUrl . StartsWith ( '/' ) && ! string . IsNullOrEmpty ( newUrl ) )
312+ {
313+ // eat overall path prefix since its gets appended later
314+ var subPrefix = context . CurrentUrlPath . Length >= urlPathPrefix . Length
315+ ? context . CurrentUrlPath [ urlPathPrefix . Length ..]
316+ : urlPathPrefix ;
317+
318+ // if we are trying to resolve a relative url from a _snippet folder ensure we eat the _snippet folder
319+ // as it's not part of url by chopping of the extra parent navigation
320+ if ( newUrl . StartsWith ( "../" ) && context . DocumentationFileLookup ( context . MarkdownSourcePath ) is SnippetFile snippetFile )
321+ newUrl = url . Substring ( 3 ) ;
322+
323+ // TODO check through context.DocumentationFileLookup if file is index vs "index.md" check
324+ var markdownPath = context . MarkdownSourcePath ;
325+ // if the current path is an index e.g /reference/cloud-k8s/
326+ // './' current path lookups should be relative to sub-path.
327+ // If it's not e.g /reference/cloud-k8s/api-docs/ these links should resolve on folder up.
328+ var lastIndexPath = subPrefix . LastIndexOf ( '/' ) ;
329+ if ( lastIndexPath >= 0 && markdownPath . Name != "index.md" )
330+ subPrefix = subPrefix [ ..lastIndexPath ] ;
331+ var combined = '/' + Path . Combine ( subPrefix , newUrl ) . TrimStart ( '/' ) ;
332+ newUrl = Path . GetFullPath ( combined ) ;
333+ }
334+
335+ // When running on Windows, path traversal results must be normalized prior to being used in a URL
336+ // Path.GetFullPath() will result in the drive letter being appended to the path, which needs to be pruned back.
337+ if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) )
338+ {
339+ newUrl = newUrl . Replace ( '\\ ' , '/' ) ;
340+ if ( newUrl . Length > 2 && newUrl [ 1 ] == ':' )
341+ newUrl = newUrl [ 2 ..] ;
342+ }
343+
344+ if ( ! string . IsNullOrWhiteSpace ( newUrl ) && ! string . IsNullOrWhiteSpace ( urlPathPrefix ) )
345+ newUrl = $ "{ urlPathPrefix . TrimEnd ( '/' ) } { newUrl } ";
346+
331347 }
332348
333- // When running on Windows, path traversal results must be normalized prior to being used in a URL
334- // Path.GetFullPath() will result in the drive letter being appended to the path, which needs to be pruned back.
335- if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) )
349+ if ( newUrl . EndsWith ( ".md" ) )
336350 {
337- url = url . Replace ( ' \\ ' , '/' ) ;
338- if ( url . Length > 2 && url [ 1 ] == ':' )
339- url = url [ 2 .. ] ;
351+ newUrl = newUrl . EndsWith ( $ " { Path . DirectorySeparatorChar } index.md" )
352+ ? newUrl . Remove ( newUrl . LastIndexOf ( "index.md" , StringComparison . Ordinal ) , "index.md" . Length )
353+ : newUrl . Remove ( url . LastIndexOf ( ".md" , StringComparison . Ordinal ) , ".md" . Length ) ;
340354 }
341355
342- if ( ! string . IsNullOrWhiteSpace ( url ) && ! string . IsNullOrWhiteSpace ( urlPathPrefix ) )
343- url = $ "{ urlPathPrefix . TrimEnd ( '/' ) } { url } ";
344-
345356 // TODO this is hardcoded should be part of extension system
346- if ( url . EndsWith ( ".toml" ) )
347- url = url [ ..^ 5 ] ;
357+ if ( newUrl . EndsWith ( ".toml" ) )
358+ newUrl = url [ ..^ 5 ] ;
348359
349- link . Url = string . IsNullOrEmpty ( anchor ) ? url : $ "{ url } #{ anchor } ";
360+ link . Url = string . IsNullOrEmpty ( anchor ) ? newUrl : $ "{ newUrl } #{ anchor } ";
350361
351362 }
352363
0 commit comments