提交 72ef9aa1 编写于 作者: G Gen Lu

Merge remote-tracking branch 'dotnet/master' into Fix5130SecondTry

......@@ -12,6 +12,7 @@
using WCHAR = System.Char;
using WORD = System.UInt16;
using System.Reflection.PortableExecutable;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis
{
......@@ -204,9 +205,9 @@ static internal Microsoft.Cci.ResourceSection ReadWin32ResourcesFromCOFF(Stream
var imageResourceSectionBytes = new byte[checked(rsrc1.SizeOfRawData + rsrc2.SizeOfRawData)];
stream.Seek(rsrc1.PointerToRawData, SeekOrigin.Begin);
stream.Read(imageResourceSectionBytes, 0, rsrc1.SizeOfRawData);
stream.TryReadAll(imageResourceSectionBytes, 0, rsrc1.SizeOfRawData); // ConfirmSectionValues ensured that data are available
stream.Seek(rsrc2.PointerToRawData, SeekOrigin.Begin);
stream.Read(imageResourceSectionBytes, rsrc1.SizeOfRawData, rsrc2.SizeOfRawData);
stream.TryReadAll(imageResourceSectionBytes, rsrc1.SizeOfRawData, rsrc2.SizeOfRawData); // ConfirmSectionValues ensured that data are available
const int SizeOfRelocationEntry = 10;
......
......@@ -219,7 +219,7 @@ private static bool TryGetByteArrayFromFileStream(Stream stream, out byte[] buff
// than the buffer size. The default buffer size is 4KB, so this will incur a 4KB
// allocation for any files less than 4KB. That's why, for example, the command
// line compiler actually specifies a very small buffer size.
return stream.Read(buffer, 0, length) == length;
return stream.TryReadAll(buffer, 0, length) == length;
}
}
}
......@@ -24,9 +24,13 @@ public void Commit(int grfCommitFlags)
_stream.Flush();
}
/// <summary>
/// The actual number of bytes read can be fewer than the number of bytes requested
/// if an error occurs or if the end of the stream is reached during the read operation.
/// </summary>
public unsafe void Read(byte[] pv, int cb, IntPtr pcbRead)
{
int bytesRead = _stream.Read(pv, 0, cb);
int bytesRead = _stream.TryReadAll(pv, 0, cb);
if (pcbRead != IntPtr.Zero)
{
......
......@@ -186,7 +186,7 @@ public int WriteBytes(Stream source, int byteCount)
}
int start = Advance(byteCount);
int bytesRead = source.Read(_buffer, start, byteCount);
int bytesRead = source.TryReadAll(_buffer, start, byteCount);
_position = start + bytesRead;
return bytesRead;
}
......
......@@ -92,6 +92,9 @@
<Compile Include="..\..\Compilers\Core\Portable\InternalUtilities\ComStreamWrapper.cs">
<Link>TestHelpers\ComStreamWrapper.cs</Link>
</Compile>
<Compile Include="..\..\Compilers\Core\Portable\InternalUtilities\StreamExtensions.cs">
<Link>TestHelpers\StreamExtensions.cs</Link>
</Compile>
</ItemGroup>
<ItemGroup>
<Compile Include="TestHelpers\AssertEx.cs" />
......
......@@ -60,6 +60,14 @@
"lib/net40/Moq.dll": {}
}
},
"Newtonsoft.Json/6.0.4": {
"compile": {
"lib/net45/Newtonsoft.Json.dll": {}
},
"runtime": {
"lib/net45/Newtonsoft.Json.dll": {}
}
},
"System.AppContext/4.0.0": {
"dependencies": {
"System.Runtime": "[4.0.0, )"
......@@ -370,6 +378,14 @@
"lib/net40/Moq.dll": {}
}
},
"Newtonsoft.Json/6.0.4": {
"compile": {
"lib/net45/Newtonsoft.Json.dll": {}
},
"runtime": {
"lib/net45/Newtonsoft.Json.dll": {}
}
},
"System.AppContext/4.0.0": {
"dependencies": {
"System.Runtime": "[4.0.0, )"
......@@ -790,6 +806,31 @@
"package/services/metadata/core-properties/98e2d674c8ec4e5fbda07a9e01280647.psmdcp"
]
},
"Newtonsoft.Json/6.0.4": {
"sha512": "FyQLmEpjsCrEP+znauLDGAi+h6i9YnaMkITlfIoiM4RYyX3nki306bTHsr/0okiIvIc7BJhQTbOAIZVocccFUw==",
"type": "Package",
"files": [
"[Content_Types].xml",
"_rels/.rels",
"lib/net20/Newtonsoft.Json.dll",
"lib/net20/Newtonsoft.Json.xml",
"lib/net35/Newtonsoft.Json.dll",
"lib/net35/Newtonsoft.Json.xml",
"lib/net40/Newtonsoft.Json.dll",
"lib/net40/Newtonsoft.Json.xml",
"lib/net45/Newtonsoft.Json.dll",
"lib/net45/Newtonsoft.Json.xml",
"lib/netcore45/Newtonsoft.Json.dll",
"lib/netcore45/Newtonsoft.Json.xml",
"lib/portable-net40+sl5+wp80+win8+wpa81/Newtonsoft.Json.dll",
"lib/portable-net40+sl5+wp80+win8+wpa81/Newtonsoft.Json.xml",
"lib/portable-net45+wp80+win8+wpa81/Newtonsoft.Json.dll",
"lib/portable-net45+wp80+win8+wpa81/Newtonsoft.Json.xml",
"Newtonsoft.Json.nuspec",
"package/services/metadata/core-properties/87a0a4e28d50417ea282e20f81bc6477.psmdcp",
"tools/install.ps1"
]
},
"System.AppContext/4.0.0": {
"sha512": "gUoYgAWDC3+xhKeU5KSLbYDhTdBYk9GssrMSCcWUADzOglW+s0AmwVhOUGt2tL5xUl7ZXoYTPdA88zCgKrlG0A==",
"type": "Package",
......
......@@ -372,7 +372,9 @@ End Module</File>
Test(text, expected)
End Sub
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSpellcheck)>
<ConditionalFact(GetType(x86))>
<Trait(Traits.Feature, Traits.Features.CodeActionsSpellcheck)>
<WorkItem(5391, "https://github.com/dotnet/roslyn/issues/5391")>
Public Sub SuggestEscapedPredefinedTypes()
Dim text = <File>
Imports System
......
......@@ -21,12 +21,16 @@ internal class DiagnosticAnalyzerLogger
private const string AnalyzerException = "Analyzer.Exception";
private const string AnalyzerExceptionHashCode = "Analyzer.ExceptionHashCode";
private static readonly SHA256CryptoServiceProvider s_sha256CryptoServiceProvider = new SHA256CryptoServiceProvider();
private static readonly SHA256CryptoServiceProvider s_sha256CryptoServiceProvider = GetSha256CryptoServiceProvider();
private static readonly ConditionalWeakTable<DiagnosticAnalyzer, StrongBox<bool>> s_telemetryCache = new ConditionalWeakTable<DiagnosticAnalyzer, StrongBox<bool>>();
private static string ComputeSha256Hash(string name)
{
if (s_sha256CryptoServiceProvider == null)
{
return "Hash Provider Not Available";
}
byte[] hash = s_sha256CryptoServiceProvider.ComputeHash(Encoding.UTF8.GetBytes(name));
return Convert.ToBase64String(hash);
}
......@@ -170,5 +174,18 @@ private static bool CheckTelemetry(DiagnosticAnalyzerService service, Diagnostic
DiagnosticDescriptor diagnostic = diagDescriptors.Length > 0 ? diagDescriptors[0] : null;
return diagnostic == null ? false : diagnostic.CustomTags.Any(t => t == WellKnownDiagnosticTags.Telemetry);
}
private static SHA256CryptoServiceProvider GetSha256CryptoServiceProvider()
{
try
{
// not all environment allows SHA256 encryption
return new SHA256CryptoServiceProvider();
}
catch
{
return null;
}
}
}
}
......@@ -131,7 +131,6 @@
<Compile Include="Extensibility\Interactive\InteractiveEvaluator.cs" />
<Compile Include="Extensibility\Interactive\InteractiveMetadataReferenceResolver.cs" />
<Compile Include="Extensibility\Interactive\CSharpVBInteractiveCommandContentTypes.cs" />
<Compile Include="Extensibility\Interactive\NuGetPackageResolverImpl.cs" />
<Compile Include="Implementation\Completion\InteractiveCommandCompletionService.cs" />
<Compile Include="Implementation\Completion\Presentation\CompletionPresenter.cs" />
<Compile Include="Implementation\Interactive\InertClassifierProvider.cs" />
......
......@@ -25,7 +25,6 @@
using Roslyn.Utilities;
using RuntimeMetadataReferenceResolver = WORKSPACES::Microsoft.CodeAnalysis.Scripting.Hosting.RuntimeMetadataReferenceResolver;
using NuGetPackageResolver = WORKSPACES::Microsoft.CodeAnalysis.Scripting.Hosting.NuGetPackageResolver;
using GacFileResolver = WORKSPACES::Microsoft.CodeAnalysis.Scripting.Hosting.GacFileResolver;
namespace Microsoft.CodeAnalysis.Interactive
......@@ -166,9 +165,13 @@ public void Initialize(Type replServiceProviderType)
private MetadataReferenceResolver CreateMetadataReferenceResolver(ImmutableArray<string> searchPaths, string baseDirectory)
{
var userProfilePath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
var packagesDirectory = string.IsNullOrEmpty(userProfilePath) ?
null :
Path.Combine(userProfilePath, Path.Combine(".nuget", "packages"));
return new RuntimeMetadataReferenceResolver(
new RelativePathResolver(searchPaths, baseDirectory),
null, // TODO
string.IsNullOrEmpty(packagesDirectory) ? null : new NuGetPackageResolverImpl(packagesDirectory),
new GacFileResolver(
architectures: GacFileResolver.Default.Architectures, // TODO (tomat)
preferredCulture: CultureInfo.CurrentCulture), // TODO (tomat)
......
......@@ -10,13 +10,23 @@
using System.Diagnostics;
using System.IO;
using System.Reflection;
using NuGetPackageResolver = WORKSPACES::Microsoft.CodeAnalysis.Scripting.Hosting.NuGetPackageResolver;
namespace Microsoft.CodeAnalysis.Editor.Interactive
namespace Microsoft.CodeAnalysis.Interactive
{
internal sealed class NuGetPackageResolverImpl : WORKSPACES::Microsoft.CodeAnalysis.Scripting.Hosting.NuGetPackageResolver
internal sealed class NuGetPackageResolverImpl : NuGetPackageResolver
{
private const string ProjectJsonFramework = "net46";
private const string ProjectLockJsonFramework = ".NETFramework,Version=v4.6";
private const string EmptyNuGetConfig =
@"<?xml version=""1.0"" encoding=""utf-8""?>
<configuration>
<packageRestore>
<add key=""enabled"" value=""True"" />
<add key=""automatic"" value=""False"" />
</packageRestore>
<packageSources/>
</configuration>";
private readonly string _packagesDirectory;
private readonly Action<ProcessStartInfo> _restore;
......@@ -28,35 +38,39 @@ internal NuGetPackageResolverImpl(string packagesDirectory, Action<ProcessStartI
_restore = restore ?? NuGetRestore;
}
internal override ImmutableArray<string> ResolveNuGetPackage(string reference)
internal new static bool TryParsePackageReference(string reference, out string name, out string version)
{
string packageName;
string packageVersion;
if (!ParsePackageReference(reference, out packageName, out packageVersion))
{
return default(ImmutableArray<string>);
}
return NuGetPackageResolver.TryParsePackageReference(reference, out name, out version);
}
internal override ImmutableArray<string> ResolveNuGetPackage(string packageName, string packageVersion)
{
try
{
var tempPath = PathUtilities.CombineAbsoluteAndRelativePaths(Path.GetTempPath(), Guid.NewGuid().ToString("D"));
var tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("D"));
var tempDir = Directory.CreateDirectory(tempPath);
try
{
// Create project.json.
var projectJson = PathUtilities.CombineAbsoluteAndRelativePaths(tempPath, "project.json");
using (var stream = File.OpenWrite(projectJson))
var projectJsonPath = Path.Combine(tempPath, "project.json");
using (var stream = File.OpenWrite(projectJsonPath))
using (var writer = new StreamWriter(stream))
{
WriteProjectJson(writer, packageName, packageVersion);
}
// Run "nuget.exe restore project.json" to generate project.lock.json.
NuGetRestore(projectJson);
// Create nuget.config with no package sources so restore
// uses the local cache only, no downloading.
var configPath = Path.Combine(tempPath, "nuget.config");
File.WriteAllText(configPath, EmptyNuGetConfig);
// Run "nuget.exe restore project.json -configfile nuget.config"
// to generate project.lock.json.
NuGetRestore(projectJsonPath, configPath);
// Read the references from project.lock.json.
var projectLockJson = PathUtilities.CombineAbsoluteAndRelativePaths(tempPath, "project.lock.json");
using (var stream = File.OpenRead(projectLockJson))
var projectLockJsonPath = Path.Combine(tempPath, "project.lock.json");
using (var stream = File.OpenRead(projectLockJsonPath))
using (var reader = new StreamReader(stream))
{
return ReadProjectLockJson(_packagesDirectory, reader);
......@@ -76,25 +90,6 @@ internal override ImmutableArray<string> ResolveNuGetPackage(string reference)
return default(ImmutableArray<string>);
}
/// <summary>
/// Syntax is "id/version", matching references in project.lock.json.
/// </summary>
internal static bool ParsePackageReference(string reference, out string name, out string version)
{
var parts = reference.Split('/');
if ((parts.Length == 2) &&
(parts[0].Length > 0) &&
(parts[1].Length > 0))
{
name = parts[0];
version = parts[1];
return true;
}
name = null;
version = null;
return false;
}
/// <summary>
/// Generate a project.json file with the packages as "dependencies".
/// </summary>
......@@ -139,7 +134,7 @@ internal static ImmutableArray<string> ReadProjectLockJson(string packagesDirect
{
foreach (var package in (JObject)target.Value)
{
var packageRoot = PathUtilities.CombineAbsoluteAndRelativePaths(packagesDirectory, package.Key);
var packageRoot = Path.Combine(packagesDirectory, package.Key);
var runtime = (JObject)GetPropertyValue((JObject)package.Value, "runtime");
if (runtime == null)
{
......@@ -147,8 +142,14 @@ internal static ImmutableArray<string> ReadProjectLockJson(string packagesDirect
}
foreach (var item in runtime)
{
var path = PathUtilities.CombinePossiblyRelativeAndRelativePaths(packageRoot, item.Key);
builder.Add(path);
var relativePath = item.Key;
// Ignore placeholder "_._" files.
var name = Path.GetFileName(relativePath);
if (string.Equals(name, "_._", StringComparison.InvariantCulture))
{
continue;
}
builder.Add(Path.Combine(packageRoot, relativePath));
}
}
break;
......@@ -164,17 +165,17 @@ private static JToken GetPropertyValue(JObject obj, string propertyName)
return value;
}
private void NuGetRestore(string projectJsonPath)
private void NuGetRestore(string projectJsonPath, string configPath)
{
// Load nuget.exe from same directory as current assembly.
var nugetExePath = PathUtilities.CombineAbsoluteAndRelativePaths(
PathUtilities.GetDirectoryName(
var nugetExePath = Path.Combine(
Path.GetDirectoryName(
CorLightup.Desktop.GetAssemblyLocation(typeof(NuGetPackageResolverImpl).GetTypeInfo().Assembly)),
"nuget.exe");
var startInfo = new ProcessStartInfo()
{
FileName = nugetExePath,
Arguments = $"restore \"{projectJsonPath}\" -PackagesDirectory \"{_packagesDirectory}\"",
Arguments = $"restore \"{projectJsonPath}\" -ConfigFile \"{configPath}\" -PackagesDirectory \"{_packagesDirectory}\"",
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardOutput = true,
......
......@@ -94,6 +94,7 @@
<Compile Include="Interactive\Core\HostObject\SearchPaths.cs" />
<Compile Include="Interactive\Core\SerializableAssemblyLoadResult.cs" />
<Compile Include="Interactive\Core\HostObject\SynchronizedVersionedList.cs" />
<Compile Include="Interactive\Core\NuGetPackageResolverImpl.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="Interactive\Core\InteractiveHost.LazyRemoteService.cs" />
......
{
"dependencies": {
"Microsoft.Composition": "1.0.27",
"Newtonsoft.Json": "6.0.4",
"System.Collections": "4.0.10",
"System.Diagnostics.Debug": "4.0.10",
"System.Globalization": "4.0.10",
......
......@@ -28,6 +28,14 @@
"lib/net45/_._": {}
}
},
"Newtonsoft.Json/6.0.4": {
"compile": {
"lib/net45/Newtonsoft.Json.dll": {}
},
"runtime": {
"lib/net45/Newtonsoft.Json.dll": {}
}
},
"System.AppContext/4.0.0": {
"dependencies": {
"System.Runtime": "[4.0.0, )"
......@@ -377,6 +385,31 @@
"runtimes/aot/lib/netcore50/System.Xml.Serialization.dll"
]
},
"Newtonsoft.Json/6.0.4": {
"sha512": "FyQLmEpjsCrEP+znauLDGAi+h6i9YnaMkITlfIoiM4RYyX3nki306bTHsr/0okiIvIc7BJhQTbOAIZVocccFUw==",
"type": "Package",
"files": [
"[Content_Types].xml",
"_rels/.rels",
"lib/net20/Newtonsoft.Json.dll",
"lib/net20/Newtonsoft.Json.xml",
"lib/net35/Newtonsoft.Json.dll",
"lib/net35/Newtonsoft.Json.xml",
"lib/net40/Newtonsoft.Json.dll",
"lib/net40/Newtonsoft.Json.xml",
"lib/net45/Newtonsoft.Json.dll",
"lib/net45/Newtonsoft.Json.xml",
"lib/netcore45/Newtonsoft.Json.dll",
"lib/netcore45/Newtonsoft.Json.xml",
"lib/portable-net40+sl5+wp80+win8+wpa81/Newtonsoft.Json.dll",
"lib/portable-net40+sl5+wp80+win8+wpa81/Newtonsoft.Json.xml",
"lib/portable-net45+wp80+win8+wpa81/Newtonsoft.Json.dll",
"lib/portable-net45+wp80+win8+wpa81/Newtonsoft.Json.xml",
"Newtonsoft.Json.nuspec",
"package/services/metadata/core-properties/87a0a4e28d50417ea282e20f81bc6477.psmdcp",
"tools/install.ps1"
]
},
"System.AppContext/4.0.0": {
"sha512": "gUoYgAWDC3+xhKeU5KSLbYDhTdBYk9GssrMSCcWUADzOglW+s0AmwVhOUGt2tL5xUl7ZXoYTPdA88zCgKrlG0A==",
"type": "Package",
......@@ -1164,6 +1197,7 @@
"projectFileDependencyGroups": {
"": [
"Microsoft.Composition >= 1.0.27",
"Newtonsoft.Json >= 6.0.4",
"System.Collections >= 4.0.10",
"System.Diagnostics.Debug >= 4.0.10",
"System.Globalization >= 4.0.10",
......
......@@ -28,6 +28,14 @@
"lib/net45/_._": {}
}
},
"Newtonsoft.Json/6.0.4": {
"compile": {
"lib/net45/Newtonsoft.Json.dll": {}
},
"runtime": {
"lib/net45/Newtonsoft.Json.dll": {}
}
},
"System.AppContext/4.0.0": {
"dependencies": {
"System.Runtime": "[4.0.0, )"
......@@ -287,6 +295,14 @@
"lib/net45/_._": {}
}
},
"Newtonsoft.Json/6.0.4": {
"compile": {
"lib/net45/Newtonsoft.Json.dll": {}
},
"runtime": {
"lib/net45/Newtonsoft.Json.dll": {}
}
},
"System.AppContext/4.0.0": {
"dependencies": {
"System.Runtime": "[4.0.0, )"
......@@ -636,6 +652,31 @@
"runtimes/aot/lib/netcore50/System.Xml.Serialization.dll"
]
},
"Newtonsoft.Json/6.0.4": {
"sha512": "FyQLmEpjsCrEP+znauLDGAi+h6i9YnaMkITlfIoiM4RYyX3nki306bTHsr/0okiIvIc7BJhQTbOAIZVocccFUw==",
"type": "Package",
"files": [
"[Content_Types].xml",
"_rels/.rels",
"lib/net20/Newtonsoft.Json.dll",
"lib/net20/Newtonsoft.Json.xml",
"lib/net35/Newtonsoft.Json.dll",
"lib/net35/Newtonsoft.Json.xml",
"lib/net40/Newtonsoft.Json.dll",
"lib/net40/Newtonsoft.Json.xml",
"lib/net45/Newtonsoft.Json.dll",
"lib/net45/Newtonsoft.Json.xml",
"lib/netcore45/Newtonsoft.Json.dll",
"lib/netcore45/Newtonsoft.Json.xml",
"lib/portable-net40+sl5+wp80+win8+wpa81/Newtonsoft.Json.dll",
"lib/portable-net40+sl5+wp80+win8+wpa81/Newtonsoft.Json.xml",
"lib/portable-net45+wp80+win8+wpa81/Newtonsoft.Json.dll",
"lib/portable-net45+wp80+win8+wpa81/Newtonsoft.Json.xml",
"Newtonsoft.Json.nuspec",
"package/services/metadata/core-properties/87a0a4e28d50417ea282e20f81bc6477.psmdcp",
"tools/install.ps1"
]
},
"System.AppContext/4.0.0": {
"sha512": "gUoYgAWDC3+xhKeU5KSLbYDhTdBYk9GssrMSCcWUADzOglW+s0AmwVhOUGt2tL5xUl7ZXoYTPdA88zCgKrlG0A==",
"type": "Package",
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.CodeAnalysis.Editor.Interactive;
using Microsoft.CodeAnalysis.Interactive;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Roslyn.Utilities;
......@@ -13,6 +13,9 @@ namespace Microsoft.CodeAnalysis.UnitTests.Interactive
{
public class NuGetPackageResolverTests : TestBase
{
/// <summary>
/// Valid reference.
/// </summary>
[ConditionalFact(typeof(WindowsOnly))]
public void ResolveReference()
{
......@@ -25,6 +28,15 @@ public void ResolveReference()
""net46"": {}
}
}";
var expectedConfig =
@"<?xml version=""1.0"" encoding=""utf-8""?>
<configuration>
<packageRestore>
<add key=""enabled"" value=""True"" />
<add key=""automatic"" value=""False"" />
</packageRestore>
<packageSources/>
</configuration>";
var actualProjectLockJson =
@"{
""locked"": false,
......@@ -48,6 +60,11 @@ public void ResolveReference()
""System.Runtime"": """"
},
},
""System.Runtime/4.0.0"": {
""runtime"": {
""ref/dotnet/_._"": {}
}
},
""System.IO/4.0.10"": {
""dependencies"": {},
""runtime"": {
......@@ -65,23 +82,32 @@ public void ResolveReference()
packagesDirectory,
startInfo =>
{
// Verify arguments.
var arguments = startInfo.Arguments.Split('"');
Assert.Equal(5, arguments.Length);
Assert.Equal(7, arguments.Length);
Assert.Equal("restore ", arguments[0]);
Assert.Equal("project.json", PathUtilities.GetFileName(arguments[1]));
Assert.Equal(" -PackagesDirectory ", arguments[2]);
Assert.Equal(packagesDirectory, arguments[3]);
Assert.Equal("", arguments[4]);
Assert.Equal(" -ConfigFile ", arguments[2]);
Assert.Equal("nuget.config", PathUtilities.GetFileName(arguments[3]));
Assert.Equal(" -PackagesDirectory ", arguments[4]);
Assert.Equal(packagesDirectory, arguments[5]);
Assert.Equal("", arguments[6]);
// Verify project.json contents.
var projectJsonPath = arguments[1];
var actualProjectJson = File.ReadAllText(projectJsonPath);
Assert.Equal(expectedProjectJson, actualProjectJson);
// Verify config file contents.
var configPath = arguments[3];
var actualConfig = File.ReadAllText(configPath);
Assert.Equal(expectedConfig, actualConfig);
// Generate project.lock.json.
var projectLockJsonPath = PathUtilities.CombineAbsoluteAndRelativePaths(PathUtilities.GetDirectoryName(projectJsonPath), "project.lock.json");
using (var writer = new StreamWriter(projectLockJsonPath))
{
writer.Write(actualProjectLockJson);
}
});
var actualPaths = resolver.ResolveNuGetPackage("A.B.C/1.2");
var actualPaths = resolver.ResolveNuGetPackage("A.B.C", "1.2");
AssertEx.SetEqual(actualPaths,
PathUtilities.CombineAbsoluteAndRelativePaths(packagesDirectory, PathUtilities.CombinePossiblyRelativeAndRelativePaths("System.Collections/4.0.10", "ref/dotnet/System.Collections.dll")),
PathUtilities.CombineAbsoluteAndRelativePaths(packagesDirectory, PathUtilities.CombinePossiblyRelativeAndRelativePaths("System.IO/4.0.10", "ref/dotnet/System.Runtime.dll")),
......@@ -89,24 +115,34 @@ public void ResolveReference()
}
}
/// <summary>
/// Expected exception thrown during restore.
/// </summary>
[ConditionalFact(typeof(WindowsOnly))]
public void HandledException()
{
using (var directory = new DisposableDirectory(Temp))
{
var resolver = new NuGetPackageResolverImpl(directory.Path, startInfo => { throw new IOException(); });
var actualPaths = resolver.ResolveNuGetPackage("A.B.C/1.2");
bool restored = false;
var resolver = new NuGetPackageResolverImpl(directory.Path, startInfo => { restored = true; throw new IOException(); });
var actualPaths = resolver.ResolveNuGetPackage("A.B.C", "1.2");
Assert.True(actualPaths.IsDefault);
Assert.True(restored);
}
}
/// <summary>
/// Unexpected exception thrown during restore.
/// </summary>
[ConditionalFact(typeof(WindowsOnly))]
public void UnhandledException()
{
using (var directory = new DisposableDirectory(Temp))
{
var resolver = new NuGetPackageResolverImpl(directory.Path, startInfo => { throw new InvalidOperationException(); });
Assert.Throws<InvalidOperationException>(() => resolver.ResolveNuGetPackage("A.B.C/1.2"));
bool restored = false;
var resolver = new NuGetPackageResolverImpl(directory.Path, startInfo => { restored = true; throw new InvalidOperationException(); });
Assert.Throws<InvalidOperationException>(() => resolver.ResolveNuGetPackage("A.B.C", "1.2"));
Assert.True(restored);
}
}
......@@ -114,24 +150,31 @@ public void UnhandledException()
public void ParsePackageNameAndVersion()
{
ParseInvalidPackageReference("A");
ParseInvalidPackageReference("A.B");
ParseInvalidPackageReference("A/");
ParseInvalidPackageReference("A//1.0");
ParseInvalidPackageReference("/1.0.0");
ParseInvalidPackageReference("A/B/2.0.0");
ParseInvalidPackageReference("A/1");
ParseInvalidPackageReference("nuget");
ParseInvalidPackageReference("nuget:");
ParseInvalidPackageReference("NUGET:");
ParseInvalidPackageReference("nugetA/1");
ParseInvalidPackageReference("nuget:A");
ParseInvalidPackageReference("nuget:A.B");
ParseInvalidPackageReference("nuget:A/");
ParseInvalidPackageReference("nuget:A//1.0");
ParseInvalidPackageReference("nuget:/1.0.0");
ParseInvalidPackageReference("nuget:A/B/2.0.0");
ParseValidPackageReference("A/1", "A", "1");
ParseValidPackageReference("A.B/1.0.0", "A.B", "1.0.0");
ParseValidPackageReference("A/B.C", "A", "B.C");
ParseValidPackageReference(" /1", " ", "1");
ParseValidPackageReference("A\t/\n1.0\r ", "A\t", "\n1.0\r ");
ParseValidPackageReference("nuget::nuget/1", ":nuget", "1");
ParseValidPackageReference("nuget:A/1", "A", "1");
ParseValidPackageReference("nuget:A.B/1.0.0", "A.B", "1.0.0");
ParseValidPackageReference("nuget:A/B.C", "A", "B.C");
ParseValidPackageReference("nuget: /1", " ", "1");
ParseValidPackageReference("nuget:A\t/\n1.0\r ", "A\t", "\n1.0\r ");
}
private static void ParseValidPackageReference(string reference, string expectedName, string expectedVersion)
{
string name;
string version;
Assert.True(NuGetPackageResolverImpl.ParsePackageReference(reference, out name, out version));
Assert.True(NuGetPackageResolverImpl.TryParsePackageReference(reference, out name, out version));
Assert.Equal(expectedName, name);
Assert.Equal(expectedVersion, version);
}
......@@ -140,7 +183,7 @@ private static void ParseInvalidPackageReference(string reference)
{
string name;
string version;
Assert.False(NuGetPackageResolverImpl.ParsePackageReference(reference, out name, out version));
Assert.False(NuGetPackageResolverImpl.TryParsePackageReference(reference, out name, out version));
Assert.Null(name);
Assert.Null(version);
}
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Text;
namespace Microsoft.VisualStudio.InteractiveWindow
{
/// <summary>
/// REPL session buffer: input, output, or prompt.
/// </summary>
[DataContract]
internal struct BufferBlock
{
[DataMember(Name = "kind")]
internal readonly ReplSpanKind Kind;
[DataMember(Name = "content")]
internal readonly string Content;
internal BufferBlock(ReplSpanKind kind, string content)
{
Kind = kind;
Content = content;
}
internal static string Serialize(BufferBlock[] blocks)
{
var serializer = new DataContractJsonSerializer(typeof(BufferBlock[]));
using (var stream = new MemoryStream())
{
serializer.WriteObject(stream, blocks);
return Encoding.UTF8.GetString(stream.GetBuffer(), 0, (int)stream.Length);
}
}
internal static BufferBlock[] Deserialize(string str)
{
var serializer = new DataContractJsonSerializer(typeof(BufferBlock[]));
var bytes = Encoding.UTF8.GetBytes(str);
using (var stream = new MemoryStream(bytes))
{
var obj = serializer.ReadObject(stream);
return (BufferBlock[])obj;
}
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Runtime.Serialization;
namespace Microsoft.VisualStudio.InteractiveWindow
{
internal partial class InteractiveWindow
[DataContract]
internal enum ReplSpanKind
{
private enum ReplSpanKind
{
/// <summary>
/// Primary, secondary, or standard input prompt.
/// </summary>
Prompt,
/// <summary>
/// Primary, secondary, or standard input prompt.
/// </summary>
[EnumMember]
Prompt = 0,
/// <summary>
/// Line break inserted at end of output.
/// </summary>
LineBreak,
/// <summary>
/// The span represents output from the program (standard output).
/// </summary>
[EnumMember]
Output = 1,
/// <summary>
/// The span represents output from the program (standard output).
/// </summary>
Output,
/// <summary>
/// The span represents code inputted after a prompt or secondary prompt.
/// </summary>
[EnumMember]
Input = 2,
/// <summary>
/// The span represents code inputted after a prompt or secondary prompt.
/// </summary>
Language,
/// <summary>
/// The span represents the input for a standard input (non code input).
/// </summary>
[EnumMember]
StandardInput = 3,
/// <summary>
/// The span represents the input for a standard input (non code input).
/// </summary>
StandardInput,
}
/// <summary>
/// Line break inserted at end of output.
/// </summary>
[EnumMember]
LineBreak = 4,
}
}
\ No newline at end of file
......@@ -213,7 +213,7 @@ public async Task<ExecutionResult> ResetAsync(bool initialize)
{
var snapshot = _projectionBuffer.CurrentSnapshot;
var spanCount = snapshot.SpanCount;
Debug.Assert(GetSpanKind(snapshot.GetSourceSpan(spanCount - 1)) == ReplSpanKind.Language);
Debug.Assert(GetSpanKind(snapshot.GetSourceSpan(spanCount - 1)) == ReplSpanKind.Input);
StoreUncommittedInput();
RemoveProjectionSpans(spanCount - 2, 2);
CurrentLanguageBuffer = null;
......@@ -356,7 +356,7 @@ public async Task<TextReader> ReadStandardInputAsync()
{
var snapshot = _projectionBuffer.CurrentSnapshot;
var spanCount = snapshot.SpanCount;
if (spanCount > 0 && GetSpanKind(snapshot.GetSourceSpan(spanCount - 1)) == ReplSpanKind.Language)
if (spanCount > 0 && GetSpanKind(snapshot.GetSourceSpan(spanCount - 1)) == ReplSpanKind.Input)
{
// we need to remove our input prompt.
RemoveLastInputPrompt();
......@@ -602,6 +602,22 @@ public bool Paste()
{
InsertCode(format);
}
else if (Clipboard.ContainsData(ClipboardFormat))
{
var blocks = BufferBlock.Deserialize((string)Clipboard.GetData(ClipboardFormat));
// Paste each block separately.
foreach (var block in blocks)
{
switch (block.Kind)
{
case ReplSpanKind.Input:
case ReplSpanKind.Output:
case ReplSpanKind.StandardInput:
InsertCode(block.Content);
break;
}
}
}
else if (Clipboard.ContainsText())
{
InsertCode(Clipboard.GetText());
......@@ -675,7 +691,7 @@ private void AppendInput(string text)
var snapshot = _projectionBuffer.CurrentSnapshot;
var spanCount = snapshot.SpanCount;
var inputSpan = snapshot.GetSourceSpan(spanCount - 1);
Debug.Assert(GetSpanKind(inputSpan) == ReplSpanKind.Language ||
Debug.Assert(GetSpanKind(inputSpan) == ReplSpanKind.Input ||
GetSpanKind(inputSpan) == ReplSpanKind.StandardInput);
var buffer = inputSpan.Snapshot.TextBuffer;
......@@ -1112,7 +1128,7 @@ private ITextBuffer GetLanguageBuffer(SnapshotPoint point)
// Grab the span following the prompt (either language or standard input).
var projectionSpan = sourceSpans[promptIndex + 1];
var kind = GetSpanKind(projectionSpan);
if (kind != ReplSpanKind.Language)
if (kind != ReplSpanKind.Input)
{
Debug.Assert(kind == ReplSpanKind.StandardInput);
return null;
......@@ -1579,7 +1595,7 @@ private int GetProjectionSpanIndexFromEditableBufferPosition(IProjectionSnapshot
// and ending at the end of the projection buffer, each language buffer projection is on a separate line:
// [prompt)[language)...[prompt)[language)<end of projection buffer>
int result = projectionSpansCount - (surfaceSnapshot.LineCount - surfaceLineNumber) * SpansPerLineOfInput + 1;
Debug.Assert(GetSpanKind(surfaceSnapshot.GetSourceSpan(result)) == ReplSpanKind.Language);
Debug.Assert(GetSpanKind(surfaceSnapshot.GetSourceSpan(result)) == ReplSpanKind.Input);
return result;
}
......@@ -2071,11 +2087,11 @@ public void SelectAll()
var inputSnapshot = projectionSpan.Snapshot;
var kind = GetSpanKind(projectionSpan);
Debug.Assert(kind == ReplSpanKind.Language || kind == ReplSpanKind.StandardInput);
Debug.Assert(kind == ReplSpanKind.Input || kind == ReplSpanKind.StandardInput);
// Language input block is a projection of the entire snapshot;
// std input block is a projection of a single span:
SnapshotPoint inputBufferEnd = (kind == ReplSpanKind.Language) ?
SnapshotPoint inputBufferEnd = (kind == ReplSpanKind.Input) ?
new SnapshotPoint(inputSnapshot, inputSnapshot.Length) :
projectionSpan.End;
......@@ -2441,17 +2457,45 @@ private static NormalizedSnapshotSpanCollection GetSelectionSpans(ITextView text
private DataObject Copy(NormalizedSnapshotSpanCollection spans)
{
var text = spans.Aggregate(new StringBuilder(), GetTextWithoutPrompts, b => b.ToString());
var text = GetText(spans);
var blocks = GetTextBlocks(spans);
var rtf = _rtfBuilderService.GenerateRtf(spans, TextView);
var data = new DataObject();
data.SetData(DataFormats.StringFormat, text);
data.SetData(DataFormats.Text, text);
data.SetData(DataFormats.UnicodeText, text);
data.SetData(DataFormats.Rtf, rtf);
data.SetData(ClipboardFormat, blocks);
return data;
}
private StringBuilder GetTextWithoutPrompts(StringBuilder builder, SnapshotSpan span)
/// <summary>
/// Get the text of the given spans as a simple concatenated string.
/// </summary>
private static string GetText(NormalizedSnapshotSpanCollection spans)
{
var builder = new StringBuilder();
foreach (var span in spans)
{
builder.Append(span.GetText());
}
return builder.ToString();
}
/// <summary>
/// Get the text of the given spans as a serialized BufferBlock[].
/// </summary>
private string GetTextBlocks(NormalizedSnapshotSpanCollection spans)
{
var blocks = new List<BufferBlock>();
foreach (var span in spans)
{
GetTextBlocks(blocks, span);
}
return BufferBlock.Serialize(blocks.ToArray());
}
private void GetTextBlocks(List<BufferBlock> blocks, SnapshotSpan span)
{
// Find the range of source spans that cover the span.
var sourceSpans = GetSourceSpans(span.Snapshot);
......@@ -2462,7 +2506,6 @@ private StringBuilder GetTextWithoutPrompts(StringBuilder builder, SnapshotSpan
index--;
}
// Add the text for all non-prompt spans within the range.
for (; index < n; index++)
{
var sourceSpan = sourceSpans[index];
......@@ -2470,28 +2513,29 @@ private StringBuilder GetTextWithoutPrompts(StringBuilder builder, SnapshotSpan
{
continue;
}
if (!IsPrompt(sourceSpan))
var sourceSnapshot = sourceSpan.Snapshot;
var mappedSpans = TextView.BufferGraph.MapDownToBuffer(span, SpanTrackingMode.EdgeExclusive, sourceSnapshot.TextBuffer);
bool added = false;
foreach (var mappedSpan in mappedSpans)
{
var sourceSnapshot = sourceSpan.Snapshot;
var mappedSpans = TextView.BufferGraph.MapDownToBuffer(span, SpanTrackingMode.EdgeExclusive, sourceSnapshot.TextBuffer);
bool added = false;
foreach (var mappedSpan in mappedSpans)
var intersection = sourceSpan.Span.Intersection(mappedSpan);
if (intersection.HasValue && !intersection.Value.IsEmpty)
{
var intersection = sourceSpan.Span.Intersection(mappedSpan);
if (intersection.HasValue)
var kind = GetSpanKind(span);
if (kind == ReplSpanKind.LineBreak)
{
builder.Append(sourceSnapshot.GetText(intersection.Value));
added = true;
kind = ReplSpanKind.Output;
}
var content = sourceSnapshot.GetText(intersection.Value);
blocks.Add(new BufferBlock(kind, content));
added = true;
}
if (!added)
{
break;
}
}
if (!added)
{
break;
}
}
return builder;
}
/// <summary>Implements <see cref="IInteractiveWindowOperations.Backspace"/>.</summary>
......@@ -2795,7 +2839,7 @@ private ReplSpanKind GetSpanKind(SnapshotSpan span)
ReplSpanKind.LineBreak :
ReplSpanKind.Prompt;
}
return ReplSpanKind.Language;
return ReplSpanKind.Input;
}
#region Output
......
......@@ -33,6 +33,8 @@ namespace Microsoft.VisualStudio.InteractiveWindow
/// </summary>
internal partial class InteractiveWindow : IInteractiveWindow, IInteractiveWindowOperations2
{
internal const string ClipboardFormat = "89344A36-9821-495A-8255-99A63969F87D";
public event EventHandler<SubmissionBufferAddedEventArgs> SubmissionBufferAdded;
PropertyCollection IPropertyOwner.Properties { get; } = new PropertyCollection();
......
......@@ -56,8 +56,7 @@
<Reference Include="System" />
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Xaml" />
<Reference Include="System.XML" />
<Reference Include="WindowsBase" />
......@@ -66,6 +65,7 @@
<Content Include="Resources\ReplToolBarImages.bmp" />
</ItemGroup>
<ItemGroup>
<Compile Include="BufferBlock.cs" />
<Compile Include="Commands\CancelExecutionCommand.cs" />
<Compile Include="Commands\ClearScreenCommand.cs" />
<Compile Include="Commands\CommandClassifier.cs" />
......
......@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using Microsoft.VisualStudio.InteractiveWindow.Commands;
......@@ -586,7 +587,7 @@ public void CopyWithinInput()
Window.InsertCode("1 + 2");
Window.Operations.SelectAll();
Window.Operations.Copy();
VerifyClipboardData("1 + 2");
VerifyClipboardData("1 + 2", "1 + 2", @"[{""content"":""1 + 2"",""kind"":2}]");
// Shrink the selection.
var selection = Window.TextView.Selection;
......@@ -594,7 +595,7 @@ public void CopyWithinInput()
selection.Select(new SnapshotSpan(span.Snapshot, span.Start + 1, span.Length - 2), isReversed: false);
Window.Operations.Copy();
VerifyClipboardData(" + ");
VerifyClipboardData(" + ", " + ", @"[{""content"":"" + "",""kind"":2}]");
}
[Fact]
......@@ -616,13 +617,14 @@ public void CopyInputAndOutput()
Window.Operations.SelectAll();
Window.Operations.SelectAll();
Window.Operations.Copy();
VerifyClipboardData(@"foreach (var o in new[] { 1, 2, 3 })
System.Console.WriteLine();
VerifyClipboardData(@"> foreach (var o in new[] { 1, 2, 3 })
> System.Console.WriteLine();
1
2
3
",
@"> foreach (var o in new[] \{ 1, 2, 3 \})\par > System.Console.WriteLine();\par 1\par 2\par 3\par > ");
> ",
@"> foreach (var o in new[] \{ 1, 2, 3 \})\par > System.Console.WriteLine();\par 1\par 2\par 3\par > ",
@"[{""content"":""> "",""kind"":2},{""content"":""foreach (var o in new[] { 1, 2, 3 })\u000d\u000a"",""kind"":2},{""content"":""> "",""kind"":2},{""content"":""System.Console.WriteLine();\u000d\u000a"",""kind"":2},{""content"":""1\u000d\u000a2\u000d\u000a3\u000d\u000a"",""kind"":2},{""content"":""> "",""kind"":2}]");
// Shrink the selection.
var selection = Window.TextView.Selection;
......@@ -631,11 +633,12 @@ public void CopyInputAndOutput()
Window.Operations.Copy();
VerifyClipboardData(@"oreach (var o in new[] { 1, 2, 3 })
System.Console.WriteLine();
> System.Console.WriteLine();
1
2
3",
@"oreach (var o in new[] \{ 1, 2, 3 \})\par > System.Console.WriteLine();\par 1\par 2\par 3");
@"oreach (var o in new[] \{ 1, 2, 3 \})\par > System.Console.WriteLine();\par 1\par 2\par 3",
@"[{""content"":""oreach (var o in new[] { 1, 2, 3 })\u000d\u000a"",""kind"":2},{""content"":""> "",""kind"":2},{""content"":""System.Console.WriteLine();\u000d\u000a"",""kind"":2},{""content"":""1\u000d\u000a2\u000d\u000a3"",""kind"":2}]");
}
[Fact]
......@@ -662,7 +665,8 @@ public void CutWithinInput()
VerifyClipboardData(
@"each (var o in new[] { 1, 2, 3 })
System.Console.WriteLine()",
expectedRtf: null);
expectedRtf: null,
expectedRepl: null);
}
[Fact]
......@@ -684,7 +688,7 @@ public void CutInputAndOutput()
Window.Operations.SelectAll();
Window.Operations.SelectAll();
Window.Operations.Cut();
VerifyClipboardData(null);
VerifyClipboardData(null, null, null);
}
/// <summary>
......@@ -701,15 +705,15 @@ public void CopyNoSelection()
@" 1
2 ");
CopyNoSelectionAndVerify(0, 7, "s +\r\n", @"> s +\par ");
CopyNoSelectionAndVerify(7, 11, "\r\n", @"> \par ");
CopyNoSelectionAndVerify(11, 17, " t\r\n", @"> t\par ");
CopyNoSelectionAndVerify(17, 21, " 1\r\n", @" 1\par ");
CopyNoSelectionAndVerify(21, 23, "\r\n", @"\par ");
CopyNoSelectionAndVerify(23, 28, "2 ", "2 > ");
CopyNoSelectionAndVerify(0, 7, "> s +\r\n", @"> s +\par ", @"[{""content"":""> "",""kind"":2},{""content"":""s +\u000d\u000a"",""kind"":2}]");
CopyNoSelectionAndVerify(7, 11, "> \r\n", @"> \par ", @"[{""content"":""> "",""kind"":2},{""content"":""\u000d\u000a"",""kind"":2}]");
CopyNoSelectionAndVerify(11, 17, "> t\r\n", @"> t\par ", @"[{""content"":""> "",""kind"":2},{""content"":"" t\u000d\u000a"",""kind"":2}]");
CopyNoSelectionAndVerify(17, 21, " 1\r\n", @" 1\par ", @"[{""content"":"" 1\u000d\u000a"",""kind"":2}]");
CopyNoSelectionAndVerify(21, 23, "\r\n", @"\par ", @"[{""content"":""\u000d\u000a"",""kind"":2}]");
CopyNoSelectionAndVerify(23, 28, "2 > ", "2 > ", @"[{""content"":""2 "",""kind"":2},{""content"":""> "",""kind"":2}]");
}
private void CopyNoSelectionAndVerify(int start, int end, string expectedText, string expectedRtf)
private void CopyNoSelectionAndVerify(int start, int end, string expectedText, string expectedRtf, string expectedRepl)
{
var caret = Window.TextView.Caret;
var snapshot = Window.TextView.TextBuffer.CurrentSnapshot;
......@@ -718,7 +722,81 @@ private void CopyNoSelectionAndVerify(int start, int end, string expectedText, s
Clipboard.Clear();
caret.MoveTo(new SnapshotPoint(snapshot, i));
Window.Operations.Copy();
VerifyClipboardData(expectedText, expectedRtf);
VerifyClipboardData(expectedText, expectedRtf, expectedRepl);
}
}
[Fact]
public void Paste()
{
var blocks = new[]
{
new BufferBlock(ReplSpanKind.Output, "a\r\nbc"),
new BufferBlock(ReplSpanKind.Prompt, "> "),
new BufferBlock(ReplSpanKind.Prompt, "< "),
new BufferBlock(ReplSpanKind.Input, "12"),
new BufferBlock(ReplSpanKind.StandardInput, "3"),
new BufferBlock((ReplSpanKind)10, "xyz")
};
// Paste from text clipboard format.
CopyToClipboard(blocks, includeRepl: false);
Window.Operations.Paste();
var text = Window.TextView.TextBuffer.CurrentSnapshot.GetText();
Assert.Equal("> a\r\n> bc> < 123xyz", text);
Window.Operations.ClearView();
text = Window.TextView.TextBuffer.CurrentSnapshot.GetText();
Assert.Equal("> ", text);
// Paste from custom clipboard format.
CopyToClipboard(blocks, includeRepl: true);
Window.Operations.Paste();
text = Window.TextView.TextBuffer.CurrentSnapshot.GetText();
Assert.Equal("> a\r\n> bc123", text);
}
private static void CopyToClipboard(BufferBlock[] blocks, bool includeRepl)
{
Clipboard.Clear();
var data = new DataObject();
var builder = new StringBuilder();
foreach (var block in blocks)
{
builder.Append(block.Content);
}
var text = builder.ToString();
data.SetData(DataFormats.UnicodeText, text);
data.SetData(DataFormats.StringFormat, text);
if (includeRepl)
{
data.SetData(InteractiveWindow.ClipboardFormat, BufferBlock.Serialize(blocks));
}
Clipboard.SetDataObject(data, false);
}
[Fact]
public void JsonSerialization()
{
var expectedContent = new []
{
new BufferBlock(ReplSpanKind.Prompt, "> "),
new BufferBlock(ReplSpanKind.Input, "Hello"),
new BufferBlock(ReplSpanKind.Prompt, ". "),
new BufferBlock(ReplSpanKind.StandardInput, "world"),
new BufferBlock(ReplSpanKind.Output, "Hello world"),
};
var actualJson = BufferBlock.Serialize(expectedContent);
var expectedJson = @"[{""content"":""> "",""kind"":0},{""content"":""Hello"",""kind"":2},{""content"":"". "",""kind"":0},{""content"":""world"",""kind"":3},{""content"":""Hello world"",""kind"":1}]";
Assert.Equal(expectedJson, actualJson);
var actualContent = BufferBlock.Deserialize(actualJson);
Assert.Equal(expectedContent.Length, actualContent.Length);
for (int i = 0; i < expectedContent.Length; i++)
{
var expectedBuffer = expectedContent[i];
var actualBuffer = actualContent[i];
Assert.Equal(expectedBuffer.Kind, actualBuffer.Kind);
Assert.Equal(expectedBuffer.Content, actualBuffer.Content);
}
}
......@@ -1051,7 +1129,7 @@ public void CutWithOutSelectionInReadOnlyArea()
Assert.Equal("> 1\r\n1\r\n> 2", Window.TextView.TextBuffer.CurrentSnapshot.GetText());
AssertCaretVirtualPosition(1, 1);
VerifyClipboardData(null);
VerifyClipboardData(null, null, null);
// Cut() with caret in active prompt
caret.MoveToNextCaretPosition();
......@@ -1060,7 +1138,7 @@ public void CutWithOutSelectionInReadOnlyArea()
Assert.Equal("> 1\r\n1\r\n> ", Window.TextView.TextBuffer.CurrentSnapshot.GetText());
AssertCaretVirtualPosition(2, 2);
VerifyClipboardData("2", expectedRtf: null);
VerifyClipboardData("2", expectedRtf: null, expectedRepl: null);
}
[Fact]
......@@ -1089,7 +1167,7 @@ public void CutWithSelectionInReadonlyArea()
Task.Run(() => Window.Operations.Cut()).PumpingWait();
Assert.Equal("> 1\r\n1\r\n> 23", Window.TextView.TextBuffer.CurrentSnapshot.GetText());
VerifyClipboardData(null);
VerifyClipboardData(null, null, null);
// Cut() with selection in active prompt, no-op
selection.Clear();
......@@ -1102,7 +1180,7 @@ public void CutWithSelectionInReadonlyArea()
Task.Run(() => Window.Operations.Cut()).PumpingWait();
Assert.Equal("> 1\r\n1\r\n> 23", Window.TextView.TextBuffer.CurrentSnapshot.GetText());
VerifyClipboardData(null);
VerifyClipboardData(null, null, null);
// Cut() with selection overlaps with editable buffer,
// Cut editable content and move caret to closest editable location
......@@ -1119,7 +1197,7 @@ public void CutWithSelectionInReadonlyArea()
Task.Run(() => Window.Operations.Cut()).PumpingWait();
Assert.Equal("> 1\r\n1\r\n> 3", Window.TextView.TextBuffer.CurrentSnapshot.GetText());
AssertCaretVirtualPosition(2, 2);
VerifyClipboardData("2", expectedRtf: null);
VerifyClipboardData("2", expectedRtf: null, expectedRepl: null);
}
[Fact]
......@@ -1136,7 +1214,7 @@ public void PasteWithOutSelectionInReadOnlyArea()
Clipboard.Clear();
Window.Operations.Home(true);
Window.Operations.Copy();
VerifyClipboardData("2", @"\ansi{\fonttbl{\f0 Consolas;}}{\colortbl;\red0\green0\blue0;\red255\green255\blue255;}\f0 \fs24 \cf1 \cb2 \highlight2 2");
VerifyClipboardData("2", @"\ansi{\fonttbl{\f0 Consolas;}}{\colortbl;\red0\green0\blue0;\red255\green255\blue255;}\f0 \fs24 \cf1 \cb2 \highlight2 2", @"[{""content"":""2"",""kind"":2}]");
// Paste() with caret in readonly area, no-op
Window.TextView.Selection.Clear();
......@@ -1173,7 +1251,7 @@ public void PasteWithSelectionInReadonlyArea()
Clipboard.Clear();
Window.Operations.Home(true);
Window.Operations.Copy();
VerifyClipboardData("23", @"\ansi{\fonttbl{\f0 Consolas;}}{\colortbl;\red0\green0\blue0;\red255\green255\blue255;}\f0 \fs24 \cf1 \cb2 \highlight2 23");
VerifyClipboardData("23", @"\ansi{\fonttbl{\f0 Consolas;}}{\colortbl;\red0\green0\blue0;\red255\green255\blue255;}\f0 \fs24 \cf1 \cb2 \highlight2 23", @"[{""content"":""23"",""kind"":2}]");
// Paste() with selection in readonly area, no-op
......@@ -1231,17 +1309,13 @@ private void Submit(string submission, string output)
}
}
private static void VerifyClipboardData(string expectedText)
{
VerifyClipboardData(expectedText, expectedText);
}
private static void VerifyClipboardData(string expectedText, string expectedRtf)
private static void VerifyClipboardData(string expectedText, string expectedRtf, string expectedRepl)
{
var data = Clipboard.GetDataObject();
Assert.Equal(expectedText, data.GetData(DataFormats.StringFormat));
Assert.Equal(expectedText, data.GetData(DataFormats.Text));
Assert.Equal(expectedText, data.GetData(DataFormats.UnicodeText));
Assert.Equal(expectedRepl, (string)data.GetData(InteractiveWindow.ClipboardFormat));
var actualRtf = (string)data.GetData(DataFormats.Rtf);
if (expectedRtf == null)
{
......
......@@ -127,7 +127,7 @@ public InteractiveAssemblyLoader(MetadataShadowCopyProvider shadowCopyProvider =
internal Assembly Load(Stream peStream, Stream pdbStream)
{
byte[] peImage = new byte[peStream.Length];
peStream.Read(peImage, 0, peImage.Length);
peStream.TryReadAll(peImage, 0, peImage.Length);
var assembly = CorLightup.Desktop.LoadAssembly(peImage);
RegisterDependency(assembly);
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Immutable;
namespace Microsoft.CodeAnalysis.Scripting.Hosting
{
internal abstract class NuGetPackageResolver
{
internal abstract ImmutableArray<string> ResolveNuGetPackage(string reference);
private const string ReferencePrefix = "nuget:";
/// <summary>
/// Syntax is "nuget:id/version".
/// </summary>
internal static bool TryParsePackageReference(string reference, out string name, out string version)
{
if (reference.StartsWith(ReferencePrefix, StringComparison.Ordinal))
{
var parts = reference.Substring(ReferencePrefix.Length).Split('/');
if ((parts.Length == 2) &&
(parts[0].Length > 0) &&
(parts[1].Length > 0))
{
name = parts[0];
version = parts[1];
return true;
}
}
name = null;
version = null;
return false;
}
internal abstract ImmutableArray<string> ResolveNuGetPackage(string packageName, string packageVersion);
}
}
......@@ -2,7 +2,7 @@
using System;
using System.Collections.Immutable;
using System.IO;
using System.Diagnostics;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Scripting.Hosting
......@@ -15,7 +15,7 @@ namespace Microsoft.CodeAnalysis.Scripting.Hosting
/// </remarks>
internal sealed class RuntimeMetadataReferenceResolver : MetadataReferenceResolver, IEquatable<RuntimeMetadataReferenceResolver>
{
public static readonly RuntimeMetadataReferenceResolver Default = new RuntimeMetadataReferenceResolver();
public static readonly RuntimeMetadataReferenceResolver Default = new RuntimeMetadataReferenceResolver(ImmutableArray<string>.Empty, baseDirectory: null);
internal readonly RelativePathResolver PathResolver;
internal readonly NuGetPackageResolver PackageResolver;
......@@ -23,8 +23,8 @@ internal sealed class RuntimeMetadataReferenceResolver : MetadataReferenceResolv
private readonly Func<string, MetadataReferenceProperties, PortableExecutableReference> _fileReferenceProvider;
internal RuntimeMetadataReferenceResolver(
ImmutableArray<string> searchPaths = default(ImmutableArray<string>),
string baseDirectory = null)
ImmutableArray<string> searchPaths,
string baseDirectory)
: this(new RelativePathResolver(searchPaths.NullToEmpty(), baseDirectory),
null,
GacFileResolver.Default)
......@@ -46,27 +46,29 @@ internal sealed class RuntimeMetadataReferenceResolver : MetadataReferenceResolv
public override ImmutableArray<PortableExecutableReference> ResolveReference(string reference, string baseFilePath, MetadataReferenceProperties properties)
{
if (PathResolver != null && PathUtilities.IsFilePath(reference))
string packageName;
string packageVersion;
if (NuGetPackageResolver.TryParsePackageReference(reference, out packageName, out packageVersion))
{
var resolvedPath = PathResolver.ResolvePath(reference, baseFilePath);
if (resolvedPath == null)
if (PackageResolver != null)
{
return ImmutableArray<PortableExecutableReference>.Empty;
var paths = PackageResolver.ResolveNuGetPackage(packageName, packageVersion);
Debug.Assert(!paths.IsDefault);
return paths.SelectAsArray(path => _fileReferenceProvider(path, properties));
}
return ImmutableArray.Create(_fileReferenceProvider(resolvedPath, properties));
}
if (PackageResolver != null)
else if (PathUtilities.IsFilePath(reference))
{
var paths = PackageResolver.ResolveNuGetPackage(reference);
if (!paths.IsDefaultOrEmpty)
if (PathResolver != null)
{
return paths.SelectAsArray(path => _fileReferenceProvider(path, properties));
var resolvedPath = PathResolver.ResolvePath(reference, baseFilePath);
if (resolvedPath != null)
{
return ImmutableArray.Create(_fileReferenceProvider(resolvedPath, properties));
}
}
}
if (GacFileResolver != null)
else if (GacFileResolver != null)
{
var path = GacFileResolver.Resolve(reference);
if (path != null)
......@@ -74,7 +76,6 @@ public override ImmutableArray<PortableExecutableReference> ResolveReference(str
return ImmutableArray.Create(_fileReferenceProvider(path, properties));
}
}
return ImmutableArray<PortableExecutableReference>.Empty;
}
......
......@@ -340,7 +340,9 @@ private static ImmutableArray<T> CheckImmutableArray<T>(ImmutableArray<T> items,
private static ImmutableArray<T> ToImmutableArrayChecked<T>(IEnumerable<T> items, string parameterName)
where T : class
{
return AddRangeAndFreeChecked(ArrayBuilder<T>.GetInstance(), items, parameterName);
var builder = ArrayBuilder<T>.GetInstance();
AddRangeChecked(builder, items, parameterName);
return builder.ToImmutableAndFree();
}
private static ImmutableArray<T> ConcatChecked<T>(ImmutableArray<T> existing, IEnumerable<T> items, string parameterName)
......@@ -348,10 +350,11 @@ private static ImmutableArray<T> ConcatChecked<T>(ImmutableArray<T> existing, IE
{
var builder = ArrayBuilder<T>.GetInstance();
builder.AddRange(existing);
return AddRangeAndFreeChecked(builder, items, parameterName);
AddRangeChecked(builder, items, parameterName);
return builder.ToImmutableAndFree();
}
private static ImmutableArray<T> AddRangeAndFreeChecked<T>(ArrayBuilder<T> builder, IEnumerable<T> items, string parameterName)
private static void AddRangeChecked<T>(ArrayBuilder<T> builder, IEnumerable<T> items, string parameterName)
where T : class
{
RequireNonNull(items, parameterName);
......@@ -360,14 +363,11 @@ private static ImmutableArray<T> AddRangeAndFreeChecked<T>(ArrayBuilder<T> build
{
if (item == null)
{
builder.Free();
throw new ArgumentNullException($"{parameterName}[{builder.Count}]");
}
builder.Add(item);
}
return builder.ToImmutableAndFree();
}
private static IEnumerable<S> SelectChecked<T, S>(IEnumerable<T> items, string parameterName, Func<T, S> selector)
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.CodeAnalysis.Scripting.Hosting;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using System.Collections.Immutable;
using Xunit;
namespace Microsoft.CodeAnalysis.UnitTests.Interactive
{
public class RuntimeMetadataReferenceResolverTests : TestBase
{
[Fact]
public void Resolve()
{
using (var directory = new DisposableDirectory(Temp))
{
var assembly1 = directory.CreateFile("_1.dll");
var assembly2 = directory.CreateFile("_2.dll");
// With NuGetPackageResolver.
var resolver = new RuntimeMetadataReferenceResolver(
new RelativePathResolver(ImmutableArray.Create(directory.Path), baseDirectory: directory.Path),
new PackageResolver(ImmutableDictionary<string, ImmutableArray<string>>.Empty.Add("nuget:N/1.0", ImmutableArray.Create(assembly1.Path, assembly2.Path))),
gacFileResolver: null);
// Recognized NuGet reference.
var actualReferences = resolver.ResolveReference("nuget:N/1.0", baseFilePath: null, properties: MetadataReferenceProperties.Assembly);
AssertEx.SetEqual(actualReferences.SelectAsArray(r => r.FilePath), assembly1.Path, assembly2.Path);
// Unrecognized NuGet reference.
actualReferences = resolver.ResolveReference("nuget:N/2.0", baseFilePath: null, properties: MetadataReferenceProperties.Assembly);
Assert.True(actualReferences.IsEmpty);
// Recognized file path.
actualReferences = resolver.ResolveReference("_2.dll", baseFilePath: null, properties: MetadataReferenceProperties.Assembly);
AssertEx.SetEqual(actualReferences.SelectAsArray(r => r.FilePath), assembly2.Path);
// Unrecognized file path.
actualReferences = resolver.ResolveReference("_3.dll", baseFilePath: null, properties: MetadataReferenceProperties.Assembly);
Assert.True(actualReferences.IsEmpty);
// Without NuGetPackageResolver.
resolver = new RuntimeMetadataReferenceResolver(
new RelativePathResolver(ImmutableArray.Create(directory.Path), baseDirectory: directory.Path),
packageResolver: null,
gacFileResolver: null);
// Unrecognized NuGet reference.
actualReferences = resolver.ResolveReference("nuget:N/1.0", baseFilePath: null, properties: MetadataReferenceProperties.Assembly);
Assert.True(actualReferences.IsEmpty);
// Recognized file path.
actualReferences = resolver.ResolveReference("_2.dll", baseFilePath: null, properties: MetadataReferenceProperties.Assembly);
AssertEx.SetEqual(actualReferences.SelectAsArray(r => r.FilePath), assembly2.Path);
// Unrecognized file path.
actualReferences = resolver.ResolveReference("_3.dll", baseFilePath: null, properties: MetadataReferenceProperties.Assembly);
Assert.True(actualReferences.IsEmpty);
}
}
private sealed class PackageResolver : NuGetPackageResolver
{
private const string Prefix = "nuget:";
private readonly IImmutableDictionary<string, ImmutableArray<string>> _map;
internal PackageResolver(IImmutableDictionary<string, ImmutableArray<string>> map)
{
_map = map;
}
internal override ImmutableArray<string> ResolveNuGetPackage(string packageName, string packageVersion)
{
var reference = $"{Prefix}{packageName}/{packageVersion}";
ImmutableArray<string> paths;
if (_map.TryGetValue(reference, out paths))
{
return paths;
}
return ImmutableArray<string>.Empty;
}
}
}
}
......@@ -59,6 +59,7 @@
<Compile Include="ObjectFormatterTestBase.cs" />
<Compile Include="ObjectFormatterTests.Fixtures.cs" />
<Compile Include="RedirectedOutput.cs" />
<Compile Include="RuntimeMetadataReferenceResolverTests.cs" />
<Compile Include="ScriptingTestHelpers.cs" />
<Compile Include="ScriptOptionsTests.cs" />
<Compile Include="ScriptTaskExtensions.cs" />
......
......@@ -70,6 +70,9 @@
<Compile Include="..\..\Compilers\Core\Portable\InternalUtilities\Hash.cs">
<Link>InternalUtilities\Hash.cs</Link>
</Compile>
<Compile Include="..\..\Compilers\Core\Portable\InternalUtilities\StreamExtensions.cs">
<Link>InternalUtilities\StreamExtensions.cs</Link>
</Compile>
<Compile Include="..\..\Compilers\Core\Portable\PEWriter\CustomDebugInfoConstants.cs">
<Link>Shared\CustomDebugInfoConstants.cs</Link>
</Compile>
......
......@@ -46,6 +46,9 @@
<Compile Include="..\..\..\Compilers\Core\Portable\EncodedStringText.cs">
<Link>InternalUtilities\EncodedStringText.cs</Link>
</Compile>
<Compile Include="..\..\..\Compilers\Core\Portable\InternalUtilities\StreamExtensions.cs">
<Link>InternalUtilities\StreamExtensions.cs</Link>
</Compile>
<Compile Include="..\..\..\Compilers\Helpers\CoreClrShim.cs">
<Link>InternalUtilities\CoreClrShim.cs</Link>
</Compile>
......
......@@ -310,5 +310,6 @@ internal enum FunctionId
VisualBasic_Interactive_Window,
NonFatalWatson,
GlobalOperationRegistration,
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Threading;
using Microsoft.CodeAnalysis.Internal.Log;
namespace Microsoft.CodeAnalysis.Notification
{
internal class GlobalOperationRegistration : IDisposable
{
private readonly AbstractGlobalOperationNotificationService _service;
private readonly CancellationTokenSource _source;
private readonly IDisposable _logging;
private bool _done;
public GlobalOperationRegistration(AbstractGlobalOperationNotificationService service, string operation)
{
_service = service;
this.Operation = operation;
_done = false;
this.Operation = operation;
_source = new CancellationTokenSource();
_logging = Logger.LogBlock(FunctionId.GlobalOperationRegistration, operation, _source.Token);
}
public string Operation { get; }
......@@ -28,10 +36,15 @@ public void Dispose()
if (_done)
{
_service.Done(this);
_logging.Dispose();
}
else
{
_service.Cancel(this);
_source.Cancel();
_logging.Dispose();
}
}
}
......
......@@ -61,7 +61,8 @@ public SourceCodeKind SourceCodeKind
/// <summary>
/// Get the current syntax tree for the document if the text is already loaded and the tree is already parsed.
/// Returns true if the syntax tree is already available, or false if getting the syntax tree would have incurred additional work.
/// In almost all cases, you should call <see cref="GetSyntaxTreeAsync"/> to fetch the tree, which will parse the tree
/// if it's not already parsed.
/// </summary>
public bool TryGetSyntaxTree(out SyntaxTree syntaxTree)
{
......@@ -88,7 +89,8 @@ public bool TryGetSyntaxTree(out SyntaxTree syntaxTree)
/// <summary>
/// Get the current syntax tree version for the document if the text is already loaded and the tree is already parsed.
/// Returns true if the syntax tree is already available, or false if getting the syntax tree would have incurred additional work.
/// In almost all cases, you should call <see cref="GetSyntaxVersionAsync"/> to fetch the version, which will load the tree
/// if it's not already available.
/// </summary>
public bool TryGetSyntaxVersion(out VersionStamp version)
{
......@@ -199,7 +201,9 @@ public Task<SyntaxTree> GetSyntaxTreeAsync(CancellationToken cancellationToken =
}
/// <summary>
/// Gets the root node of the current syntax tree if it is available.
/// Gets the root node of the current syntax tree if the syntax tree has already been parsed and the tree is still cached.
/// In almost all cases, you should call <see cref="GetSyntaxRootAsync"/> to fetch the root node, which will parse
/// the document if necessary.
/// </summary>
public bool TryGetSyntaxRoot(out SyntaxNode root)
{
......@@ -223,7 +227,9 @@ public async Task<SyntaxNode> GetSyntaxRootAsync(CancellationToken cancellationT
}
/// <summary>
/// Gets the current semantic model for this document if the model is already computed.
/// Gets the current semantic model for this document if the model is already computed and still cached.
/// In almost all cases, you should call <see cref="GetSemanticModelAsync"/>, which will compute the semantic model
/// if necessary.
/// </summary>
public bool TryGetSemanticModel(out SemanticModel semanticModel)
{
......
......@@ -372,7 +372,9 @@ private static TextDocument CreateAdditionalDocument(DocumentId documentId, Proj
}
/// <summary>
/// Get the <see cref="Compilation"/> for this project if it is available.
/// Tries to get the cached <see cref="Compilation"/> for this project if it has already been created and is still cached. In almost all
/// cases you should call <see cref="GetCompilationAsync"/> which will either return the cached <see cref="Compilation"/>
/// or create a new one otherwise.
/// </summary>
public bool TryGetCompilation(out Compilation compilation)
{
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册