зеркало из https://github.com/github/VisualStudio.git
Fix positioning of review comments.
This required a rewrite of our diff handling - now using VS' inbuilt differ rather than DiffPlex.
This commit is contained in:
Родитель
3e41d5dd1c
Коммит
f6491f2cfe
|
@ -0,0 +1,5 @@
|
|||
<ProjectConfiguration>
|
||||
<Settings>
|
||||
<PreviouslyBuiltSuccessfully>True</PreviouslyBuiltSuccessfully>
|
||||
</Settings>
|
||||
</ProjectConfiguration>
|
15
GitHubVS.sln
15
GitHubVS.sln
|
@ -111,6 +111,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GitHub.UI.UnitTests", "test
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GitHub.InlineReviews", "src\GitHub.InlineReviews\GitHub.InlineReviews.csproj", "{7F5ED78B-74A3-4406-A299-70CFB5885B8B}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GitHub.InlineReviews.UnitTests", "test\GitHub.InlineReviews.UnitTests\GitHub.InlineReviews.UnitTests.csproj", "{17EB676B-BB91-48B5-AA59-C67695C647C2}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -461,6 +463,18 @@ Global
|
|||
{7F5ED78B-74A3-4406-A299-70CFB5885B8B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7F5ED78B-74A3-4406-A299-70CFB5885B8B}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{7F5ED78B-74A3-4406-A299-70CFB5885B8B}.Release|x86.Build.0 = Release|Any CPU
|
||||
{17EB676B-BB91-48B5-AA59-C67695C647C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{17EB676B-BB91-48B5-AA59-C67695C647C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{17EB676B-BB91-48B5-AA59-C67695C647C2}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{17EB676B-BB91-48B5-AA59-C67695C647C2}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{17EB676B-BB91-48B5-AA59-C67695C647C2}.Publish|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{17EB676B-BB91-48B5-AA59-C67695C647C2}.Publish|Any CPU.Build.0 = Release|Any CPU
|
||||
{17EB676B-BB91-48B5-AA59-C67695C647C2}.Publish|x86.ActiveCfg = Release|Any CPU
|
||||
{17EB676B-BB91-48B5-AA59-C67695C647C2}.Publish|x86.Build.0 = Release|Any CPU
|
||||
{17EB676B-BB91-48B5-AA59-C67695C647C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{17EB676B-BB91-48B5-AA59-C67695C647C2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{17EB676B-BB91-48B5-AA59-C67695C647C2}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{17EB676B-BB91-48B5-AA59-C67695C647C2}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -485,5 +499,6 @@ Global
|
|||
{DD99FD0F-82F6-4C30-930E-4A1D0DF01D65} = {1E7F7253-A6AF-43C4-A955-37BEDDA01AB9}
|
||||
{7B835A7D-CF94-45E8-B191-96F5A4FE26A8} = {8A7DA2E7-262B-4581-807A-1C45CE79CDFD}
|
||||
{110B206F-8554-4B51-BF86-94DAA32F5E26} = {8A7DA2E7-262B-4581-807A-1C45CE79CDFD}
|
||||
{17EB676B-BB91-48B5-AA59-C67695C647C2} = {8A7DA2E7-262B-4581-807A-1C45CE79CDFD}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
|
@ -61,6 +61,9 @@
|
|||
<Link>Properties\SolutionInfo.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="InlineReviewsPackage.cs" />
|
||||
<Compile Include="Models\DiffChangeType.cs" />
|
||||
<Compile Include="Models\DiffChunk.cs" />
|
||||
<Compile Include="Models\DiffLine.cs" />
|
||||
<Compile Include="Models\InlineCommentModel.cs" />
|
||||
<Compile Include="Peek\ReviewPeekableItem.cs" />
|
||||
<Compile Include="Peek\ReviewPeekableItemSource.cs" />
|
||||
|
@ -73,6 +76,8 @@
|
|||
<Compile Include="Peek\ReviewPeekSessionCreationOptions.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="SampleData\PullRequestReviewCommentDesigner.cs" />
|
||||
<Compile Include="Services\DiffService.cs" />
|
||||
<Compile Include="Services\IDiffService.cs" />
|
||||
<Compile Include="Tags\InlineCommentBuilder.cs" />
|
||||
<Compile Include="Tags\ReviewGlyph.xaml.cs">
|
||||
<DependentUpon>ReviewGlyph.xaml</DependentUpon>
|
||||
|
@ -143,10 +148,6 @@
|
|||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="DiffPlex, Version=1.4.1.0, Culture=neutral, PublicKeyToken=1d35e91d1bd7bc0f, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\DiffPlex.1.4.1\lib\netstandard1.0\DiffPlex.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="EnvDTE, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<EmbedInteropTypes>False</EmbedInteropTypes>
|
||||
</Reference>
|
||||
|
@ -271,58 +272,14 @@
|
|||
<HintPath>..\..\packages\Microsoft.VisualStudio.Validation.14.1.111\lib\net45\Microsoft.VisualStudio.Validation.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Win32.Primitives, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.Win32.Primitives.4.3.0\lib\net46\Microsoft.Win32.Primitives.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="PresentationCore" />
|
||||
<Reference Include="PresentationFramework" />
|
||||
<Reference Include="stdole, Version=7.0.3300.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<EmbedInteropTypes>False</EmbedInteropTypes>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.AppContext, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.AppContext.4.3.0\lib\net46\System.AppContext.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.ComponentModel.Composition" />
|
||||
<Reference Include="System.Console, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Console.4.3.0\lib\net46\System.Console.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Diagnostics.DiagnosticSource, Version=4.0.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Diagnostics.DiagnosticSource.4.3.0\lib\net46\System.Diagnostics.DiagnosticSource.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Globalization.Calendars, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Globalization.Calendars.4.3.0\lib\net46\System.Globalization.Calendars.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.IO.Compression, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.IO.Compression.4.3.0\lib\net46\System.IO.Compression.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.IO.Compression.FileSystem" />
|
||||
<Reference Include="System.IO.Compression.ZipFile, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.IO.Compression.ZipFile.4.3.0\lib\net46\System.IO.Compression.ZipFile.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.IO.FileSystem, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.IO.FileSystem.4.3.0\lib\net46\System.IO.FileSystem.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.IO.FileSystem.Primitives, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.IO.FileSystem.Primitives.4.3.0\lib\net46\System.IO.FileSystem.Primitives.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Net.Http, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Net.Http.4.3.0\lib\net46\System.Net.Http.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Net.Sockets, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Net.Sockets.4.3.0\lib\net46\System.Net.Sockets.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Numerics" />
|
||||
<Reference Include="System.Reactive.Core, Version=2.2.5.0, Culture=neutral, PublicKeyToken=62aa029873c516b4, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Rx-Core.2.2.5-custom\lib\net45\System.Reactive.Core.dll</HintPath>
|
||||
|
@ -340,33 +297,9 @@
|
|||
<HintPath>..\..\packages\Rx-PlatformServices.2.2.5-custom\lib\net45\System.Reactive.PlatformServices.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.InteropServices.RuntimeInformation, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Security.Cryptography.Algorithms, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Security.Cryptography.Algorithms.4.3.0\lib\net461\System.Security.Cryptography.Algorithms.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Security.Cryptography.Encoding, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Security.Cryptography.Primitives, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Security.Cryptography.X509Certificates, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net461\System.Security.Cryptography.X509Certificates.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Xaml" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Xml.ReaderWriter, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Xml.ReaderWriter.4.3.0\lib\net46\System.Xml.ReaderWriter.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="WindowsBase" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
using System;
|
||||
|
||||
namespace GitHub.InlineReviews.Models
|
||||
{
|
||||
public enum DiffChangeType
|
||||
{
|
||||
None,
|
||||
Add,
|
||||
Delete,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace GitHub.InlineReviews.Models
|
||||
{
|
||||
public class DiffChunk
|
||||
{
|
||||
public int OldLineNumber { get; set; }
|
||||
public int NewLineNumber { get; set; }
|
||||
public IList<DiffLine> Lines { get; } = new List<DiffLine>();
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
|
||||
foreach (var line in Lines)
|
||||
{
|
||||
builder.AppendLine(line.Content);
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
|
||||
namespace GitHub.InlineReviews.Models
|
||||
{
|
||||
public class DiffLine
|
||||
{
|
||||
public DiffChangeType Type { get; set; }
|
||||
public int OldLineNumber { get; set; } = -1;
|
||||
public int NewLineNumber { get; set; } = -1;
|
||||
public int DiffLineNumber { get; set; } = -1;
|
||||
public string Content { get; set; }
|
||||
|
||||
public override string ToString() => Content;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using GitHub.InlineReviews.Models;
|
||||
using Microsoft.VisualStudio.Text.Differencing;
|
||||
|
||||
namespace GitHub.InlineReviews.Services
|
||||
{
|
||||
[Export(typeof(IDiffService))]
|
||||
[PartCreationPolicy(CreationPolicy.NonShared)]
|
||||
public class DiffService : IDiffService
|
||||
{
|
||||
static readonly Regex ChunkHeaderRegex = new Regex(@"^@@\s+\-(\d+),?\d+?\s+\+(\d+),?\d+?\s@@");
|
||||
static readonly char[] Newlines = new[] { '\r', '\n' };
|
||||
readonly ITextDifferencingService vsDiff;
|
||||
|
||||
[ImportingConstructor]
|
||||
public DiffService(ITextDifferencingSelectorService diffSelector)
|
||||
{
|
||||
this.vsDiff = diffSelector.DefaultTextDifferencingService;
|
||||
}
|
||||
|
||||
public IEnumerable<DiffChunk> Diff(string left, string right, int contextLines = 3)
|
||||
{
|
||||
var diff = vsDiff.DiffStrings(left, right, new StringDifferenceOptions
|
||||
{
|
||||
DifferenceType = StringDifferenceTypes.Line,
|
||||
IgnoreTrimWhiteSpace = true,
|
||||
});
|
||||
|
||||
foreach (var difference in diff.Differences)
|
||||
{
|
||||
var chunk = new DiffChunk();
|
||||
|
||||
for (var i = contextLines; i > 0; --i)
|
||||
{
|
||||
if (difference.Left.Start - i < 0 || difference.Right.Start - i < 0) break;
|
||||
|
||||
chunk.Lines.Add(new DiffLine
|
||||
{
|
||||
OldLineNumber = (difference.Left.Start - i) + 1,
|
||||
NewLineNumber = (difference.Right.Start - i) + 1,
|
||||
Content = ' ' + diff.LeftSequence[difference.Left.Start - i].TrimEnd(Newlines),
|
||||
});
|
||||
}
|
||||
|
||||
if (difference.DifferenceType == DifferenceType.Remove || difference.DifferenceType == DifferenceType.Change)
|
||||
{
|
||||
for (var i = 0; i < difference.Left.Length; ++i)
|
||||
{
|
||||
chunk.Lines.Add(new DiffLine
|
||||
{
|
||||
Type = DiffChangeType.Delete,
|
||||
OldLineNumber = (difference.Left.Start + i) + 1,
|
||||
Content = '-' + diff.LeftSequence[difference.Left.Start + i].TrimEnd(Newlines),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (difference.DifferenceType == DifferenceType.Add || difference.DifferenceType == DifferenceType.Change)
|
||||
{
|
||||
for (var i = 0; i < difference.Right.Length; ++i)
|
||||
{
|
||||
chunk.Lines.Add(new DiffLine
|
||||
{
|
||||
Type = DiffChangeType.Add,
|
||||
NewLineNumber = (difference.Right.Start + i) + 1,
|
||||
Content = '+' + diff.RightSequence[difference.Right.Start + i].TrimEnd(Newlines),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
chunk.OldLineNumber = chunk.Lines.FirstOrDefault(x => x.OldLineNumber != -1)?.OldLineNumber ?? -1;
|
||||
chunk.NewLineNumber = chunk.Lines.FirstOrDefault(x => x.NewLineNumber != -1)?.NewLineNumber ?? -1;
|
||||
yield return chunk;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<DiffChunk> ParseFragment(string diff)
|
||||
{
|
||||
using (var reader = new StringReader(diff))
|
||||
{
|
||||
string line;
|
||||
DiffChunk chunk = null;
|
||||
int diffLine = 1;
|
||||
int oldLine = -1;
|
||||
int newLine = -1;
|
||||
|
||||
while ((line = reader.ReadLine()) != null)
|
||||
{
|
||||
var headerMatch = ChunkHeaderRegex.Match(line);
|
||||
|
||||
if (headerMatch.Success)
|
||||
{
|
||||
if (chunk != null)
|
||||
{
|
||||
yield return chunk;
|
||||
}
|
||||
|
||||
chunk = new DiffChunk
|
||||
{
|
||||
OldLineNumber = oldLine = int.Parse(headerMatch.Groups[1].Value),
|
||||
NewLineNumber = newLine = int.Parse(headerMatch.Groups[2].Value),
|
||||
};
|
||||
}
|
||||
else if (chunk != null)
|
||||
{
|
||||
var type = GetLineChange(line[0]);
|
||||
|
||||
chunk.Lines.Add(new DiffLine
|
||||
{
|
||||
Type = type,
|
||||
OldLineNumber = type != DiffChangeType.Add ? oldLine : -1,
|
||||
NewLineNumber = type != DiffChangeType.Delete ? newLine : -1,
|
||||
DiffLineNumber = diffLine,
|
||||
Content = line,
|
||||
});
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case DiffChangeType.None:
|
||||
++oldLine;
|
||||
++newLine;
|
||||
break;
|
||||
case DiffChangeType.Delete:
|
||||
++oldLine;
|
||||
break;
|
||||
case DiffChangeType.Add:
|
||||
++newLine;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
++diffLine;
|
||||
}
|
||||
|
||||
if (chunk != null)
|
||||
{
|
||||
yield return chunk;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static DiffChangeType GetLineChange(char c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case ' ': return DiffChangeType.None;
|
||||
case '+': return DiffChangeType.Add;
|
||||
case '-': return DiffChangeType.Delete;
|
||||
default: throw new InvalidDataException(@"Invalid diff line change char: '{c}'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
using System.Collections.Generic;
|
||||
using GitHub.InlineReviews.Models;
|
||||
|
||||
namespace GitHub.InlineReviews.Services
|
||||
{
|
||||
public interface IDiffService
|
||||
{
|
||||
IEnumerable<DiffChunk> Diff(string before, string after, int contextLines = 3);
|
||||
IEnumerable<DiffChunk> ParseFragment(string diff);
|
||||
}
|
||||
}
|
|
@ -1,11 +1,9 @@
|
|||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using DiffPlex;
|
||||
using DiffPlex.DiffBuilder;
|
||||
using DiffPlex.DiffBuilder.Model;
|
||||
using GitHub.Extensions;
|
||||
using GitHub.InlineReviews.Models;
|
||||
using GitHub.Models;
|
||||
|
@ -18,20 +16,23 @@ namespace GitHub.InlineReviews.Services
|
|||
public class InlineCommentBuilder
|
||||
{
|
||||
readonly IGitClient gitClient;
|
||||
readonly IDiffService diffService;
|
||||
readonly IPullRequestReviewSession session;
|
||||
readonly IRepository repository;
|
||||
readonly string path;
|
||||
readonly bool leftHandSide;
|
||||
readonly string tabsToSpaces;
|
||||
readonly IReadOnlyList<IPullRequestReviewCommentModel> comments;
|
||||
readonly InlineDiffBuilder differ = new InlineDiffBuilder(new Differ());
|
||||
Dictionary<int, DiffHunk> diffHunks;
|
||||
Dictionary<int, List<DiffLine>> diffHunks;
|
||||
string baseCommit;
|
||||
|
||||
public InlineCommentBuilder(
|
||||
IGitClient gitClient,
|
||||
IDiffService diffService,
|
||||
IPullRequestReviewSession session,
|
||||
IRepository repository,
|
||||
string path,
|
||||
bool leftHandSide,
|
||||
int? tabsToSpaces)
|
||||
{
|
||||
Guard.ArgumentNotNull(gitClient, nameof(gitClient));
|
||||
|
@ -40,9 +41,11 @@ namespace GitHub.InlineReviews.Services
|
|||
Guard.ArgumentNotNull(path, nameof(path));
|
||||
|
||||
this.gitClient = gitClient;
|
||||
this.diffService = diffService;
|
||||
this.session = session;
|
||||
this.repository = repository;
|
||||
this.path = path;
|
||||
this.leftHandSide = leftHandSide;
|
||||
|
||||
if (tabsToSpaces.HasValue)
|
||||
{
|
||||
|
@ -62,17 +65,17 @@ namespace GitHub.InlineReviews.Services
|
|||
return await Task.Run(() =>
|
||||
{
|
||||
var current = snapshot.GetText();
|
||||
var snapshotDiff = BuildDiff(differ.BuildDiffModel(baseCommit, current));
|
||||
var snapshotDiff = diffService.Diff(baseCommit, current, 4).ToList();
|
||||
var result = new List<InlineCommentModel>();
|
||||
|
||||
foreach (var comment in comments)
|
||||
{
|
||||
var hunk = diffHunks[comment.Id];
|
||||
var match = snapshotDiff.IndexOf(hunk.Text);
|
||||
var match = Match(snapshotDiff, hunk);
|
||||
var lineNumber = GetLineNumber(match);
|
||||
|
||||
if (match != -1)
|
||||
if (lineNumber != -1)
|
||||
{
|
||||
var lineNumber = LineFromPosition(snapshotDiff, match) + hunk.LineCount - 1;
|
||||
var snapshotLine = snapshot.GetLineFromLineNumber(lineNumber);
|
||||
var trackingPoint = snapshot.CreateTrackingPoint(snapshotLine.Start, PointTrackingMode.Positive);
|
||||
result.Add(new InlineCommentModel(lineNumber, comment, trackingPoint));
|
||||
|
@ -85,30 +88,37 @@ namespace GitHub.InlineReviews.Services
|
|||
|
||||
void BuildDiffHunks()
|
||||
{
|
||||
diffHunks = new Dictionary<int, DiffHunk>();
|
||||
diffHunks = new Dictionary<int, List<DiffLine>>();
|
||||
|
||||
foreach (var comment in comments)
|
||||
{
|
||||
// This can definitely be done more efficiently!
|
||||
var lines = ReadLines(comment.DiffHunk)
|
||||
.Reverse()
|
||||
.Take(5)
|
||||
.TakeWhile(x => !x.StartsWith("@@"))
|
||||
.Reverse();
|
||||
var builder = new StringBuilder();
|
||||
var count = 0;
|
||||
|
||||
foreach (var line in lines)
|
||||
{
|
||||
builder.AppendLine(TabsToSpaces(line));
|
||||
++count;
|
||||
}
|
||||
|
||||
var hunk = new DiffHunk(builder.ToString(), count);
|
||||
diffHunks.Add(comment.Id, hunk);
|
||||
var last = diffService.ParseFragment(comment.DiffHunk).Last();
|
||||
diffHunks.Add(comment.Id, last.Lines.Reverse().Take(5).ToList());
|
||||
}
|
||||
}
|
||||
|
||||
DiffLine Match(IEnumerable<DiffChunk> diff, List<DiffLine> target)
|
||||
{
|
||||
int j = 0;
|
||||
|
||||
foreach (var source in diff)
|
||||
{
|
||||
for (var i = source.Lines.Count - 1; i >= 0; --i)
|
||||
{
|
||||
if (source.Lines[i].Content == target[j].Content)
|
||||
{
|
||||
if (++j == target.Count) return source.Lines[i + j - 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
j = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
string TabsToSpaces(string s)
|
||||
{
|
||||
return tabsToSpaces != null ? s.Replace("\t", tabsToSpaces) : s;
|
||||
|
@ -122,66 +132,17 @@ namespace GitHub.InlineReviews.Services
|
|||
path) ?? string.Empty;
|
||||
}
|
||||
|
||||
static string BuildDiff(DiffPaneModel diffModel)
|
||||
int GetLineNumber(DiffLine line)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
|
||||
foreach (var line in diffModel.Lines)
|
||||
if (line != null)
|
||||
{
|
||||
switch (line.Type)
|
||||
{
|
||||
case ChangeType.Inserted:
|
||||
builder.Append('+');
|
||||
break;
|
||||
case ChangeType.Deleted:
|
||||
builder.Append('-');
|
||||
break;
|
||||
default:
|
||||
builder.Append(' ');
|
||||
break;
|
||||
}
|
||||
|
||||
builder.AppendLine(line.Text);
|
||||
if (leftHandSide && line.OldLineNumber != -1)
|
||||
return line.OldLineNumber - 1;
|
||||
if (!leftHandSide && line.NewLineNumber != -1)
|
||||
return line.NewLineNumber - 1;
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
static int LineFromPosition(string s, int position)
|
||||
{
|
||||
var result = 0;
|
||||
|
||||
for (var i = 0; i < position; ++i)
|
||||
{
|
||||
if (s[i] == '\n') ++result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static IEnumerable<string> ReadLines(string s)
|
||||
{
|
||||
using (var reader = new StringReader(s))
|
||||
{
|
||||
string line;
|
||||
|
||||
while ((line = reader.ReadLine()) != null)
|
||||
{
|
||||
yield return line;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DiffHunk
|
||||
{
|
||||
public DiffHunk(string text, int lineCount)
|
||||
{
|
||||
Text = text;
|
||||
LineCount = lineCount;
|
||||
}
|
||||
|
||||
public string Text { get; }
|
||||
public int LineCount { get; }
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ namespace GitHub.InlineReviews.Tags
|
|||
{
|
||||
readonly IGitService gitService;
|
||||
readonly IGitClient gitClient;
|
||||
readonly IDiffService diffService;
|
||||
readonly ITextBuffer buffer;
|
||||
readonly ITextView view;
|
||||
readonly IPullRequestReviewSessionManager sessionManager;
|
||||
|
@ -29,7 +30,7 @@ namespace GitHub.InlineReviews.Tags
|
|||
readonly int? tabsToSpaces;
|
||||
bool initialized;
|
||||
string fullPath;
|
||||
bool diffLeftHandSide;
|
||||
bool leftHandSide;
|
||||
IDisposable subscription;
|
||||
IPullRequestReviewSession session;
|
||||
InlineCommentBuilder commentBuilder;
|
||||
|
@ -38,17 +39,20 @@ namespace GitHub.InlineReviews.Tags
|
|||
public ReviewTagger(
|
||||
IGitService gitService,
|
||||
IGitClient gitClient,
|
||||
IDiffService diffService,
|
||||
ITextView view,
|
||||
ITextBuffer buffer,
|
||||
IPullRequestReviewSessionManager sessionManager)
|
||||
{
|
||||
Guard.ArgumentNotNull(gitService, nameof(gitService));
|
||||
Guard.ArgumentNotNull(gitClient, nameof(gitClient));
|
||||
Guard.ArgumentNotNull(diffService, nameof(diffService));
|
||||
Guard.ArgumentNotNull(buffer, nameof(buffer));
|
||||
Guard.ArgumentNotNull(sessionManager, nameof(sessionManager));
|
||||
|
||||
this.gitService = gitService;
|
||||
this.gitClient = gitClient;
|
||||
this.diffService = diffService;
|
||||
this.buffer = buffer;
|
||||
this.view = view;
|
||||
this.sessionManager = sessionManager;
|
||||
|
@ -123,7 +127,7 @@ namespace GitHub.InlineReviews.Tags
|
|||
if (bufferTag != null)
|
||||
{
|
||||
fullPath = bufferTag.Path;
|
||||
diffLeftHandSide = bufferTag.IsLeftBuffer;
|
||||
leftHandSide = bufferTag.IsLeftBuffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -169,7 +173,7 @@ namespace GitHub.InlineReviews.Tags
|
|||
|
||||
var snapshot = buffer.CurrentSnapshot;
|
||||
|
||||
if (diffLeftHandSide)
|
||||
if (leftHandSide)
|
||||
{
|
||||
// If we're tagging the LHS of a diff, then the snapshot will be the base commit
|
||||
// (as you'd expect) but that means that the diff will be empty, so get the RHS
|
||||
|
@ -181,7 +185,14 @@ namespace GitHub.InlineReviews.Tags
|
|||
if (snapshot == null) return;
|
||||
|
||||
var repository = gitService.GetRepository(session.Repository.LocalPath);
|
||||
commentBuilder = new InlineCommentBuilder(gitClient, session, repository, path, tabsToSpaces);
|
||||
commentBuilder = new InlineCommentBuilder(
|
||||
gitClient,
|
||||
diffService,
|
||||
session,
|
||||
repository,
|
||||
path,
|
||||
leftHandSide,
|
||||
tabsToSpaces);
|
||||
|
||||
comments = await commentBuilder.Update(snapshot);
|
||||
NotifyTagsChanged();
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using GitHub.Extensions;
|
||||
using GitHub.InlineReviews.Services;
|
||||
using GitHub.Services;
|
||||
using Microsoft.VisualStudio.Text;
|
||||
using Microsoft.VisualStudio.Text.Editor;
|
||||
|
@ -16,12 +17,14 @@ namespace GitHub.InlineReviews.Tags
|
|||
{
|
||||
readonly IGitService gitService;
|
||||
readonly IGitClient gitClient;
|
||||
readonly IDiffService diffService;
|
||||
readonly IPullRequestReviewSessionManager sessionManager;
|
||||
|
||||
[ImportingConstructor]
|
||||
public ReviewTaggerProvider(
|
||||
IGitService gitService,
|
||||
IGitClient gitClient,
|
||||
IDiffService diffService,
|
||||
IPullRequestReviewSessionManager sessionManager)
|
||||
{
|
||||
Guard.ArgumentNotNull(gitService, nameof(gitService));
|
||||
|
@ -30,13 +33,20 @@ namespace GitHub.InlineReviews.Tags
|
|||
|
||||
this.gitService = gitService;
|
||||
this.gitClient = gitClient;
|
||||
this.diffService = diffService;
|
||||
this.sessionManager = sessionManager;
|
||||
}
|
||||
|
||||
public ITagger<T> CreateTagger<T>(ITextView view, ITextBuffer buffer) where T : ITag
|
||||
{
|
||||
return buffer.Properties.GetOrCreateSingletonProperty(()=>
|
||||
new ReviewTagger(gitService, gitClient, view, buffer, sessionManager)) as ITagger<T>;
|
||||
new ReviewTagger(
|
||||
gitService,
|
||||
gitClient,
|
||||
diffService,
|
||||
view,
|
||||
buffer,
|
||||
sessionManager)) as ITagger<T>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="DiffPlex" version="1.4.1" targetFramework="net461" />
|
||||
<package id="LibGit2Sharp" version="0.22.0" targetFramework="net461" />
|
||||
<package id="LibGit2Sharp.NativeBinaries" version="1.0.129" targetFramework="net461" />
|
||||
<package id="Microsoft.NETCore.Platforms" version="1.1.0" targetFramework="net461" />
|
||||
<package id="Microsoft.VisualStudio.CoreUtility" version="14.3.25407" targetFramework="net461" />
|
||||
<package id="Microsoft.VisualStudio.Editor" version="14.3.25407" targetFramework="net461" />
|
||||
<package id="Microsoft.VisualStudio.Imaging" version="14.3.25407" targetFramework="net452" />
|
||||
|
@ -30,54 +28,9 @@
|
|||
<package id="Microsoft.VisualStudio.Utilities" version="14.3.25407" targetFramework="net452" />
|
||||
<package id="Microsoft.VisualStudio.Validation" version="14.1.111" targetFramework="net452" />
|
||||
<package id="Microsoft.VSSDK.BuildTools" version="14.3.25407" targetFramework="net452" developmentDependency="true" />
|
||||
<package id="Microsoft.Win32.Primitives" version="4.3.0" targetFramework="net461" />
|
||||
<package id="NETStandard.Library" version="1.6.1" targetFramework="net461" />
|
||||
<package id="Rx-Core" version="2.2.5-custom" targetFramework="net461" />
|
||||
<package id="Rx-Interfaces" version="2.2.5-custom" targetFramework="net461" />
|
||||
<package id="Rx-Linq" version="2.2.5-custom" targetFramework="net461" />
|
||||
<package id="Rx-Main" version="2.2.5-custom" targetFramework="net461" />
|
||||
<package id="Rx-PlatformServices" version="2.2.5-custom" targetFramework="net461" />
|
||||
<package id="System.AppContext" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Collections" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Collections.Concurrent" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Console" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Diagnostics.Debug" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Diagnostics.DiagnosticSource" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Diagnostics.Tools" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Diagnostics.Tracing" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Globalization" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Globalization.Calendars" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.IO" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.IO.Compression" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.IO.Compression.ZipFile" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.IO.FileSystem" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.IO.FileSystem.Primitives" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Linq" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Linq.Expressions" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Net.Http" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Net.Primitives" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Net.Sockets" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.ObjectModel" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Reflection" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Reflection.Extensions" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Reflection.Primitives" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Resources.ResourceManager" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Runtime" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Runtime.Extensions" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Runtime.Handles" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Runtime.InteropServices" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Runtime.InteropServices.RuntimeInformation" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Runtime.Numerics" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Security.Cryptography.Algorithms" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Security.Cryptography.Encoding" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Security.Cryptography.Primitives" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Security.Cryptography.X509Certificates" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Text.Encoding" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Text.Encoding.Extensions" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Text.RegularExpressions" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Threading" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Threading.Tasks" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Threading.Timer" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Xml.ReaderWriter" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Xml.XDocument" version="4.3.0" targetFramework="net461" />
|
||||
</packages>
|
|
@ -135,9 +135,9 @@ namespace GitHub.VisualStudio.UI.Views
|
|||
|
||||
void AddCompareBufferTag(ITextBuffer buffer, string path, bool isLeftBuffer)
|
||||
{
|
||||
buffer.Properties.AddProperty(
|
||||
buffer.Properties.GetOrCreateSingletonProperty(
|
||||
typeof(CompareBufferTag),
|
||||
new CompareBufferTag(path, isLeftBuffer));
|
||||
() => new CompareBufferTag(path, isLeftBuffer));
|
||||
}
|
||||
|
||||
void EnableGlyphMargin(IPropertyOwner propertyOwner)
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{17EB676B-BB91-48B5-AA59-C67695C647C2}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>GitHub.InlineReviews.UnitTests</RootNamespace>
|
||||
<AssemblyName>GitHub.InlineReviews.UnitTests</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.VisualStudio.CoreUtility, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.VisualStudio.CoreUtility.14.3.25407\lib\net45\Microsoft.VisualStudio.CoreUtility.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.VisualStudio.Text.Data, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.VisualStudio.Text.Data.14.3.25407\lib\net45\Microsoft.VisualStudio.Text.Data.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="NSubstitute, Version=2.0.3.0, Culture=neutral, PublicKeyToken=92dd2e9066daa5ca, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\NSubstitute.2.0.3\lib\net45\NSubstitute.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="xunit.abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\xunit.abstractions.2.0.0\lib\net35\xunit.abstractions.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="xunit.assert, Version=2.1.0.3179, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\xunit.assert.2.1.0\lib\dotnet\xunit.assert.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="xunit.core, Version=2.1.0.3179, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\xunit.extensibility.core.2.1.0\lib\dotnet\xunit.core.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="xunit.execution.desktop, Version=2.1.0.3179, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\xunit.extensibility.execution.2.1.0\lib\net45\xunit.execution.desktop.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Services\DiffServiceTests.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\GitHub.InlineReviews\GitHub.InlineReviews.csproj">
|
||||
<Project>{7f5ed78b-74a3-4406-a299-70cfb5885b8b}</Project>
|
||||
<Name>GitHub.InlineReviews</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Resources\pr-960-diff.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -0,0 +1,36 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("GitHub.InlineReviews.UnitTests")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("GitHub.InlineReviews.UnitTests")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2017")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("17eb676b-bb91-48b5-aa59-c67695c647c2")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
@ -0,0 +1,85 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace GitHub.InlineReviews.UnitTests.Properties {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("GitHub.InlineReviews.UnitTests.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to diff --git a/src/GitHub.VisualStudio/Services/UsageTracker.cs b/src/GitHub.VisualStudio/Services/UsageTracker.cs
|
||||
///index b02decb..f7dadae 100644
|
||||
///--- a/src/GitHub.VisualStudio/Services/UsageTracker.cs
|
||||
///+++ b/src/GitHub.VisualStudio/Services/UsageTracker.cs
|
||||
///@@ -11,21 +11,21 @@
|
||||
/// using GitHub.Extensions;
|
||||
/// using System.Threading.Tasks;
|
||||
/// using GitHub.Helpers;
|
||||
///+using System.Threading;
|
||||
///
|
||||
/// namespace GitHub.Services
|
||||
/// {
|
||||
///- public class UsageTracker : IUsageTracker
|
||||
///+ public sealed class UsageTracker : IU [rest of string was truncated]";.
|
||||
/// </summary>
|
||||
internal static string pr_960_diff {
|
||||
get {
|
||||
return ResourceManager.GetString("pr_960_diff", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||
<data name="pr_960_diff" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\resources\pr-960-diff.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
|
||||
</data>
|
||||
</root>
|
|
@ -0,0 +1,83 @@
|
|||
diff --git a/src/GitHub.VisualStudio/Services/UsageTracker.cs b/src/GitHub.VisualStudio/Services/UsageTracker.cs
|
||||
index b02decb..f7dadae 100644
|
||||
--- a/src/GitHub.VisualStudio/Services/UsageTracker.cs
|
||||
+++ b/src/GitHub.VisualStudio/Services/UsageTracker.cs
|
||||
@@ -11,21 +11,21 @@
|
||||
using GitHub.Extensions;
|
||||
using System.Threading.Tasks;
|
||||
using GitHub.Helpers;
|
||||
+using System.Threading;
|
||||
|
||||
namespace GitHub.Services
|
||||
{
|
||||
- public class UsageTracker : IUsageTracker
|
||||
+ public sealed class UsageTracker : IUsageTracker, IDisposable
|
||||
{
|
||||
const string StoreFileName = "ghfvs.usage";
|
||||
static readonly Calendar cal = CultureInfo.InvariantCulture.Calendar;
|
||||
|
||||
readonly IGitHubServiceProvider gitHubServiceProvider;
|
||||
- readonly DispatcherTimer timer;
|
||||
-
|
||||
IMetricsService client;
|
||||
IConnectionManager connectionManager;
|
||||
IPackageSettings userSettings;
|
||||
IVSServices vsservices;
|
||||
+ Timer timer;
|
||||
string storePath;
|
||||
bool firstRun = true;
|
||||
|
||||
@@ -61,13 +61,16 @@ public UsageTracker(IGitHubServiceProvider gitHubServiceProvider)
|
||||
};
|
||||
dirCreate = (path) => System.IO.Directory.CreateDirectory(path);
|
||||
|
||||
- this.timer = new DispatcherTimer(
|
||||
- TimeSpan.FromMinutes(3),
|
||||
- DispatcherPriority.Background,
|
||||
+ this.timer = new Timer(
|
||||
TimerTick,
|
||||
- ThreadingHelper.MainThreadDispatcher);
|
||||
+ null,
|
||||
+ TimeSpan.FromMinutes(3),
|
||||
+ TimeSpan.FromHours(8));
|
||||
+ }
|
||||
|
||||
- RunTimer();
|
||||
+ public void Dispose()
|
||||
+ {
|
||||
+ timer?.Dispose();
|
||||
}
|
||||
|
||||
public async Task IncrementLaunchCount()
|
||||
@@ -244,14 +247,7 @@ void SaveUsage(UsageStore store)
|
||||
writeAllText(storePath, json, Encoding.UTF8);
|
||||
}
|
||||
|
||||
- void RunTimer()
|
||||
- {
|
||||
- // The timer first ticks after 3 minutes to allow things to settle down after startup.
|
||||
- // This will be changed to 8 hours after the first tick by the TimerTick method.
|
||||
- timer.Start();
|
||||
- }
|
||||
-
|
||||
- void TimerTick(object sender, EventArgs e)
|
||||
+ void TimerTick(object state)
|
||||
{
|
||||
TimerTick()
|
||||
.Catch(ex =>
|
||||
@@ -268,13 +264,13 @@ void TimerTick(object sender, EventArgs e)
|
||||
if (firstRun)
|
||||
{
|
||||
await IncrementLaunchCount();
|
||||
- timer.Interval = TimeSpan.FromHours(8);
|
||||
firstRun = false;
|
||||
}
|
||||
|
||||
if (client == null || !userSettings.CollectMetrics)
|
||||
{
|
||||
- timer.Stop();
|
||||
+ timer.Dispose();
|
||||
+ timer = null;
|
||||
return;
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using GitHub.InlineReviews.Services;
|
||||
using GitHub.InlineReviews.UnitTests.Properties;
|
||||
using Microsoft.VisualStudio.Text.Differencing;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace GitHub.InlineReviews.UnitTests.Services
|
||||
{
|
||||
public class DiffServiceTests
|
||||
{
|
||||
public class TheParseFragmentMethod
|
||||
{
|
||||
[Fact]
|
||||
public void ShouldParsePr960()
|
||||
{
|
||||
var target = new DiffService(Substitute.For<ITextDifferencingSelectorService>());
|
||||
var result = target.ParseFragment(Resources.pr_960_diff).ToList();
|
||||
|
||||
Assert.Equal(4, result.Count);
|
||||
|
||||
Assert.Equal(11, result[0].OldLineNumber);
|
||||
Assert.Equal(11, result[0].NewLineNumber);
|
||||
Assert.Equal(24, result[0].Lines.Count);
|
||||
|
||||
Assert.Equal(61, result[1].OldLineNumber);
|
||||
Assert.Equal(61, result[1].NewLineNumber);
|
||||
Assert.Equal(21, result[1].Lines.Count);
|
||||
|
||||
Assert.Equal(244, result[2].OldLineNumber);
|
||||
Assert.Equal(247, result[2].NewLineNumber);
|
||||
Assert.Equal(15, result[2].Lines.Count);
|
||||
|
||||
Assert.Equal(268, result[3].OldLineNumber);
|
||||
Assert.Equal(264, result[3].NewLineNumber);
|
||||
Assert.Equal(15, result[3].Lines.Count);
|
||||
|
||||
// - public class UsageTracker : IUsageTracker
|
||||
Assert.Equal(17, result[0].Lines[7].OldLineNumber);
|
||||
Assert.Equal(-1, result[0].Lines[7].NewLineNumber);
|
||||
Assert.Equal(13, result[0].Lines[7].DiffLineNumber);
|
||||
|
||||
// + public sealed class UsageTracker : IUsageTracker, IDisposable
|
||||
Assert.Equal(-1, result[0].Lines[8].OldLineNumber);
|
||||
Assert.Equal(18, result[0].Lines[8].NewLineNumber);
|
||||
Assert.Equal(14, result[0].Lines[8].DiffLineNumber);
|
||||
|
||||
// IConnectionManager connectionManager;
|
||||
Assert.Equal(26, result[0].Lines[17].OldLineNumber);
|
||||
Assert.Equal(25, result[0].Lines[17].NewLineNumber);
|
||||
Assert.Equal(23, result[0].Lines[17].DiffLineNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.1.1.0" newVersion="4.1.1.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Diagnostics.DiagnosticSource" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Xml.ReaderWriter" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.1.0.0" newVersion="4.1.0.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.VisualStudio.CoreUtility" version="14.3.25407" targetFramework="net461" />
|
||||
<package id="Microsoft.VisualStudio.Text.Data" version="14.3.25407" targetFramework="net461" />
|
||||
<package id="NSubstitute" version="2.0.3" targetFramework="net461" />
|
||||
<package id="xunit" version="2.1.0" targetFramework="net452" />
|
||||
<package id="xunit.abstractions" version="2.0.0" targetFramework="net452" />
|
||||
<package id="xunit.assert" version="2.1.0" targetFramework="net452" />
|
||||
<package id="xunit.core" version="2.1.0" targetFramework="net452" />
|
||||
<package id="xunit.extensibility.core" version="2.1.0" targetFramework="net452" />
|
||||
<package id="xunit.extensibility.execution" version="2.1.0" targetFramework="net452" />
|
||||
</packages>
|
Загрузка…
Ссылка в новой задаче