@@ -637,55 +637,55 @@ private static string UnescapeDriveColon(string fileUri)
637
637
/// A file system path. Note: if the path is already a DocumentUri, it will be returned unmodified.
638
638
/// </param>
639
639
/// <returns>The file system path encoded as a DocumentUri.</returns>
640
- internal static string ConvertPathToDocumentUri ( string path )
640
+ public static string ConvertPathToDocumentUri ( string path )
641
641
{
642
642
const string fileUriPrefix = "file:///" ;
643
+ const string untitledUriPrefix = "untitled:" ;
643
644
644
- if ( path . StartsWith ( "untitled:" , StringComparison . Ordinal ) )
645
+ // If path is already in document uri form, there is nothing to convert.
646
+ if ( path . StartsWith ( untitledUriPrefix , StringComparison . Ordinal ) ||
647
+ path . StartsWith ( fileUriPrefix , StringComparison . Ordinal ) )
645
648
{
646
649
return path ;
647
650
}
648
651
649
- if ( path . StartsWith ( fileUriPrefix , StringComparison . Ordinal ) )
650
- {
651
- return path ;
652
- }
652
+ string escapedPath = Uri . EscapeDataString ( path ) ;
653
+ var docUriStrBld = new StringBuilder ( escapedPath ) ;
653
654
654
- if ( ! RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) )
655
+ if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) )
655
656
{
656
- // On a Linux filesystem, you can have multiple colons in a filename e.g. foo:bar:baz.txt
657
- string absoluteUri = new Uri ( path ) . AbsoluteUri ;
658
-
659
- // First colon is part of the protocol scheme, see if there are other colons in the path
660
- int firstColonIndex = absoluteUri . IndexOf ( ':' ) ;
661
- if ( absoluteUri . IndexOf ( ':' , firstColonIndex + 1 ) > firstColonIndex )
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.
660
+ if ( path . Contains ( ':' ) )
662
661
{
663
- absoluteUri = new StringBuilder ( absoluteUri )
664
- . Replace (
665
- oldValue : ":" ,
666
- newValue : "%3A" ,
667
- startIndex : firstColonIndex + 1 ,
668
- count : absoluteUri . Length - firstColonIndex - 1 )
669
- . ToString ( ) ;
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 ++ )
665
+ {
666
+ if ( ( docUriStrBld [ i ] == '%' ) && ( docUriStrBld [ i + 1 ] == '3' ) && ( docUriStrBld [ i + 2 ] == 'A' ) )
667
+ {
668
+ int driveLetterIndex = i - 1 ;
669
+ char driveLetter = char . ToLowerInvariant ( docUriStrBld [ driveLetterIndex ] ) ;
670
+ docUriStrBld . Replace ( path [ driveLetterIndex ] , driveLetter , driveLetterIndex , 1 ) ;
671
+ break ;
672
+ }
673
+ }
670
674
}
671
675
672
- return absoluteUri ;
676
+ // Uri.EscapeDataString goes a bit far, encoding \ chars. Also, VSCode wants / instead of \.
677
+ docUriStrBld . Replace ( "%5C" , "/" ) ;
673
678
}
674
-
675
- // VSCode file URIs on Windows need the drive letter lowercase, and the colon
676
- // URI encoded. System.Uri won't do that, so we manually create the URI.
677
- var newUri = new StringBuilder ( System . Web . HttpUtility . UrlPathEncode ( path ) ) ;
678
- int colonIndex = path . IndexOf ( ':' ) ;
679
- if ( colonIndex > 0 )
679
+ else
680
680
{
681
- int driveLetterIndex = colonIndex - 1 ;
682
- char driveLetter = char . ToLowerInvariant ( path [ driveLetterIndex ] ) ;
683
- newUri
684
- . Replace ( path [ driveLetterIndex ] , driveLetter , driveLetterIndex , 1 )
685
- . Replace ( ":" , "%3A" , colonIndex , 1 ) ;
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" , "/" ) ;
686
685
}
687
686
688
- return newUri . Replace ( '\\ ' , '/' ) . Insert ( 0 , fileUriPrefix ) . ToString ( ) ;
687
+ // ' is not always encoded. I've seen this in Windows PowerShell.
688
+ return docUriStrBld . Replace ( "'" , "%27" ) . Insert ( 0 , fileUriPrefix ) . ToString ( ) ;
689
689
}
690
690
691
691
#endregion
0 commit comments