Skip to content

Commit 0bf0415

Browse files
Merge pull request #721 from StealthyDeveloper/master
Projectnames with a suffix equal to other projects unjustly converted.
2 parents ba17216 + 529a4db commit 0bf0415

File tree

60 files changed

+3998
-104
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+3998
-104
lines changed

CodeConverter/CodeConverter.csproj

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44
<AssemblyName>ICSharpCode.CodeConverter</AssemblyName>
55
<RootNamespace>ICSharpCode.CodeConverter</RootNamespace>
66
<Company>ICSharpCode</Company>
7-
<Description>Convert VB.NET to/from C#
8-
* Accurate: Full project context (through Roslyn) is used to get the most accurate conversion.
9-
* Actively developed: User feedback helps us continuously strive for a more accurate conversion.
10-
* Completely free and open source
11-
This nuget package is useful if you're building a tool which converts code, or if you want finer-grained control over what's converted.
12-
See https://github.com/icsharpcode/CodeConverter for a pre-built command line, visual studio extension and web converter.
13-
</Description>
7+
<Description>
8+
Convert VB.NET to/from C#
9+
* Accurate: Full project context (through Roslyn) is used to get the most accurate conversion.
10+
* Actively developed: User feedback helps us continuously strive for a more accurate conversion.
11+
* Completely free and open source
12+
This nuget package is useful if you're building a tool which converts code, or if you want finer-grained control over what's converted.
13+
See https://github.com/icsharpcode/CodeConverter for a pre-built command line, visual studio extension and web converter.
14+
</Description>
1415
<Product>Code Converter for C# to/from VB.NET</Product>
1516
<Copyright>Copyright (c) 2017-2020 AlphaSierraPapa for the CodeConverter team</Copyright>
1617
<AssemblyVersion>8.2.4.0</AssemblyVersion>
@@ -46,6 +47,7 @@ See https://github.com/icsharpcode/CodeConverter for a pre-built command line, v
4647
<PackageReference Include="Microsoft.VisualStudio.Composition" Version="15.8.98" />
4748
<PackageReference Include="Microsoft.VisualStudio.Threading" Version="15.8.122" />
4849
<PackageReference Include="System.Globalization.Extensions" Version="4.3.0" />
50+
<PackageReference Include="System.IO.Abstractions" Version="13.2.33" />
4951
<PackageReference Include="System.Linq.Async" Version="4.0.0" />
5052
<PackageReference Include="System.Threading.Tasks.Dataflow" Version="4.6.0">
5153
<IncludeAssets>all</IncludeAssets>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace ICSharpCode.CodeConverter.Shared
2+
{
3+
using System.Collections.Generic;
4+
5+
public interface ISolutionFileTextEditor
6+
{
7+
List<(string Find, string Replace, bool FirstOnly)> GetProjectFileProjectReferenceReplacements(
8+
IEnumerable<(string Name, string RelativeProjPath, string ProjContents)> projTuples, string sourceSolutionContents);
9+
}
10+
}

CodeConverter/Shared/PathConverter.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,13 @@ private static string GetConvertedExtension(string originalExtension)
3333
return originalExtension;
3434
}
3535
}
36+
37+
public static string GetRelativePath(string relativeTo, string path)
38+
{
39+
var uri = new Uri(relativeTo);
40+
var rel = Uri.UnescapeDataString(uri.MakeRelativeUri(new Uri(path)).ToString()).Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
41+
42+
return rel;
43+
}
3644
}
3745
}

