@@ -650,50 +650,76 @@ public static string ConvertPathToDocumentUri(string path)
650650 }
651651
652652 string escapedPath = Uri . EscapeDataString ( path ) ;
653- var docUriStrBld = new StringBuilder ( escapedPath ) ;
653+
654+ // Max capacity of the StringBuilder will be the current escapedPath length
655+ // plus extra chars for file:///.
656+ var docUriStrBld = new StringBuilder ( escapedPath . Length + fileUriPrefix . Length + 3 ) ;
657+ docUriStrBld . Append ( fileUriPrefix ) . Append ( "//" ) ;
654658
655659 if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) )
656660 {
657- // VSCode file URIs on Windows need the drive letter lowercase.
658- // Search original path for colon since a char search (no string culture involved)
659- // is faster than a string search.
661+ // VSCode file URIs on Windows need the drive letter to be lowercase. Search the
662+ // original path for colon since a char search (no string culture involved) is
663+ // faster than a string search. If found, then lowercase the associated drive letter .
660664 if ( path . Contains ( ':' ) )
661665 {
662- // Start at index 1 to avoid an index out of range check when accessing index - 1.
663- // Also, if the colon is at index 0 there is no drive letter before it to lower case.
664- for ( int i = 1 ; i < docUriStrBld . Length - 2 ; i ++ )
666+ // A valid, drive-letter based path converted to URI form needs to be prefixed
667+ // with a / to indicate the path is an absolute path.
668+ docUriStrBld . Append ( "/" ) ;
669+ int prefixLen = docUriStrBld . Length ;
670+
671+ docUriStrBld . Append ( escapedPath ) ;
672+
673+ // Uri.EscapeDataString goes a bit far, encoding \ chars. Also, VSCode wants / instead of \.
674+ docUriStrBld . Replace ( "%5C" , "/" ) ;
675+
676+ // Find the first colon after the "file:///" prefix, skipping the first char after
677+ // the prefix since a Windows path cannot start with a colon. End the check at
678+ // less than docUriStrBld.Length - 2 since we need to look-ahead two characters.
679+ for ( int i = prefixLen + 1 ; i < docUriStrBld . Length - 2 ; i ++ )
665680 {
666681 if ( ( docUriStrBld [ i ] == '%' ) && ( docUriStrBld [ i + 1 ] == '3' ) && ( docUriStrBld [ i + 2 ] == 'A' ) )
667682 {
668683 int driveLetterIndex = i - 1 ;
669684 char driveLetter = char . ToLowerInvariant ( docUriStrBld [ driveLetterIndex ] ) ;
670- docUriStrBld . Replace ( path [ driveLetterIndex ] , driveLetter , driveLetterIndex , 1 ) ;
685+ docUriStrBld . Replace ( docUriStrBld [ driveLetterIndex ] , driveLetter , driveLetterIndex , 1 ) ;
671686 break ;
672687 }
673688 }
674689 }
690+ else
691+ {
692+ // This is a Windows path without a drive specifier, must be either a relative or UNC path.
693+ int prefixLen = docUriStrBld . Length ;
694+
695+ docUriStrBld . Append ( escapedPath ) ;
696+
697+ // Uri.EscapeDataString goes a bit far, encoding \ chars. Also, VSCode wants / instead of \.
698+ docUriStrBld . Replace ( "%5C" , "/" ) ;
675699
676- // Uri.EscapeDataString goes a bit far, encoding \ chars. Also, VSCode wants / instead of \.
677- docUriStrBld . Replace ( "%5C" , "/" ) ;
700+ // The proper URI form for a UNC path is file://server/share. In the case of a UNC
701+ // path, remove the path's leading // because the file:// prefix already provides it.
702+ if ( ( docUriStrBld . Length > prefixLen + 1 ) &&
703+ ( docUriStrBld [ prefixLen ] == '/' ) &&
704+ ( docUriStrBld [ prefixLen + 1 ] == '/' ) )
705+ {
706+ docUriStrBld . Remove ( prefixLen , 2 ) ;
707+ }
708+ }
678709 }
679710 else
680711 {
681- // Because we will prefix later with file:///, remove the initial encoded / if this is an absolute path.
682- // See https://docs.microsoft.com/en-us/dotnet/api/system.uri?view=netframework-4.7.2#implicit-file-path-support
683- // Uri.EscapeDataString goes a bit far, encoding / chars.
684- docUriStrBld . Replace ( "%2F" , string . Empty , 0 , 3 ) . Replace ( "%2F" , "/" ) ;
712+ // On non-Windows system, simply append the escaped path.
713+ docUriStrBld . Append ( escapedPath ) ;
685714 }
686715
687- // ' is not always encoded. I've seen this in Windows PowerShell.
716+ #if ! CoreCLR
717+ // ' is not encoded by Uri.EscapeDataString in Windows PowerShell 5.x.
718+ // This is apparently a difference between .NET Framework and .NET Core.
688719 docUriStrBld . Replace ( "'" , "%27" ) ;
720+ #endif
689721
690- // Insert /// unless path is a UNC path in which case the proper URI form is file://server/share.
691- if ( ( docUriStrBld . Length < 2 ) || ( ( docUriStrBld [ 0 ] != '/' ) && ( docUriStrBld [ 1 ] != '/' ) ) )
692- {
693- docUriStrBld . Insert ( 0 , "///" ) ;
694- }
695-
696- return docUriStrBld . Insert ( 0 , fileUriPrefix ) . ToString ( ) ;
722+ return docUriStrBld . ToString ( ) ;
697723 }
698724
699725 #endregion
0 commit comments