CodeConverter/Shared/ProjectConversion.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@
1010
using ICSharpCode.CodeConverter.CSharp;
1111
using ICSharpCode.CodeConverter.Util;
1212
using Microsoft.CodeAnalysis;
13-
using Microsoft.CodeAnalysis.Text;
14-
1513
namespace ICSharpCode.CodeConverter.Shared
1614
{
15+
using System.IO.Abstractions;
16+
using Microsoft.CodeAnalysis.Text;
17+
1718
public class ProjectConversion
1819
{
1920
private readonly IProjectContentsConverter _projectContentsConverter;
@@ -22,7 +23,7 @@ public class ProjectConversion
2223
private readonly ILanguageConversion _languageConversion;
2324
private readonly bool _showCompilationErrors;
2425
private readonly bool _returnSelectedNode;
25-
private static readonly string[] BannedPaths = new[] { ".AssemblyAttributes.", "\\bin\\", "\\obj\\" };
26+
private static readonly string[] BannedPaths = { ".AssemblyAttributes.", "\\bin\\", "\\obj\\" };
2627
private readonly CancellationToken _cancellationToken;
2728

2829
private ProjectConversion(IProjectContentsConverter projectContentsConverter, IEnumerable<Document> documentsToConvert, IEnumerable<TextDocument> additionalDocumentsToConvert,

CodeConverter/Shared/SolutionConverter.cs

Lines changed: 54 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
using System.Collections.Generic;
33
using System.IO;
44
using System.Linq;
5-
using System.Text.RegularExpressions;
65
using System.Threading;
76
using System.Threading.Tasks;
87
using ICSharpCode.CodeConverter.Util;
98
using Microsoft.CodeAnalysis;
9+
using System.IO.Abstractions;
1010

1111
namespace ICSharpCode.CodeConverter.Shared
1212
{
@@ -18,32 +18,58 @@ public class SolutionConverter
1818
private readonly List<(string Find, string Replace, bool FirstOnly)> _projectReferenceReplacements;
1919
private readonly IProgress<ConversionProgress> _progress;
2020
private readonly ILanguageConversion _languageConversion;
21+
private readonly SolutionFileTextEditor _solutionFileTextEditor;
2122
private readonly CancellationToken _cancellationToken;
2223

24+
public static IFileSystem FileSystem { get; set; } = new FileSystem();
25+
26+
public static SolutionConverter CreateFor<TLanguageConversion>(IReadOnlyCollection<Project> projectsToConvert, string sourceSolutionContents)
27+
where TLanguageConversion : ILanguageConversion, new()
28+
{
29+
return CreateFor<TLanguageConversion>(projectsToConvert, solutionContents: sourceSolutionContents);
30+
}
31+
2332
public static SolutionConverter CreateFor<TLanguageConversion>(IReadOnlyCollection<Project> projectsToConvert,
2433
ConversionOptions conversionOptions = default,
2534
IProgress<ConversionProgress> progress = null,
26-
CancellationToken cancellationToken = default) where TLanguageConversion : ILanguageConversion, new()
35+
CancellationToken cancellationToken = default,
36+
string solutionContents = "") where TLanguageConversion : ILanguageConversion, new()
2737
{
28-
var conversion = new TLanguageConversion {ConversionOptions = conversionOptions };
29-
return CreateFor(conversion, projectsToConvert, progress, cancellationToken);
38+
var conversion = new TLanguageConversion { ConversionOptions = conversionOptions };
39+
return CreateFor(conversion, projectsToConvert, progress, cancellationToken, solutionContents);
3040
}
3141

3242
public static SolutionConverter CreateFor(ILanguageConversion languageConversion, IReadOnlyCollection<Project> projectsToConvert,
3343
IProgress<ConversionProgress> progress,
34-
CancellationToken cancellationToken)
44+
CancellationToken cancellationToken, string solutionContents = "")
3545
{
3646
languageConversion.ConversionOptions ??= new ConversionOptions();
3747
var solutionFilePath = projectsToConvert.First().Solution.FilePath;
38-
var sourceSolutionContents = File.Exists(solutionFilePath) ? File.ReadAllText(solutionFilePath) : "";
39-
var projectReferenceReplacements = GetProjectReferenceReplacements(projectsToConvert, sourceSolutionContents);
40-
return new SolutionConverter(solutionFilePath, sourceSolutionContents, projectsToConvert, projectReferenceReplacements, progress ?? new Progress<ConversionProgress>(), cancellationToken, languageConversion);
48+
var sourceSolutionContents = File.Exists(solutionFilePath)
49+
? FileSystem.File.ReadAllText(solutionFilePath)
50+
: solutionContents;
51+
52+
var projTuples = projectsToConvert.Select(proj =>
53+
{
54+
var relativeProjPath = PathConverter.GetRelativePath(solutionFilePath, proj.FilePath);
55+
var projContents = FileSystem.File.ReadAllText(proj.FilePath);
56+
57+
return (proj.Name, RelativeProjPath: relativeProjPath, ProjContents: projContents);
58+
});
59+
60+
var solutionFileTextEditor = new SolutionFileTextEditor();
61+
var projectReferenceReplacements = solutionFileTextEditor.GetProjectFileProjectReferenceReplacements(projTuples, sourceSolutionContents);
62+
63+
return new SolutionConverter(solutionFilePath, sourceSolutionContents, projectsToConvert, projectReferenceReplacements,
64+
progress ?? new Progress<ConversionProgress>(), cancellationToken, languageConversion, solutionFileTextEditor);
4165
}
4266

4367
private SolutionConverter(string solutionFilePath,
4468
string sourceSolutionContents, IReadOnlyCollection<Project> projectsToConvert,
45-
List<(string Find, string Replace, bool FirstOnly)> projectReferenceReplacements, IProgress<ConversionProgress> showProgressMessage,
46-
CancellationToken cancellationToken, ILanguageConversion languageConversion)
69+
List<(string Find, string Replace, bool FirstOnly)> projectReferenceReplacements,
70+
IProgress<ConversionProgress> showProgressMessage,
71+
CancellationToken cancellationToken, ILanguageConversion languageConversion,
72+
SolutionFileTextEditor solutionFileTextEditor)
4773
{
4874
_solutionFilePath = solutionFilePath;
4975
_sourceSolutionContents = sourceSolutionContents;
@@ -52,15 +78,18 @@ private SolutionConverter(string solutionFilePath,
5278
_progress = showProgressMessage;
5379
_languageConversion = languageConversion;
5480
_cancellationToken = cancellationToken;
81+
_solutionFileTextEditor = solutionFileTextEditor;
5582
}
5683

57-
public async Task<IAsyncEnumerable<ConversionResult>> Convert()
84+
public async IAsyncEnumerable<ConversionResult> Convert()
5885
{
5986
var projectsToUpdateReferencesOnly = _projectsToConvert.First().Solution.Projects.Except(_projectsToConvert);
6087
var solutionResult = string.IsNullOrWhiteSpace(_sourceSolutionContents) ? Enumerable.Empty<ConversionResult>() : ConvertSolutionFile().Yield();
6188
var convertedProjects = await ConvertProjects();
62-
return convertedProjects
63-
.Concat(UpdateProjectReferences(projectsToUpdateReferencesOnly).Concat(solutionResult).ToAsyncEnumerable());
89+
var projectsAndSolutionResults = UpdateProjectReferences(projectsToUpdateReferencesOnly).Concat(solutionResult).ToAsyncEnumerable();
90+
await foreach (var p in convertedProjects.Concat(projectsAndSolutionResults)) {
91+
yield return p;
92+
}
6493
}
6594

6695
private async Task<IAsyncEnumerable<ConversionResult>> ConvertProjects()
@@ -79,63 +108,32 @@ private IAsyncEnumerable<ConversionResult> ConvertProject(Project project, IEnum
79108

80109
private IEnumerable<ConversionResult> UpdateProjectReferences(IEnumerable<Project> projectsToUpdateReferencesOnly)
81110
{
82-
return projectsToUpdateReferencesOnly
83-
.Where(p => p.FilePath != null) //Some project types like Websites don't have a project file
84-
.Select(project => {
111+
var conversionResults = projectsToUpdateReferencesOnly
112+
.Where(p => p.FilePath != null) //Some project types like Websites don't have a project file
113+
.Select(project => {
85114
var withReferencesReplaced =
86115
new FileInfo(project.FilePath).ConversionResultFromReplacements(_projectReferenceReplacements);
87116
withReferencesReplaced.TargetPathOrNull = withReferencesReplaced.SourcePathOrNull;
88117
return withReferencesReplaced;
89-
}).Where(c => !c.IsIdentity);
90-
}
118+
});
91119

92-
private static List<(string Find, string Replace, bool FirstOnly)> GetProjectReferenceReplacements(IReadOnlyCollection<Project> projectsToConvert,
93-
string sourceSolutionContents)
94-
{
95-
var projectReferenceReplacements = new List<(string Find, string Replace, bool FirstOnly)>();
96-
foreach (var project in projectsToConvert)
97-
{
98-
var projFilename = Path.GetFileName(project.FilePath);
99-
var newProjFilename = PathConverter.TogglePathExtension(projFilename);
100-
projectReferenceReplacements.Add((projFilename, newProjFilename, false));
101-
if (!string.IsNullOrWhiteSpace(sourceSolutionContents)) projectReferenceReplacements.Add(GetProjectGuidReplacement(projFilename, sourceSolutionContents));
102-
}
103-
104-
return projectReferenceReplacements;
120+
return conversionResults.Where(c => !c.IsIdentity);
105121
}
106122

107-
private ConversionResult ConvertSolutionFile()
123+
public ConversionResult ConvertSolutionFile()
108124
{
109125
var projectTypeGuidMappings = _languageConversion.GetProjectTypeGuidMappings();
110-
var projectTypeReplacements = _projectsToConvert.SelectMany(project => GetProjectTypeReplacement(project, projectTypeGuidMappings)).ToList();
126+
var relativeProjPaths = _projectsToConvert.Select(proj =>
127+
(proj.Name, RelativeProjPath: PathConverter.GetRelativePath(_solutionFilePath, proj.FilePath)));
128+
129+
var slnProjectReferenceReplacements = _solutionFileTextEditor.GetSolutionFileProjectReferenceReplacements(relativeProjPaths,
130+
_sourceSolutionContents, projectTypeGuidMappings);
111131

112-
var convertedSolutionContents = _sourceSolutionContents.Replace(_projectReferenceReplacements.Concat(projectTypeReplacements));
132+
var convertedSolutionContents = _sourceSolutionContents.Replace(slnProjectReferenceReplacements);
113133
return new ConversionResult(convertedSolutionContents) {
114134
SourcePathOrNull = _solutionFilePath,
115135
TargetPathOrNull = _solutionFilePath
116136
};
117137
}
118-
119-
private static (string Find, string Replace, bool FirstOnly) GetProjectGuidReplacement(string projFilename, string contents)
120-
{
121-
var projGuidRegex = new Regex(projFilename + @""", ""({[0-9A-Fa-f\-]{32,36}})("")");
122-
var projGuidMatch = projGuidRegex.Match(contents);
123-
var oldGuid = projGuidMatch.Groups[1].Value;
124-
var newGuid = GetDeterministicGuidFrom(new Guid(oldGuid));
125-
return (oldGuid, newGuid.ToString("B").ToUpperInvariant(), false);
126-
}
127-
128-
private IEnumerable<(string, string, bool)> GetProjectTypeReplacement(Project project, IReadOnlyCollection<(string, string)> typeGuidMappings)
129-
{
130-
return typeGuidMappings.Select(guidReplacement => ($@"Project\s*\(\s*""{guidReplacement.Item1}""\s*\)\s*=\s*""{project.Name}""", $@"Project(""{guidReplacement.Item2}"") = ""{project.Name}""", false));
131-
}
132-
133-
private static Guid GetDeterministicGuidFrom(Guid guidToConvert)
134-
{
135-
var codeConverterStaticGuid = new Guid("{B224816B-CC58-4FF1-8258-CA7E629734A0}");
136-
var deterministicNewBytes = codeConverterStaticGuid.ToByteArray().Zip(guidToConvert.ToByteArray(),
137-
(fromFirst, fromSecond) => (byte)(fromFirst ^ fromSecond));
138-
return new Guid(deterministicNewBytes.ToArray());
139-
}
140138
}
141139
}

0 commit comments

Comments
 (0)