提交 bc7f85d8 编写于 作者: S Shyam N 提交者: GitHub

Merge pull request #20306 from dotnet/merges/master-to-dev15.6-20170619-070015

Merge master to dev15.6
...@@ -1261,4 +1261,4 @@ private struct ILMarker ...@@ -1261,4 +1261,4 @@ private struct ILMarker
public int AbsoluteOffset; public int AbsoluteOffset;
} }
} }
} }
\ No newline at end of file
...@@ -139,8 +139,15 @@ public object ReadValue() ...@@ -139,8 +139,15 @@ public object ReadValue()
_cancellationToken, _cancellationToken,
TaskCreationOptions.LongRunning, TaskCreationOptions.LongRunning,
TaskScheduler.Default); TaskScheduler.Default);
task.Wait(_cancellationToken);
value = task.Result; // We must not proceed until the additional task completes. After returning from a read, the underlying
// stream providing access to raw memory will be closed; if this occurs before the separate thread
// completes its read then an access violation can occur attempting to read from unmapped memory.
//
// CANCELLATION: If cancellation is required, DO NOT attempt to cancel the operation by cancelling this
// wait. Cancellation must only be implemented by modifying 'task' to cancel itself in a timely manner
// so the wait can complete.
value = task.GetAwaiter().GetResult();
} }
else else
{ {
......
...@@ -488,7 +488,15 @@ private void WriteArray(Array array) ...@@ -488,7 +488,15 @@ private void WriteArray(Array array)
_cancellationToken, _cancellationToken,
TaskCreationOptions.LongRunning, TaskCreationOptions.LongRunning,
TaskScheduler.Default); TaskScheduler.Default);
task.Wait();
// We must not proceed until the additional task completes. After returning from a write, the underlying
// stream providing access to raw memory will be closed; if this occurs before the separate thread
// completes its write then an access violation can occur attempting to write to unmapped memory.
//
// CANCELLATION: If cancellation is required, DO NOT attempt to cancel the operation by cancelling this
// wait. Cancellation must only be implemented by modifying 'task' to cancel itself in a timely manner
// so the wait can complete.
task.GetAwaiter().GetResult();
} }
else else
{ {
...@@ -748,7 +756,15 @@ private void WriteObject(object instance, IObjectWritable instanceAsWritableOpt) ...@@ -748,7 +756,15 @@ private void WriteObject(object instance, IObjectWritable instanceAsWritableOpt)
_cancellationToken, _cancellationToken,
TaskCreationOptions.LongRunning, TaskCreationOptions.LongRunning,
TaskScheduler.Default); TaskScheduler.Default);
task.Wait(_cancellationToken);
// We must not proceed until the additional task completes. After returning from a write, the underlying
// stream providing access to raw memory will be closed; if this occurs before the separate thread
// completes its write then an access violation can occur attempting to write to unmapped memory.
//
// CANCELLATION: If cancellation is required, DO NOT attempt to cancel the operation by cancelling this
// wait. Cancellation must only be implemented by modifying 'task' to cancel itself in a timely manner
// so the wait can complete.
task.GetAwaiter().GetResult();
} }
else else
{ {
......
...@@ -24,14 +24,17 @@ public void GetBufferTextFromNonTextContainerThrows() ...@@ -24,14 +24,17 @@ public void GetBufferTextFromNonTextContainerThrows()
[Fact] [Fact]
public void GetBufferTextFromTextContainerDoesNotThrow() public void GetBufferTextFromTextContainerDoesNotThrow()
{ {
var textSnapshotMock = new Mock<VisualStudio.Text.ITextSnapshot>(); var textImageMock = new Mock<VisualStudio.Text.ITextImage>();
var textSnapshotMock = new Mock<VisualStudio.Text.ITextSnapshot2>();
var bufferMock = new Mock<VisualStudio.Text.ITextBuffer>(); var bufferMock = new Mock<VisualStudio.Text.ITextBuffer>();
textSnapshotMock.Setup(s => s.TextImage).Returns(textImageMock.Object);
bufferMock.SetupGet(x => x.CurrentSnapshot).Returns(textSnapshotMock.Object); bufferMock.SetupGet(x => x.CurrentSnapshot).Returns(textSnapshotMock.Object);
bufferMock.SetupGet(x => x.Properties).Returns(new VisualStudio.Utilities.PropertyCollection()); bufferMock.SetupGet(x => x.Properties).Returns(new VisualStudio.Utilities.PropertyCollection());
var textContainer = Microsoft.CodeAnalysis.Text.Extensions.TextBufferContainer.From(bufferMock.Object); var textContainer = Text.Extensions.TextBufferContainer.From(bufferMock.Object);
Microsoft.CodeAnalysis.Text.Extensions.GetTextBuffer(textContainer); Text.Extensions.GetTextBuffer(textContainer);
} }
} }
} }
\ No newline at end of file
...@@ -109,7 +109,11 @@ private EditorTextFactoryService CreateMockTextFactoryService() ...@@ -109,7 +109,11 @@ private EditorTextFactoryService CreateMockTextFactoryService()
{ {
var text = reader.ReadToEnd(); var text = reader.ReadToEnd();
var mockSnapshot = new Mock<ITextSnapshot>(); var mockImage = new Mock<ITextImage>();
mockImage.Setup(i => i.GetText(It.IsAny<Span>())).Returns(text);
var mockSnapshot = new Mock<ITextSnapshot2>();
mockSnapshot.Setup(s => s.TextImage).Returns(mockImage.Object);
mockSnapshot.Setup(s => s.GetText()).Returns(text); mockSnapshot.Setup(s => s.GetText()).Returns(text);
var mockTextBuffer = new Mock<ITextBuffer>(); var mockTextBuffer = new Mock<ITextBuffer>();
......
...@@ -10,7 +10,6 @@ ...@@ -10,7 +10,6 @@
using System.Threading; using System.Threading;
using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.Text.Shared.Extensions; using Microsoft.CodeAnalysis.Text.Shared.Extensions;
using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text;
using Roslyn.Utilities; using Roslyn.Utilities;
...@@ -29,8 +28,8 @@ private class SnapshotSourceText : SourceText ...@@ -29,8 +28,8 @@ private class SnapshotSourceText : SourceText
/// </summary> /// </summary>
internal sealed class ClosedSnapshotSourceText : SnapshotSourceText internal sealed class ClosedSnapshotSourceText : SnapshotSourceText
{ {
public ClosedSnapshotSourceText(ITextSnapshot roslynSnapshot, Encoding encodingOpt) public ClosedSnapshotSourceText(ITextImage textImage, Encoding encodingOpt)
: base(roslynSnapshot, encodingOpt, containerOpt: null) : base(textImage, encodingOpt, containerOpt: null)
{ {
} }
} }
...@@ -38,9 +37,10 @@ public ClosedSnapshotSourceText(ITextSnapshot roslynSnapshot, Encoding encodingO ...@@ -38,9 +37,10 @@ public ClosedSnapshotSourceText(ITextSnapshot roslynSnapshot, Encoding encodingO
private static readonly Func<int, int, string> s_textLog = (v1, v2) => string.Format("FullRange : from {0} to {1}", v1, v2); private static readonly Func<int, int, string> s_textLog = (v1, v2) => string.Format("FullRange : from {0} to {1}", v1, v2);
/// <summary> /// <summary>
/// The ITextSnapshot backing the SourceText instance /// The <see cref="ITextImage"/> backing the SourceText instance
/// </summary> /// </summary>
protected readonly ITextSnapshot RoslynSnapshot; public readonly ITextImage TextImage;
private readonly Encoding _encodingOpt; private readonly Encoding _encodingOpt;
private readonly TextBufferContainer _containerOpt; private readonly TextBufferContainer _containerOpt;
private readonly int _reiteratedVersion; private readonly int _reiteratedVersion;
...@@ -49,17 +49,17 @@ private SnapshotSourceText(ITextSnapshot editorSnapshot, Encoding encodingOpt) ...@@ -49,17 +49,17 @@ private SnapshotSourceText(ITextSnapshot editorSnapshot, Encoding encodingOpt)
{ {
Contract.ThrowIfNull(editorSnapshot); Contract.ThrowIfNull(editorSnapshot);
this.RoslynSnapshot = TextBufferMapper.ToRoslyn(editorSnapshot); this.TextImage = TextBufferMapper.RecordTextSnapshotAndGetImage(editorSnapshot);
_containerOpt = TextBufferContainer.From(editorSnapshot.TextBuffer); _containerOpt = TextBufferContainer.From(editorSnapshot.TextBuffer);
_reiteratedVersion = editorSnapshot.Version.ReiteratedVersionNumber; _reiteratedVersion = editorSnapshot.Version.ReiteratedVersionNumber;
_encodingOpt = encodingOpt; _encodingOpt = encodingOpt;
} }
public SnapshotSourceText(ITextSnapshot roslynSnapshot, Encoding encodingOpt, TextBufferContainer containerOpt) public SnapshotSourceText(ITextImage textImage, Encoding encodingOpt, TextBufferContainer containerOpt)
{ {
Contract.ThrowIfNull(roslynSnapshot); Contract.ThrowIfNull(textImage);
this.RoslynSnapshot = roslynSnapshot; this.TextImage = textImage;
_encodingOpt = encodingOpt; _encodingOpt = encodingOpt;
_containerOpt = containerOpt; _containerOpt = containerOpt;
} }
...@@ -122,10 +122,8 @@ public override Encoding Encoding ...@@ -122,10 +122,8 @@ public override Encoding Encoding
get { return _encodingOpt; } get { return _encodingOpt; }
} }
public ITextSnapshot EditorSnapshot public ITextSnapshot TryFindEditorSnapshot()
{ => TextBufferMapper.TryFindEditorSnapshot(this.TextImage);
get { return TextBufferMapper.ToEditor(this.RoslynSnapshot); }
}
protected static ITextBufferCloneService TextBufferFactory protected static ITextBufferCloneService TextBufferFactory
{ {
...@@ -154,14 +152,14 @@ public override int Length ...@@ -154,14 +152,14 @@ public override int Length
{ {
get get
{ {
var res = this.RoslynSnapshot.Length; var res = this.TextImage.Length;
return res; return res;
} }
} }
public override char this[int position] public override char this[int position]
{ {
get { return this.RoslynSnapshot[position]; } get { return this.TextImage[position]; }
} }
#region Lines #region Lines
...@@ -181,21 +179,21 @@ public LineInfo(SnapshotSourceText text) ...@@ -181,21 +179,21 @@ public LineInfo(SnapshotSourceText text)
public override int Count public override int Count
{ {
get { return _text.RoslynSnapshot.LineCount; } get { return _text.TextImage.LineCount; }
} }
public override TextLine this[int index] public override TextLine this[int index]
{ {
get get
{ {
var line = _text.RoslynSnapshot.GetLineFromLineNumber(index); var line = _text.TextImage.GetLineFromLineNumber(index);
return TextLine.FromSpan(_text, TextSpan.FromBounds(line.Start, line.End)); return TextLine.FromSpan(_text, TextSpan.FromBounds(line.Start, line.End));
} }
} }
public override int IndexOf(int position) public override int IndexOf(int position)
{ {
return _text.RoslynSnapshot.GetLineNumberFromPosition(position); return _text.TextImage.GetLineNumberFromPosition(position);
} }
public override TextLine GetLineFromPosition(int position) public override TextLine GetLineFromPosition(int position)
...@@ -205,7 +203,7 @@ public override TextLine GetLineFromPosition(int position) ...@@ -205,7 +203,7 @@ public override TextLine GetLineFromPosition(int position)
public override LinePosition GetLinePosition(int position) public override LinePosition GetLinePosition(int position)
{ {
ITextSnapshotLine textLine = _text.RoslynSnapshot.GetLineFromPosition(position); var textLine = _text.TextImage.GetLineFromPosition(position);
return new LinePosition(textLine.LineNumber, position - textLine.Start); return new LinePosition(textLine.LineNumber, position - textLine.Start);
} }
} }
...@@ -213,13 +211,13 @@ public override LinePosition GetLinePosition(int position) ...@@ -213,13 +211,13 @@ public override LinePosition GetLinePosition(int position)
public override string ToString() public override string ToString()
{ {
return this.RoslynSnapshot.GetText(); return this.TextImage.GetText();
} }
public override string ToString(TextSpan textSpan) public override string ToString(TextSpan textSpan)
{ {
var editorSpan = new Span(textSpan.Start, textSpan.Length); var editorSpan = new Span(textSpan.Start, textSpan.Length);
var res = this.RoslynSnapshot.GetText(editorSpan); var res = this.TextImage.GetText(editorSpan);
return res; return res;
} }
...@@ -244,7 +242,7 @@ public override SourceText WithChanges(IEnumerable<TextChange> changes) ...@@ -244,7 +242,7 @@ public override SourceText WithChanges(IEnumerable<TextChange> changes)
} }
// otherwise, create a new cloned snapshot // otherwise, create a new cloned snapshot
var buffer = factory.Clone(RoslynSnapshot.GetFullSpan()); var buffer = factory.Clone(TextImage);
var baseSnapshot = buffer.CurrentSnapshot; var baseSnapshot = buffer.CurrentSnapshot;
// apply the change to the buffer // apply the change to the buffer
...@@ -258,7 +256,10 @@ public override SourceText WithChanges(IEnumerable<TextChange> changes) ...@@ -258,7 +256,10 @@ public override SourceText WithChanges(IEnumerable<TextChange> changes)
edit.Apply(); edit.Apply();
} }
return new ChangedSourceText(this, baseSnapshot, buffer.CurrentSnapshot); return new ChangedSourceText(
baseText: this,
baseSnapshot: ((ITextSnapshot2)baseSnapshot).TextImage,
currentSnapshot: ((ITextSnapshot2)buffer.CurrentSnapshot).TextImage);
} }
/// <summary> /// <summary>
...@@ -267,9 +268,9 @@ public override SourceText WithChanges(IEnumerable<TextChange> changes) ...@@ -267,9 +268,9 @@ public override SourceText WithChanges(IEnumerable<TextChange> changes)
private class ChangedSourceText : SnapshotSourceText private class ChangedSourceText : SnapshotSourceText
{ {
private readonly SnapshotSourceText _baseText; private readonly SnapshotSourceText _baseText;
private readonly ITextSnapshot _baseSnapshot; private readonly ITextImage _baseSnapshot;
public ChangedSourceText(SnapshotSourceText baseText, ITextSnapshot baseSnapshot, ITextSnapshot currentSnapshot) public ChangedSourceText(SnapshotSourceText baseText, ITextImage baseSnapshot, ITextImage currentSnapshot)
: base(currentSnapshot, baseText.Encoding, containerOpt: null) : base(currentSnapshot, baseText.Encoding, containerOpt: null)
{ {
_baseText = baseText; _baseText = baseText;
...@@ -294,18 +295,18 @@ public override IReadOnlyList<TextChangeRange> GetChangeRanges(SourceText oldTex ...@@ -294,18 +295,18 @@ public override IReadOnlyList<TextChangeRange> GetChangeRanges(SourceText oldTex
return new[] { new TextChangeRange(new TextSpan(0, oldText.Length), this.Length) }; return new[] { new TextChangeRange(new TextSpan(0, oldText.Length), this.Length) };
} }
return GetChangeRanges(_baseSnapshot, _baseSnapshot.Length, this.RoslynSnapshot); return GetChangeRanges(_baseSnapshot, _baseSnapshot.Length, this.TextImage);
} }
} }
public override void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int count) public override void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int count)
{ {
this.RoslynSnapshot.CopyTo(sourceIndex, destination, destinationIndex, count); this.TextImage.CopyTo(sourceIndex, destination, destinationIndex, count);
} }
public override void Write(TextWriter textWriter, TextSpan span, CancellationToken cancellationToken) public override void Write(TextWriter textWriter, TextSpan span, CancellationToken cancellationToken)
{ {
this.RoslynSnapshot.Write(textWriter, span.ToSpan()); this.TextImage.Write(textWriter, span.ToSpan());
} }
#region GetChangeRangesImplementation #region GetChangeRangesImplementation
...@@ -334,36 +335,44 @@ public override IReadOnlyList<TextChangeRange> GetChangeRanges(SourceText oldTex ...@@ -334,36 +335,44 @@ public override IReadOnlyList<TextChangeRange> GetChangeRanges(SourceText oldTex
} }
} }
var oldSnapshot = oldText.FindCorrespondingEditorTextSnapshot(); var oldSnapshot = oldText.TryFindCorrespondingEditorTextImage();
var newSnapshot = this.FindCorrespondingEditorTextSnapshot(); var newSnapshot = this.TryFindCorrespondingEditorTextImage();
return GetChangeRanges(oldSnapshot, oldText.Length, newSnapshot); return GetChangeRanges(oldSnapshot, oldText.Length, newSnapshot);
} }
private IReadOnlyList<TextChangeRange> GetChangeRanges(ITextSnapshot oldSnapshot, int oldTextLength, ITextSnapshot newSnapshot) private IReadOnlyList<TextChangeRange> GetChangeRanges(ITextImage oldImage, int oldTextLength, ITextImage newImage)
{ {
if (oldSnapshot == null || if (oldImage == null ||
newSnapshot == null || newImage == null ||
oldSnapshot.TextBuffer != newSnapshot.TextBuffer) oldImage.Version.Identifier != newImage.Version.Identifier)
{ {
// Claim its all changed // Claim its all changed
Logger.Log(FunctionId.Workspace_SourceText_GetChangeRanges, "Invalid Snapshots"); Logger.Log(FunctionId.Workspace_SourceText_GetChangeRanges, "Invalid Snapshots");
return ImmutableArray.Create<TextChangeRange>(new TextChangeRange(new TextSpan(0, oldTextLength), this.Length)); return ImmutableArray.Create(new TextChangeRange(new TextSpan(0, oldTextLength), this.Length));
} }
else if (oldSnapshot.Version.ReiteratedVersionNumber == newSnapshot.Version.ReiteratedVersionNumber) else if (AreSameReiteratedVersion(oldImage, newImage))
{ {
// content of two snapshot must be same even if versions are different // content of two snapshot must be same even if versions are different
return TextChangeRange.NoChanges; return TextChangeRange.NoChanges;
} }
else else
{ {
return GetChangeRanges(oldSnapshot, newSnapshot, forward: oldSnapshot.Version.VersionNumber <= newSnapshot.Version.VersionNumber); return GetChangeRanges(oldImage, newImage, forward: oldImage.Version.VersionNumber <= newImage.Version.VersionNumber);
} }
} }
private static bool AreSameReiteratedVersion(ITextImage oldImage, ITextImage newImage)
{
var oldSnapshot = TextBufferMapper.TryFindEditorSnapshot(oldImage);
var newSnapshot = TextBufferMapper.TryFindEditorSnapshot(newImage);
return oldSnapshot != null && newSnapshot != null && oldSnapshot.Version.ReiteratedVersionNumber == newSnapshot.Version.ReiteratedVersionNumber;
}
private static readonly Func<ITextChange, TextChangeRange> s_forwardTextChangeRange = c => CreateTextChangeRange(c, forward: true); private static readonly Func<ITextChange, TextChangeRange> s_forwardTextChangeRange = c => CreateTextChangeRange(c, forward: true);
private static readonly Func<ITextChange, TextChangeRange> s_backwardTextChangeRange = c => CreateTextChangeRange(c, forward: false); private static readonly Func<ITextChange, TextChangeRange> s_backwardTextChangeRange = c => CreateTextChangeRange(c, forward: false);
private IReadOnlyList<TextChangeRange> GetChangeRanges(ITextSnapshot snapshot1, ITextSnapshot snapshot2, bool forward) private IReadOnlyList<TextChangeRange> GetChangeRanges(ITextImage snapshot1, ITextImage snapshot2, bool forward)
{ {
var oldSnapshot = forward ? snapshot1 : snapshot2; var oldSnapshot = forward ? snapshot1 : snapshot2;
var newSnapshot = forward ? snapshot2 : snapshot1; var newSnapshot = forward ? snapshot2 : snapshot1;
...@@ -399,7 +408,7 @@ private IReadOnlyList<TextChangeRange> GetChangeRanges(ITextSnapshot snapshot1, ...@@ -399,7 +408,7 @@ private IReadOnlyList<TextChangeRange> GetChangeRanges(ITextSnapshot snapshot1,
} }
} }
private TextChangeRange GetChangeRanges(ITextVersion oldVersion, ITextVersion newVersion, bool forward) private TextChangeRange GetChangeRanges(ITextImageVersion oldVersion, ITextImageVersion newVersion, bool forward)
{ {
TextChangeRange? range = null; TextChangeRange? range = null;
var iterator = GetMultipleVersionTextChanges(oldVersion, newVersion, forward); var iterator = GetMultipleVersionTextChanges(oldVersion, newVersion, forward);
...@@ -412,7 +421,8 @@ private TextChangeRange GetChangeRanges(ITextVersion oldVersion, ITextVersion ne ...@@ -412,7 +421,8 @@ private TextChangeRange GetChangeRanges(ITextVersion oldVersion, ITextVersion ne
return range.Value; return range.Value;
} }
private static IEnumerable<IEnumerable<TextChangeRange>> GetMultipleVersionTextChanges(ITextVersion oldVersion, ITextVersion newVersion, bool forward) private static IEnumerable<IEnumerable<TextChangeRange>> GetMultipleVersionTextChanges(
ITextImageVersion oldVersion, ITextImageVersion newVersion, bool forward)
{ {
for (var version = oldVersion; version != newVersion; version = version.Next) for (var version = oldVersion; version != newVersion; version = version.Next)
{ {
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // 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;
using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
...@@ -30,7 +29,7 @@ private TextBufferContainer(ITextBuffer editorBuffer, Encoding encoding) ...@@ -30,7 +29,7 @@ private TextBufferContainer(ITextBuffer editorBuffer, Encoding encoding)
Contract.ThrowIfNull(encoding); Contract.ThrowIfNull(encoding);
_weakEditorBuffer = new WeakReference<ITextBuffer>(editorBuffer); _weakEditorBuffer = new WeakReference<ITextBuffer>(editorBuffer);
_currentText = new SnapshotSourceText(TextBufferMapper.ToRoslyn(editorBuffer.CurrentSnapshot), encoding, this); _currentText = new SnapshotSourceText(TextBufferMapper.RecordTextSnapshotAndGetImage(editorBuffer.CurrentSnapshot), encoding, this);
} }
/// <summary> /// <summary>
...@@ -54,13 +53,14 @@ private static TextBufferContainer CreateContainer(ITextBuffer editorBuffer) ...@@ -54,13 +53,14 @@ private static TextBufferContainer CreateContainer(ITextBuffer editorBuffer)
return new TextBufferContainer(editorBuffer, editorBuffer.GetEncodingOrUTF8()); return new TextBufferContainer(editorBuffer, editorBuffer.GetEncodingOrUTF8());
} }
public ITextBuffer EditorTextBuffer { get { return _weakEditorBuffer.GetTarget(); } } public ITextBuffer TryFindEditorTextBuffer()
=> _weakEditorBuffer.GetTarget();
public override SourceText CurrentText public override SourceText CurrentText
{ {
get get
{ {
var editorBuffer = this.EditorTextBuffer; var editorBuffer = this.TryFindEditorTextBuffer();
return editorBuffer != null return editorBuffer != null
? editorBuffer.CurrentSnapshot.AsText() ? editorBuffer.CurrentSnapshot.AsText()
: _currentText; : _currentText;
...@@ -73,7 +73,7 @@ public override SourceText CurrentText ...@@ -73,7 +73,7 @@ public override SourceText CurrentText
{ {
lock (_gate) lock (_gate)
{ {
var textBuffer = this.EditorTextBuffer; var textBuffer = this.TryFindEditorTextBuffer();
if (this.EtextChanged == null && textBuffer != null) if (this.EtextChanged == null && textBuffer != null)
{ {
textBuffer.ChangedHighPriority += this.OnTextContentChanged; textBuffer.ChangedHighPriority += this.OnTextContentChanged;
...@@ -89,7 +89,7 @@ public override SourceText CurrentText ...@@ -89,7 +89,7 @@ public override SourceText CurrentText
{ {
this.EtextChanged -= value; this.EtextChanged -= value;
var textBuffer = this.EditorTextBuffer; var textBuffer = this.TryFindEditorTextBuffer();
if (this.EtextChanged == null && textBuffer != null) if (this.EtextChanged == null && textBuffer != null)
{ {
textBuffer.ChangedHighPriority -= this.OnTextContentChanged; textBuffer.ChangedHighPriority -= this.OnTextContentChanged;
...@@ -120,4 +120,4 @@ private void OnTextContentChanged(object sender, TextContentChangedEventArgs arg ...@@ -120,4 +120,4 @@ private void OnTextContentChanged(object sender, TextContentChangedEventArgs arg
public TextChangeEventArgs LastEventArgs { get; private set; } public TextChangeEventArgs LastEventArgs { get; private set; }
} }
} }
} }
\ No newline at end of file
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.Text.Shared.Extensions;
using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text;
using Roslyn.Utilities; using Roslyn.Utilities;
...@@ -16,48 +14,32 @@ public static partial class Extensions ...@@ -16,48 +14,32 @@ public static partial class Extensions
/// </summary> /// </summary>
private static class TextBufferMapper private static class TextBufferMapper
{ {
private static readonly ConditionalWeakTable<ITextSnapshot, ITextSnapshot> s_editorToRoslynSnapshotMap = new ConditionalWeakTable<ITextSnapshot, ITextSnapshot>(); private static readonly ConditionalWeakTable<ITextImage, WeakReference<ITextSnapshot>> s_roslynToEditorSnapshotMap = new ConditionalWeakTable<ITextImage, WeakReference<ITextSnapshot>>();
private static readonly ConditionalWeakTable<ITextSnapshot, WeakReference<ITextSnapshot>> s_roslynToEditorSnapshotMap = new ConditionalWeakTable<ITextSnapshot, WeakReference<ITextSnapshot>>();
private static readonly ConditionalWeakTable<ITextSnapshot, ITextSnapshot>.CreateValueCallback s_createSnapshotCallback = CreateSnapshot;
public static ITextSnapshot ToRoslyn(ITextSnapshot editorSnapshot) public static ITextImage RecordTextSnapshotAndGetImage(ITextSnapshot editorSnapshot)
{ {
Contract.ThrowIfNull(editorSnapshot); Contract.ThrowIfNull(editorSnapshot);
var roslynSnapshot = s_editorToRoslynSnapshotMap.GetValue(editorSnapshot, s_createSnapshotCallback); var textImage = ((ITextSnapshot2)editorSnapshot).TextImage;
if (roslynSnapshot == null) Contract.ThrowIfNull(textImage);
// If we're already in the map, there's nothing to update. Do a quick check
// to avoid two allocations per call to RecordTextSnapshotAndGetImage.
if (!s_roslynToEditorSnapshotMap.TryGetValue(textImage, out var weakReference) ||
weakReference.GetTarget() != editorSnapshot)
{ {
return editorSnapshot; // put reverse entry that won't hold onto anything
s_roslynToEditorSnapshotMap.GetValue(
textImage, _ => new WeakReference<ITextSnapshot>(editorSnapshot));
} }
return roslynSnapshot; return textImage;
}
private static ITextSnapshot CreateSnapshot(ITextSnapshot editorSnapshot)
{
var factory = TextBufferFactory;
// We might not have a factory if there is no primary workspace (for example, under the unit test harness,
// or in CodeSense where they are just using the parsers by themselves). In that case, just use the editor
// snapshot as-is.
//
// Creating a buffer off a given snapshot should be cheap, so it should be okay to create a dummy buffer here
// just to host the snapshot we want.
var roslynSnapshot = factory != null
? factory.Clone(editorSnapshot.GetFullSpan()).CurrentSnapshot
: editorSnapshot;
// put reverse entry that won't hold onto anything
var weakEditorSnapshot = new WeakReference<ITextSnapshot>(editorSnapshot);
s_roslynToEditorSnapshotMap.GetValue(roslynSnapshot, _ => weakEditorSnapshot);
return roslynSnapshot;
} }
public static ITextSnapshot ToEditor(ITextSnapshot roslynSnapshot) public static ITextSnapshot TryFindEditorSnapshot(ITextImage textImage)
{ {
Contract.ThrowIfNull(roslynSnapshot); Contract.ThrowIfNull(textImage);
if (!s_roslynToEditorSnapshotMap.TryGetValue(roslynSnapshot, out var weakReference) || if (!s_roslynToEditorSnapshotMap.TryGetValue(textImage, out var weakReference) ||
!weakReference.TryGetTarget(out var editorSnapshot)) !weakReference.TryGetTarget(out var editorSnapshot))
{ {
return null; return null;
...@@ -82,4 +64,4 @@ private static ITextBufferCloneService TextBufferFactory ...@@ -82,4 +64,4 @@ private static ITextBufferCloneService TextBufferFactory
} }
} }
} }
} }
\ No newline at end of file
...@@ -18,7 +18,7 @@ public static ITextBuffer GetTextBuffer(this SourceTextContainer textContainer) ...@@ -18,7 +18,7 @@ public static ITextBuffer GetTextBuffer(this SourceTextContainer textContainer)
=> TryGetTextBuffer(textContainer) ?? throw new ArgumentException(TextEditorResources.textContainer_is_not_a_SourceTextContainer_that_was_created_from_an_ITextBuffer, nameof(textContainer)); => TryGetTextBuffer(textContainer) ?? throw new ArgumentException(TextEditorResources.textContainer_is_not_a_SourceTextContainer_that_was_created_from_an_ITextBuffer, nameof(textContainer));
public static ITextBuffer TryGetTextBuffer(this SourceTextContainer textContainer) public static ITextBuffer TryGetTextBuffer(this SourceTextContainer textContainer)
=> (textContainer as TextBufferContainer)?.EditorTextBuffer; => (textContainer as TextBufferContainer)?.TryFindEditorTextBuffer();
/// <summary> /// <summary>
/// Returns the ITextSnapshot behind this SourceText, or null if it wasn't created from one. /// Returns the ITextSnapshot behind this SourceText, or null if it wasn't created from one.
...@@ -28,7 +28,10 @@ public static ITextBuffer TryGetTextBuffer(this SourceTextContainer textContaine ...@@ -28,7 +28,10 @@ public static ITextBuffer TryGetTextBuffer(this SourceTextContainer textContaine
/// </summary> /// </summary>
/// <returns>The underlying ITextSnapshot.</returns> /// <returns>The underlying ITextSnapshot.</returns>
public static ITextSnapshot FindCorrespondingEditorTextSnapshot(this SourceText text) public static ITextSnapshot FindCorrespondingEditorTextSnapshot(this SourceText text)
=> (text as SnapshotSourceText)?.EditorSnapshot; => (text as SnapshotSourceText)?.TryFindEditorSnapshot();
internal static ITextImage TryFindCorrespondingEditorTextImage(this SourceText text)
=> (text as SnapshotSourceText)?.TextImage;
internal static TextLine AsTextLine(this ITextSnapshotLine line) internal static TextLine AsTextLine(this ITextSnapshotLine line)
=> line.Snapshot.AsText().Lines[line.LineNumber]; => line.Snapshot.AsText().Lines[line.LineNumber];
...@@ -37,7 +40,7 @@ public static SourceText AsText(this ITextSnapshot textSnapshot) ...@@ -37,7 +40,7 @@ public static SourceText AsText(this ITextSnapshot textSnapshot)
=> SnapshotSourceText.From(textSnapshot); => SnapshotSourceText.From(textSnapshot);
internal static SourceText AsRoslynText(this ITextSnapshot textSnapshot, Encoding encoding) internal static SourceText AsRoslynText(this ITextSnapshot textSnapshot, Encoding encoding)
=> new SnapshotSourceText.ClosedSnapshotSourceText(textSnapshot, encoding); => new SnapshotSourceText.ClosedSnapshotSourceText(((ITextSnapshot2)textSnapshot).TextImage, encoding);
/// <summary> /// <summary>
/// Gets the workspace corresponding to the text buffer. /// Gets the workspace corresponding to the text buffer.
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // 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.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host;
using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text;
...@@ -13,5 +8,6 @@ namespace Microsoft.CodeAnalysis.Text ...@@ -13,5 +8,6 @@ namespace Microsoft.CodeAnalysis.Text
internal interface ITextBufferCloneService : IWorkspaceService internal interface ITextBufferCloneService : IWorkspaceService
{ {
ITextBuffer Clone(SnapshotSpan span); ITextBuffer Clone(SnapshotSpan span);
ITextBuffer Clone(ITextImage textImage);
} }
} }
\ No newline at end of file
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // 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.Generic;
using System.Composition; using System.Composition;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text;
...@@ -23,7 +18,7 @@ internal class TextBufferCloneServiceFactory : IWorkspaceServiceFactory ...@@ -23,7 +18,7 @@ internal class TextBufferCloneServiceFactory : IWorkspaceServiceFactory
ITextBufferFactoryService textBufferFactoryService, ITextBufferFactoryService textBufferFactoryService,
IContentTypeRegistryService contentTypeRegistry) IContentTypeRegistryService contentTypeRegistry)
{ {
_singleton = new TextBufferCloneService((ITextBufferFactoryService2)textBufferFactoryService, contentTypeRegistry.UnknownContentType); _singleton = new TextBufferCloneService((ITextBufferFactoryService3)textBufferFactoryService, contentTypeRegistry.UnknownContentType);
} }
public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices)
...@@ -33,19 +28,20 @@ public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) ...@@ -33,19 +28,20 @@ public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices)
private class TextBufferCloneService : ITextBufferCloneService private class TextBufferCloneService : ITextBufferCloneService
{ {
private readonly ITextBufferFactoryService2 _textBufferFactoryService; private readonly ITextBufferFactoryService3 _textBufferFactoryService;
private readonly IContentType _unknownContentType; private readonly IContentType _unknownContentType;
public TextBufferCloneService(ITextBufferFactoryService2 textBufferFactoryService, IContentType unknownContentType) public TextBufferCloneService(ITextBufferFactoryService3 textBufferFactoryService, IContentType unknownContentType)
{ {
_textBufferFactoryService = textBufferFactoryService; _textBufferFactoryService = textBufferFactoryService;
_unknownContentType = unknownContentType; _unknownContentType = unknownContentType;
} }
public ITextBuffer Clone(SnapshotSpan span) public ITextBuffer Clone(SnapshotSpan span)
{ => _textBufferFactoryService.CreateTextBuffer(span, _unknownContentType);
return _textBufferFactoryService.CreateTextBuffer(span, _unknownContentType);
} public ITextBuffer Clone(ITextImage textImage)
=> _textBufferFactoryService.CreateTextBuffer(textImage, _unknownContentType);
} }
} }
} }
\ No newline at end of file
...@@ -7,11 +7,12 @@ using System.Linq; ...@@ -7,11 +7,12 @@ using System.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text; using System.Text;
using System.Text.RegularExpressions;
using System.Xml.Linq; using System.Xml.Linq;
string usage = @"usage: BuildNuGets.csx <binaries-dir> <build-version> <output-directory> [<filter>]"; string usage = @"usage: BuildNuGets.csx <binaries-dir> <build-version> <output-directory> <git sha> [<filter>]";
if (Args.Count < 3 || Args.Count > 4) if (Args.Count < 4 || Args.Count > 5)
{ {
Console.WriteLine(usage); Console.WriteLine(usage);
Environment.Exit(1); Environment.Exit(1);
...@@ -32,7 +33,19 @@ var BuildVersion = Args[1].Trim(); ...@@ -32,7 +33,19 @@ var BuildVersion = Args[1].Trim();
var BuildingReleaseNugets = IsReleaseVersion(BuildVersion); var BuildingReleaseNugets = IsReleaseVersion(BuildVersion);
var NuspecDirPath = Path.Combine(SolutionRoot, "src/NuGet"); var NuspecDirPath = Path.Combine(SolutionRoot, "src/NuGet");
var OutDir = Path.GetFullPath(Args[2]).TrimEnd('\\'); var OutDir = Path.GetFullPath(Args[2]).TrimEnd('\\');
var NuspecNameFilter = Args.Count > 3 ? Args[3] : null;
var CommitSha = Args[3].Replace("<", "").Replace(">", "");
var CommitIsDeveloperBuild = CommitSha == "<developer build>";
if (!CommitIsDeveloperBuild && !Regex.IsMatch(CommitSha, "[A-Fa-f0-9]+"))
{
Console.WriteLine("Invalid Git sha value: expected <developer build> or a valid sha");
Environment.Exit(1);
}
var CommitPathMessage = CommitIsDeveloperBuild
? "This an unofficial build from a developer's machine"
: $"This package was built from the source at https://github.com/dotnet/roslyn/commit/{CommitSha}";
var NuspecNameFilter = Args.Count > 4 ? Args[4] : null;
var LicenseUrlRedist = @"http://go.microsoft.com/fwlink/?LinkId=529443"; var LicenseUrlRedist = @"http://go.microsoft.com/fwlink/?LinkId=529443";
var LicenseUrlNonRedist = @"http://go.microsoft.com/fwlink/?LinkId=529444"; var LicenseUrlNonRedist = @"http://go.microsoft.com/fwlink/?LinkId=529444";
...@@ -216,6 +229,7 @@ int PackFiles(string[] nuspecFiles, string licenseUrl) ...@@ -216,6 +229,7 @@ int PackFiles(string[] nuspecFiles, string licenseUrl)
{ "tags", Tags }, { "tags", Tags },
{ "emptyDirPath", emptyDir }, { "emptyDirPath", emptyDir },
{ "additionalFilesPath", NuGetAdditionalFilesPath }, { "additionalFilesPath", NuGetAdditionalFilesPath },
{ "commitPathMessage", CommitPathMessage },
{ "srcDirPath", SrcDirPath } { "srcDirPath", SrcDirPath }
}; };
......
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
<description> <description>
Contains the build task and targets used by MSBuild to run the C# and VB compilers. Contains the build task and targets used by MSBuild to run the C# and VB compilers.
Supports using VBCSCompiler on Windows. Supports using VBCSCompiler on Windows.
$commitPathMessage$
</description> </description>
<dependencies> <dependencies>
<group targetFramework="netstandard1.3"> <group targetFramework="netstandard1.3">
......
...@@ -2,9 +2,10 @@ ...@@ -2,9 +2,10 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata minClientVersion="3.3"> <metadata minClientVersion="3.3">
<id>Microsoft.CodeAnalysis.CSharp.CodeStyle</id> <id>Microsoft.CodeAnalysis.CSharp.CodeStyle</id>
<description> <summary>
.NET Compiler Platform ("Roslyn") code style analyzers for C#. .NET Compiler Platform ("Roslyn") code style analyzers for C#.
</description> </summary>
<description>$commitPathMessage$</description>
<dependencies> <dependencies>
</dependencies> </dependencies>
......
...@@ -2,9 +2,10 @@ ...@@ -2,9 +2,10 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata minClientVersion="3.3"> <metadata minClientVersion="3.3">
<id>Microsoft.CodeAnalysis.CSharp.Features</id> <id>Microsoft.CodeAnalysis.CSharp.Features</id>
<description> <summary>
.NET Compiler Platform ("Roslyn") support for creating C# editing experiences. .NET Compiler Platform ("Roslyn") support for creating C# editing experiences.
</description> </summary>
<description>$commitPathMessage$</description>
<dependencies> <dependencies>
<dependency id="Microsoft.CodeAnalysis.Features" version="$version$" /> <dependency id="Microsoft.CodeAnalysis.Features" version="$version$" />
<dependency id="Microsoft.CodeAnalysis.CSharp.Workspaces" version="$version$" /> <dependency id="Microsoft.CodeAnalysis.CSharp.Workspaces" version="$version$" />
......
...@@ -3,10 +3,7 @@ ...@@ -3,10 +3,7 @@
<metadata> <metadata>
<id>Microsoft.CodeAnalysis.CSharp.Scripting</id> <id>Microsoft.CodeAnalysis.CSharp.Scripting</id>
<summary>Microsoft .NET Compiler Platform ("Roslyn") CSharp scripting package.</summary> <summary>Microsoft .NET Compiler Platform ("Roslyn") CSharp scripting package.</summary>
<description> <description>$commitPathMessage$</description>
Microsoft .NET Compiler Platform ("Roslyn") CSharp scripting package.
</description>
<language>en-US</language> <language>en-US</language>
<requireLicenseAcceptance>true</requireLicenseAcceptance> <requireLicenseAcceptance>true</requireLicenseAcceptance>
<version>$version$</version> <version>$version$</version>
......
...@@ -2,9 +2,10 @@ ...@@ -2,9 +2,10 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata minClientVersion="3.3"> <metadata minClientVersion="3.3">
<id>Microsoft.CodeAnalysis.CSharp.Workspaces</id> <id>Microsoft.CodeAnalysis.CSharp.Workspaces</id>
<description> <summary>
.NET Compiler Platform ("Roslyn") support for analyzing C# projects and solutions. .NET Compiler Platform ("Roslyn") support for analyzing C# projects and solutions.
</description> </summary>
<description>$commitPathMessage$</description>
<dependencies> <dependencies>
<dependency id="Microsoft.CodeAnalysis.CSharp" version="[$version$]" /> <dependency id="Microsoft.CodeAnalysis.CSharp" version="[$version$]" />
<dependency id="Microsoft.CodeAnalysis.Workspaces.Common" version="[$version$]" /> <dependency id="Microsoft.CodeAnalysis.Workspaces.Common" version="[$version$]" />
......
...@@ -2,11 +2,12 @@ ...@@ -2,11 +2,12 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata minClientVersion="3.3"> <metadata minClientVersion="3.3">
<id>Microsoft.CodeAnalysis.CSharp</id> <id>Microsoft.CodeAnalysis.CSharp</id>
<description> <summary>
.NET Compiler Platform ("Roslyn") support for C#, Microsoft.CodeAnalysis.CSharp.dll. .NET Compiler Platform ("Roslyn") support for C#, Microsoft.CodeAnalysis.CSharp.dll.
More details at https://aka.ms/roslyn-packages More details at https://aka.ms/roslyn-packages
</description> </summary>
<description>$commitPathMessage$</description>
<dependencies> <dependencies>
<dependency id="Microsoft.CodeAnalysis.Common" version="[$version$]" /> <dependency id="Microsoft.CodeAnalysis.Common" version="[$version$]" />
</dependencies> </dependencies>
......
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
<summary>A shared package used by the Microsoft .NET Compiler Platform ("Roslyn").</summary> <summary>A shared package used by the Microsoft .NET Compiler Platform ("Roslyn").</summary>
<description> <description>
A shared package used by the Microsoft .NET Compiler Platform ("Roslyn"). Do not install this package manually, it will be added as a prerequisite by other packages that require it. A shared package used by the Microsoft .NET Compiler Platform ("Roslyn"). Do not install this package manually, it will be added as a prerequisite by other packages that require it.
$commitPathMessage$
</description> </description>
<dependencies> <dependencies>
<group targetFramework="netstandard1.3"> <group targetFramework="netstandard1.3">
......
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
.NET Compiler Platform ("Roslyn"). Install this package to get both C# and Visual Basic support. Install either of the dependencies directly to get one of the languages separately. .NET Compiler Platform ("Roslyn"). Install this package to get both C# and Visual Basic support. Install either of the dependencies directly to get one of the languages separately.
More details at https://aka.ms/roslyn-packages More details at https://aka.ms/roslyn-packages
$commitPathMessage$
</description> </description>
<dependencies> <dependencies>
<dependency id="Microsoft.CodeAnalysis.CSharp" version="[$version$]" /> <dependency id="Microsoft.CodeAnalysis.CSharp" version="[$version$]" />
......
...@@ -2,11 +2,14 @@ ...@@ -2,11 +2,14 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata> <metadata>
<id>Microsoft.CodeAnalysis.EditorFeatures.Text</id> <id>Microsoft.CodeAnalysis.EditorFeatures.Text</id>
<description> <summary>
.NET Compiler Platform ("Roslyn") support for working with Visual Studio text buffers. .NET Compiler Platform ("Roslyn") support for working with Visual Studio text buffers.
</summary>
<description>
Supported Platforms:
- .NET Framework 4.6
Supported Platforms: $commitPathMessage$
- .NET Framework 4.6
</description> </description>
<dependencies> <dependencies>
<dependency id="Microsoft.CodeAnalysis.Workspaces.Common" version="[$version$]" /> <dependency id="Microsoft.CodeAnalysis.Workspaces.Common" version="[$version$]" />
......
...@@ -2,11 +2,14 @@ ...@@ -2,11 +2,14 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata> <metadata>
<id>Microsoft.CodeAnalysis.EditorFeatures</id> <id>Microsoft.CodeAnalysis.EditorFeatures</id>
<description> <summary>
.NET Compiler Platform ("Roslyn") support for editor features inside the Visual Studio editor.. .NET Compiler Platform ("Roslyn") support for editor features inside the Visual Studio editor..
</summary>
<description>
Supported Platforms:
- .NET Framework 4.6
Supported Platforms: $commitPathMessage$
- .NET Framework 4.6
</description> </description>
<dependencies> <dependencies>
<dependency id="Microsoft.CodeAnalysis.Features" version="[$version$]" /> <dependency id="Microsoft.CodeAnalysis.Features" version="[$version$]" />
......
...@@ -2,9 +2,10 @@ ...@@ -2,9 +2,10 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata minClientVersion="3.3"> <metadata minClientVersion="3.3">
<id>Microsoft.CodeAnalysis.Features</id> <id>Microsoft.CodeAnalysis.Features</id>
<description> <summary>
.NET Compiler Platform ("Roslyn") support for creating editing experiences. .NET Compiler Platform ("Roslyn") support for creating editing experiences.
</description> </summary>
<description>$commitPathMessage$</description>
<dependencies> <dependencies>
<dependency id="Microsoft.CodeAnalysis.Workspaces.Common" version="$version$" /> <dependency id="Microsoft.CodeAnalysis.Workspaces.Common" version="$version$" />
</dependencies> </dependencies>
......
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
Supported Platforms: Supported Platforms:
- .NET Framework 4.6 - .NET Framework 4.6
$commitPathMessage$
</description> </description>
<dependencies> <dependencies>
<dependency id="Microsoft.CodeAnalysis.Remote.Workspaces" version="[$version$]" /> <dependency id="Microsoft.CodeAnalysis.Remote.Workspaces" version="[$version$]" />
......
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
Supported Platforms: Supported Platforms:
- .NET Framework 4.6 - .NET Framework 4.6
$commitPathMessage$
</description> </description>
<dependencies> <dependencies>
<dependency id="Microsoft.CodeAnalysis.Remote.Workspaces" version="[$version$]" /> <dependency id="Microsoft.CodeAnalysis.Remote.Workspaces" version="[$version$]" />
......
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
Supported Platforms: Supported Platforms:
- .NET Framework 4.6 - .NET Framework 4.6
$commitPathMessage$
</description> </description>
<dependencies> <dependencies>
<dependency id="Microsoft.CodeAnalysis.Common" version="[$version$]" /> <dependency id="Microsoft.CodeAnalysis.Common" version="[$version$]" />
......
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
<summary>Microsoft .NET Compiler Platform ("Roslyn") shared scripting package.</summary> <summary>Microsoft .NET Compiler Platform ("Roslyn") shared scripting package.</summary>
<description> <description>
Microsoft .NET Compiler Platform ("Roslyn") shared scripting package. Do not install this package manually, it will be added as a prerequisite by other packages that require it. Microsoft .NET Compiler Platform ("Roslyn") shared scripting package. Do not install this package manually, it will be added as a prerequisite by other packages that require it.
$commitPathMessage$
</description> </description>
<language>en-US</language> <language>en-US</language>
......
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
<summary>Microsoft .NET Compiler Platform ("Roslyn") CSharp and VB scripting package.</summary> <summary>Microsoft .NET Compiler Platform ("Roslyn") CSharp and VB scripting package.</summary>
<description> <description>
Microsoft .NET Compiler Platform ("Roslyn") CSharp and VB scripting package. Microsoft .NET Compiler Platform ("Roslyn") CSharp and VB scripting package.
$commitPathMessage$
</description> </description>
<dependencies> <dependencies>
<dependency id="Microsoft.CodeAnalysis.CSharp.Scripting" version="[$version$]" /> <dependency id="Microsoft.CodeAnalysis.CSharp.Scripting" version="[$version$]" />
......
...@@ -2,9 +2,10 @@ ...@@ -2,9 +2,10 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata minClientVersion="3.3"> <metadata minClientVersion="3.3">
<id>Microsoft.CodeAnalysis.VisualBasic.CodeStyle</id> <id>Microsoft.CodeAnalysis.VisualBasic.CodeStyle</id>
<description> <summary>
.NET Compiler Platform ("Roslyn") code style analyzers for Visual Basic. .NET Compiler Platform ("Roslyn") code style analyzers for Visual Basic.
</description> </summary>
<description>$commitPathMessage$</description>
<dependencies> <dependencies>
</dependencies> </dependencies>
......
...@@ -2,9 +2,10 @@ ...@@ -2,9 +2,10 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata minClientVersion="3.3"> <metadata minClientVersion="3.3">
<id>Microsoft.CodeAnalysis.VisualBasic.Features</id> <id>Microsoft.CodeAnalysis.VisualBasic.Features</id>
<description> <summary>
.NET Compiler Platform ("Roslyn") support for creating Visual Basic editing experiences. .NET Compiler Platform ("Roslyn") support for creating Visual Basic editing experiences.
</description> </summary>
<description>$commitPathMessage$</description>
<dependencies> <dependencies>
<dependency id="Microsoft.CodeAnalysis.Features" version="$version$" /> <dependency id="Microsoft.CodeAnalysis.Features" version="$version$" />
<dependency id="Microsoft.CodeAnalysis.VisualBasic.Workspaces" version="$version$" /> <dependency id="Microsoft.CodeAnalysis.VisualBasic.Workspaces" version="$version$" />
......
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
<summary>Microsoft .NET Compiler Platform ("Roslyn") Visual Basic scripting package.</summary> <summary>Microsoft .NET Compiler Platform ("Roslyn") Visual Basic scripting package.</summary>
<description> <description>
Microsoft .NET Compiler Platform ("Roslyn") Visual Basic scripting package. Microsoft .NET Compiler Platform ("Roslyn") Visual Basic scripting package.
$commitPathMessage$
</description> </description>
<language>en-US</language> <language>en-US</language>
......
...@@ -2,9 +2,10 @@ ...@@ -2,9 +2,10 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata minClientVersion="3.3"> <metadata minClientVersion="3.3">
<id>Microsoft.CodeAnalysis.VisualBasic.Workspaces</id> <id>Microsoft.CodeAnalysis.VisualBasic.Workspaces</id>
<description> <summary>
.NET Compiler Platform ("Roslyn") support for analyzing Visual Basic projects and solutions. .NET Compiler Platform ("Roslyn") support for analyzing Visual Basic projects and solutions.
</description> </summary>
<description>$commitPathMessage$</description>
<dependencies> <dependencies>
<dependency id="Microsoft.CodeAnalysis.VisualBasic" version="[$version$]" /> <dependency id="Microsoft.CodeAnalysis.VisualBasic" version="[$version$]" />
<dependency id="Microsoft.CodeAnalysis.Workspaces.Common" version="[$version$]" /> <dependency id="Microsoft.CodeAnalysis.Workspaces.Common" version="[$version$]" />
......
...@@ -2,11 +2,12 @@ ...@@ -2,11 +2,12 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata minClientVersion="3.3"> <metadata minClientVersion="3.3">
<id>Microsoft.CodeAnalysis.VisualBasic</id> <id>Microsoft.CodeAnalysis.VisualBasic</id>
<description> <summary>
.NET Compiler Platform ("Roslyn") support for Visual Basic, Microsoft.CodeAnalysis.VisualBasic.dll. .NET Compiler Platform ("Roslyn") support for Visual Basic, Microsoft.CodeAnalysis.VisualBasic.dll.
More details at https://aka.ms/roslyn-packages More details at https://aka.ms/roslyn-packages
</description> </summary>
<description>$commitPathMessage$</description>
<dependencies> <dependencies>
<dependency id="Microsoft.CodeAnalysis.Common" version="$version$" /> <dependency id="Microsoft.CodeAnalysis.Common" version="$version$" />
</dependencies> </dependencies>
......
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
</summary> </summary>
<description> <description>
A shared package used by the .NET Compiler Platform ("Roslyn") including support for analyzing projects and solutions. Do not install this package manually, it will be added as a prerequisite by other packages that require it. A shared package used by the .NET Compiler Platform ("Roslyn") including support for analyzing projects and solutions. Do not install this package manually, it will be added as a prerequisite by other packages that require it.
$commitPathMessage$
</description> </description>
<dependencies> <dependencies>
<group targetFramework="netstandard1.3"> <group targetFramework="netstandard1.3">
......
...@@ -12,6 +12,8 @@ ...@@ -12,6 +12,8 @@
- "Microsoft.CodeAnalysis.Compilers" (both compilers) - "Microsoft.CodeAnalysis.Compilers" (both compilers)
- "Microsoft.CodeAnalysis.CSharp" (only the C# compiler) - "Microsoft.CodeAnalysis.CSharp" (only the C# compiler)
- "Microsoft.CodeAnalysis.VisualBasic (only the VB compiler) - "Microsoft.CodeAnalysis.VisualBasic (only the VB compiler)
$commitPathMessage$
</description> </description>
<dependencies> <dependencies>
<dependency id="Microsoft.CodeAnalysis.CSharp.Workspaces" version="[$version$]" /> <dependency id="Microsoft.CodeAnalysis.CSharp.Workspaces" version="[$version$]" />
......
...@@ -2,9 +2,10 @@ ...@@ -2,9 +2,10 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata> <metadata>
<id>Microsoft.NETCore.Compilers</id> <id>Microsoft.NETCore.Compilers</id>
<description> <summary>
CoreCLR-compatible versions of the C# and VB compilers for use in MSBuild. CoreCLR-compatible versions of the C# and VB compilers for use in MSBuild.
</description> </summary>
<description>$commitPathMessage$</description>
<language>en-US</language> <language>en-US</language>
<requireLicenseAcceptance>true</requireLicenseAcceptance> <requireLicenseAcceptance>true</requireLicenseAcceptance>
<version>$version$</version> <version>$version$</version>
......
...@@ -2,12 +2,13 @@ ...@@ -2,12 +2,13 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata> <metadata>
<id>Microsoft.Net.CSharp.Interactive.netcore</id> <id>Microsoft.Net.CSharp.Interactive.netcore</id>
<description> <summary>
CoreCLR-compatible version of csi.exe. CoreCLR-compatible version of csi.exe.
Supported Platforms: Supported Platforms:
- .NET Core (NETCoreApp1.0) - .NET Core (NETCoreApp1.0)
</description> </summary>
<description>$commitPathMessage$</description>
<language>en-US</language> <language>en-US</language>
<requireLicenseAcceptance>true</requireLicenseAcceptance> <requireLicenseAcceptance>true</requireLicenseAcceptance>
<version>$version$</version> <version>$version$</version>
......
...@@ -2,9 +2,10 @@ ...@@ -2,9 +2,10 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata> <metadata>
<id>Microsoft.Net.Compilers.netcore</id> <id>Microsoft.Net.Compilers.netcore</id>
<description> <summary>
CoreCLR-compatible versions of the C# and VB compilers. CoreCLR-compatible versions of the C# and VB compilers.
</description> </summary>
<description>$commitPathMessage$</description>
<language>en-US</language> <language>en-US</language>
<requireLicenseAcceptance>true</requireLicenseAcceptance> <requireLicenseAcceptance>true</requireLicenseAcceptance>
<version>$version$</version> <version>$version$</version>
......
...@@ -2,11 +2,12 @@ ...@@ -2,11 +2,12 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata> <metadata>
<id>Microsoft.Net.Compilers</id> <id>Microsoft.Net.Compilers</id>
<description> <summary>
.Net Compilers package. Referencing this package will cause the project to be built using the specific version of the C# and Visual Basic compilers contained in the package, as opposed to any system installed version. .Net Compilers package. Referencing this package will cause the project to be built using the specific version of the C# and Visual Basic compilers contained in the package, as opposed to any system installed version.
This package can be used to compile code targeting any platform, but can only be run using the desktop .NET 4.6+ Full Framework. This package can be used to compile code targeting any platform, but can only be run using the desktop .NET 4.6+ Full Framework.
</description> </summary>
<description>$commitPathMessage$</description>
<language>en-US</language> <language>en-US</language>
<requireLicenseAcceptance>true</requireLicenseAcceptance> <requireLicenseAcceptance>true</requireLicenseAcceptance>
<version>$version$</version> <version>$version$</version>
......
...@@ -2,7 +2,8 @@ ...@@ -2,7 +2,8 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata> <metadata>
<id>Microsoft.VisualStudio.IntegrationTest.Utilities</id> <id>Microsoft.VisualStudio.IntegrationTest.Utilities</id>
<description>Utility methods used to run Visual Studio integration tests.</description> <summary>Utility methods used to run Visual Studio integration tests.</summary>
<description>$commitPathMessage$</description>
<language>en-US</language> <language>en-US</language>
<requireLicenseAcceptance>true</requireLicenseAcceptance> <requireLicenseAcceptance>true</requireLicenseAcceptance>
<version>$version$</version> <version>$version$</version>
......
...@@ -2,12 +2,13 @@ ...@@ -2,12 +2,13 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata> <metadata>
<id>Microsoft.VisualStudio.LanguageServices.Next</id> <id>Microsoft.VisualStudio.LanguageServices.Next</id>
<description> <summary>
.NET Compiler Platform ("Roslyn") support for Visual Studio "15". .NET Compiler Platform ("Roslyn") support for Visual Studio "15".
Supported Platforms: Supported Platforms:
- .NET Framework 4.6 - .NET Framework 4.6
</description> </summary>
<description>$commitPathMessage$</description>
<dependencies> <dependencies>
<dependency id="Microsoft.CodeAnalysis.Remote.ServiceHub" version="[$version$]" /> <dependency id="Microsoft.CodeAnalysis.Remote.ServiceHub" version="[$version$]" />
<dependency id="Microsoft.CodeAnalysis.Remote.Workspaces" version="[$version$]" /> <dependency id="Microsoft.CodeAnalysis.Remote.Workspaces" version="[$version$]" />
......
...@@ -2,11 +2,14 @@ ...@@ -2,11 +2,14 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata> <metadata>
<id>Microsoft.VisualStudio.LanguageServices.Razor.RemoteClient</id> <id>Microsoft.VisualStudio.LanguageServices.Razor.RemoteClient</id>
<description> <summary>
.NET Compiler Platform ("Roslyn") support for Visual Studio "15". .NET Compiler Platform ("Roslyn") support for Visual Studio "15".
</summary>
<description>
Supported Platforms:
- .NET Framework 4.6
Supported Platforms: $commitPathMessage$
- .NET Framework 4.6
</description> </description>
<dependencies> <dependencies>
<dependency id="Microsoft.CodeAnalysis.Workspaces.Common" version="[$version$]" /> <dependency id="Microsoft.CodeAnalysis.Workspaces.Common" version="[$version$]" />
......
...@@ -2,12 +2,13 @@ ...@@ -2,12 +2,13 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata> <metadata>
<id>Microsoft.VisualStudio.LanguageServices</id> <id>Microsoft.VisualStudio.LanguageServices</id>
<description> <summary>
.NET Compiler Platform ("Roslyn") support for Visual Studio. .NET Compiler Platform ("Roslyn") support for Visual Studio.
Supported Platforms: Supported Platforms:
- .NET Framework 4.6 - .NET Framework 4.6
</description> </summary>
<description>$commitPathMessage$</description>
<dependencies> <dependencies>
<dependency id="Microsoft.CodeAnalysis" version="[$version$]" /> <dependency id="Microsoft.CodeAnalysis" version="[$version$]" />
</dependencies> </dependencies>
......
...@@ -5,13 +5,13 @@ ...@@ -5,13 +5,13 @@
<Target Name="Build"> <Target Name="Build">
<!-- NuGetPerBuildPreReleaseVersion --> <!-- NuGetPerBuildPreReleaseVersion -->
<Exec Command="$(OutputPath)Exes\csi\csi.exe $(MSBuildThisFileDirectory)BuildNuGets.csx $(OutputPath) $(NuGetPerBuildPreReleaseVersion) $(OutputPath)NuGet\PerBuildPreRelease" Condition="'$(NuGetPerBuildPreReleaseVersion)' != ''" /> <Exec Command="$(OutputPath)Exes\csi\csi.exe $(MSBuildThisFileDirectory)BuildNuGets.csx $(OutputPath) $(NuGetPerBuildPreReleaseVersion) $(OutputPath)NuGet\PerBuildPreRelease &quot;$(GitHeadSha)&quot;" Condition="'$(NuGetPerBuildPreReleaseVersion)' != ''" />
<!-- NuGetPreReleaseVersion --> <!-- NuGetPreReleaseVersion -->
<Exec Command="$(OutputPath)Exes\csi\csi.exe $(MSBuildThisFileDirectory)BuildNuGets.csx $(OutputPath) $(NuGetPreReleaseVersion) $(OutputPath)NuGet\PreRelease" Condition="'$(NuGetPreReleaseVersion)' != ''" /> <Exec Command="$(OutputPath)Exes\csi\csi.exe $(MSBuildThisFileDirectory)BuildNuGets.csx $(OutputPath) $(NuGetPreReleaseVersion) $(OutputPath)NuGet\PreRelease &quot;$(GitHeadSha)&quot;" Condition="'$(NuGetPreReleaseVersion)' != ''" />
<!-- NuGetReleaseVersion --> <!-- NuGetReleaseVersion -->
<Exec Command="$(OutputPath)Exes\csi\csi.exe $(MSBuildThisFileDirectory)BuildNuGets.csx $(OutputPath) $(NuGetReleaseVersion) $(OutputPath)NuGet\Release" Condition="'$(NuGetReleaseVersion)' != ''" /> <Exec Command="$(OutputPath)Exes\csi\csi.exe $(MSBuildThisFileDirectory)BuildNuGets.csx $(OutputPath) $(NuGetReleaseVersion) $(OutputPath)NuGet\Release &quot;$(GitHeadSha)&quot;" Condition="'$(NuGetReleaseVersion)' != ''" />
</Target> </Target>
<Target Name="Clean"> <Target Name="Clean">
......
...@@ -15,7 +15,7 @@ internal sealed class FileChangeTracker : IVsFileChangeEvents, IDisposable ...@@ -15,7 +15,7 @@ internal sealed class FileChangeTracker : IVsFileChangeEvents, IDisposable
{ {
private const uint FileChangeFlags = (uint)(_VSFILECHANGEFLAGS.VSFILECHG_Time | _VSFILECHANGEFLAGS.VSFILECHG_Add | _VSFILECHANGEFLAGS.VSFILECHG_Del | _VSFILECHANGEFLAGS.VSFILECHG_Size); private const uint FileChangeFlags = (uint)(_VSFILECHANGEFLAGS.VSFILECHG_Time | _VSFILECHANGEFLAGS.VSFILECHG_Add | _VSFILECHANGEFLAGS.VSFILECHG_Del | _VSFILECHANGEFLAGS.VSFILECHG_Size);
private static readonly Lazy<uint> s_none = new Lazy<uint>(() => /* value doesn't matter*/ 42424242, LazyThreadSafetyMode.ExecutionAndPublication); private static readonly Lazy<uint?> s_none = new Lazy<uint?>(() => null, LazyThreadSafetyMode.ExecutionAndPublication);
private readonly IVsFileChangeEx _fileChangeService; private readonly IVsFileChangeEx _fileChangeService;
private readonly string _filePath; private readonly string _filePath;
...@@ -23,12 +23,30 @@ internal sealed class FileChangeTracker : IVsFileChangeEvents, IDisposable ...@@ -23,12 +23,30 @@ internal sealed class FileChangeTracker : IVsFileChangeEvents, IDisposable
/// <summary> /// <summary>
/// The cookie received from the IVsFileChangeEx interface that is watching for changes to /// The cookie received from the IVsFileChangeEx interface that is watching for changes to
/// this file. /// this file. This field may never be null, but might be a Lazy that has a value of null if
/// we either failed to subscribe over never have tried to subscribe.
/// </summary> /// </summary>
private Lazy<uint> _fileChangeCookie; private Lazy<uint?> _fileChangeCookie;
public event EventHandler UpdatedOnDisk; public event EventHandler UpdatedOnDisk;
/// <summary>
/// Operations on <see cref="IVsFileChangeEx"/> synchronize on a single lock within that service, so there's no point
/// in us trying to have multiple threads all trying to use it at the same time. When we queue a new background thread operation
/// we'll just do a continuation after the previous one. Any callers of <see cref="EnsureSubscription"/> will bypass that queue
/// and ensure it happens quickly.
/// </summary>
private static Task s_lastBackgroundTask = Task.CompletedTask;
/// <summary>
/// The object to use as a monitor guarding <see cref="s_lastBackgroundTask"/>. This lock is not strictly necessary, since we don't need
/// to ensure the background tasks happen entirely sequentially -- if we just removed the lock, and two subscriptions happened, we end up with
/// a 'branching' set of continuations, but that's fine since we're generally not running things in parallel. But it's easy to write,
/// and easy to delete if this lock has contention itself. Given we tend to call <see cref="StartFileChangeListeningAsync"/> on the UI
/// thread, I don't expect to see contention.
/// </summary>
private static readonly object s_lastBackgroundTaskGate = new object();
public FileChangeTracker(IVsFileChangeEx fileChangeService, string filePath) public FileChangeTracker(IVsFileChangeEx fileChangeService, string filePath)
{ {
_fileChangeService = fileChangeService; _fileChangeService = fileChangeService;
...@@ -65,44 +83,81 @@ public void StartFileChangeListeningAsync() ...@@ -65,44 +83,81 @@ public void StartFileChangeListeningAsync()
{ {
if (_disposed) if (_disposed)
{ {
throw new ObjectDisposedException(typeof(FileChangeTracker).Name); throw new ObjectDisposedException(nameof(FileChangeTracker));
} }
Contract.ThrowIfTrue(_fileChangeCookie != s_none); Contract.ThrowIfTrue(_fileChangeCookie != s_none);
_fileChangeCookie = new Lazy<uint>(() => _fileChangeCookie = new Lazy<uint?>(() =>
{ {
Marshal.ThrowExceptionForHR( try
_fileChangeService.AdviseFileChange(_filePath, FileChangeFlags, this, out var newCookie)); {
return newCookie; Marshal.ThrowExceptionForHR(
_fileChangeService.AdviseFileChange(_filePath, FileChangeFlags, this, out var newCookie));
return newCookie;
}
catch (Exception e) when (ShouldTrapException(e))
{
return null;
}
}, LazyThreadSafetyMode.ExecutionAndPublication); }, LazyThreadSafetyMode.ExecutionAndPublication);
// file change service is free-threaded. start running it in background right away lock (s_lastBackgroundTaskGate)
Task.Run(() => _fileChangeCookie.Value, CancellationToken.None); {
s_lastBackgroundTask = s_lastBackgroundTask.ContinueWith(_ => _fileChangeCookie.Value, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Default);
}
}
private static bool ShouldTrapException(Exception e)
{
if (e is FileNotFoundException)
{
// The IVsFileChange implementation shouldn't ever be throwing exceptions like this, but it's a
// transient file system issue (perhaps the file being deleted while we're changing subscriptions)
// and so there's nothing better to do. We'll still non-fatal to track the rate this is happening
return FatalError.ReportWithoutCrash(e);
}
else if (e is PathTooLongException)
{
// Nothing better we can do. We won't be able to open this file either, and thus we'll do our usual
// reporting of unopenable/missing files to the output window as usual.
return true;
}
else
{
return false;
}
} }
public void StopFileChangeListening() public void StopFileChangeListening()
{ {
if (_disposed) if (_disposed)
{ {
throw new ObjectDisposedException(typeof(FileChangeTracker).Name); throw new ObjectDisposedException(nameof(FileChangeTracker));
} }
// there is a slight chance that we haven't subscribed to the service yet so we subscribe and unsubscribe // there is a slight chance that we haven't subscribed to the service yet so we subscribe and unsubscribe
// both here unnecessarily. but I believe that probably is a theoretical problem and never happen in real life. // both here unnecessarily. but I believe that probably is a theoretical problem and never happen in real life.
// and even if that happens, it will be just a perf hit // and even if that happens, it will be just a perf hit
if (_fileChangeCookie != s_none) if (_fileChangeCookie == s_none)
{ {
var hr = _fileChangeService.UnadviseFileChange(_fileChangeCookie.Value); return;
}
var fileChangeCookie = _fileChangeCookie.Value;
_fileChangeCookie = s_none;
// Verify if the file still exists before reporting the unadvise failure. // We may have tried to subscribe but failed, so have to check a second time
// This is a workaround for VSO #248774 if (fileChangeCookie.HasValue)
if (hr != VSConstants.S_OK && File.Exists(_filePath)) {
try
{
Marshal.ThrowExceptionForHR(
_fileChangeService.UnadviseFileChange(fileChangeCookie.Value));
}
catch (Exception e) when (ShouldTrapException(e))
{ {
Marshal.ThrowExceptionForHR(hr);
} }
_fileChangeCookie = s_none;
} }
} }
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Reflection;
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.VisualStudio.LanguageServices.Implementation.TaskList; using Microsoft.VisualStudio.LanguageServices.Implementation.TaskList;
...@@ -31,7 +32,6 @@ public VisualStudioAnalyzer(string fullPath, IVsFileChangeEx fileChangeService, ...@@ -31,7 +32,6 @@ public VisualStudioAnalyzer(string fullPath, IVsFileChangeEx fileChangeService,
_tracker = new FileChangeTracker(fileChangeService, fullPath); _tracker = new FileChangeTracker(fileChangeService, fullPath);
_tracker.UpdatedOnDisk += OnUpdatedOnDisk; _tracker.UpdatedOnDisk += OnUpdatedOnDisk;
_tracker.StartFileChangeListeningAsync(); _tracker.StartFileChangeListeningAsync();
_tracker.EnsureSubscription();
_hostDiagnosticUpdateSource = hostDiagnosticUpdateSource; _hostDiagnosticUpdateSource = hostDiagnosticUpdateSource;
_projectId = projectId; _projectId = projectId;
_workspace = workspace; _workspace = workspace;
...@@ -55,7 +55,9 @@ public AnalyzerReference GetReference() ...@@ -55,7 +55,9 @@ public AnalyzerReference GetReference()
{ {
if (File.Exists(_fullPath)) if (File.Exists(_fullPath))
{ {
_analyzerReference = new AnalyzerFileReference(_fullPath, _loader); // Pass down a custom loader that will ensure we are watching for file changes once we actually load the assembly.
var assemblyLoaderForFileTracker = new AnalyzerAssemblyLoaderThatEnsuresFileBeingWatched(this);
_analyzerReference = new AnalyzerFileReference(_fullPath, assemblyLoaderForFileTracker);
((AnalyzerFileReference)_analyzerReference).AnalyzerLoadFailed += OnAnalyzerLoadError; ((AnalyzerFileReference)_analyzerReference).AnalyzerLoadFailed += OnAnalyzerLoadError;
} }
else else
...@@ -108,5 +110,30 @@ private void OnUpdatedOnDisk(object sender, EventArgs e) ...@@ -108,5 +110,30 @@ private void OnUpdatedOnDisk(object sender, EventArgs e)
{ {
UpdatedOnDisk?.Invoke(this, EventArgs.Empty); UpdatedOnDisk?.Invoke(this, EventArgs.Empty);
} }
/// <summary>
/// This custom loader just wraps an existing loader, but ensures that we start listening to the file
/// for changes once we've actually looked at the file.
/// </summary>
private class AnalyzerAssemblyLoaderThatEnsuresFileBeingWatched : IAnalyzerAssemblyLoader
{
private readonly VisualStudioAnalyzer _analyzer;
public AnalyzerAssemblyLoaderThatEnsuresFileBeingWatched(VisualStudioAnalyzer analyzer)
{
_analyzer = analyzer;
}
public void AddDependencyLocation(string fullPath)
{
_analyzer._loader.AddDependencyLocation(fullPath);
}
public Assembly LoadFromPath(string fullPath)
{
_analyzer._tracker.EnsureSubscription();
return _analyzer._loader.LoadFromPath(fullPath);
}
}
} }
} }
...@@ -139,7 +139,7 @@ internal void SolutionClosed() ...@@ -139,7 +139,7 @@ internal void SolutionClosed()
var solutionInfo = SolutionInfo.Create(id, version.Value, solutionFilePath, projects: projectInfos); var solutionInfo = SolutionInfo.Create(id, version.Value, solutionFilePath, projects: projectInfos);
_tracker.NotifyWorkspaceHosts(host => host.OnSolutionAdded(solutionInfo)); _workspaceHost.OnSolutionAdded(solutionInfo);
_solutionAdded = true; _solutionAdded = true;
} }
......
...@@ -33,13 +33,6 @@ private class WorkspaceHost : ForegroundThreadAffinitizedObject, IVisualStudioWo ...@@ -33,13 +33,6 @@ private class WorkspaceHost : ForegroundThreadAffinitizedObject, IVisualStudioWo
_currentSolutionId = workspace.CurrentSolution.Id; _currentSolutionId = workspace.CurrentSolution.Id;
} }
public Task InitializeAsync()
{
// Ensure that we populate the remote service with the initial state of
// the workspace's solution.
return RegisterPrimarySolutionAsync();
}
public void OnAfterWorkingFolderChange() public void OnAfterWorkingFolderChange()
{ {
this.AssertIsForeground(); this.AssertIsForeground();
...@@ -57,17 +50,18 @@ private async Task RegisterPrimarySolutionAsync() ...@@ -57,17 +50,18 @@ private async Task RegisterPrimarySolutionAsync()
_currentSolutionId = _workspace.CurrentSolution.Id; _currentSolutionId = _workspace.CurrentSolution.Id;
var solutionId = _currentSolutionId; var solutionId = _currentSolutionId;
using (var session = await _client.TryCreateSessionAsync(WellKnownRemoteHostServices.RemoteHostService, _workspace.CurrentSolution, CancellationToken.None).ConfigureAwait(false)) using (var connection = await _client.TryCreateConnectionAsync(WellKnownRemoteHostServices.RemoteHostService, CancellationToken.None).ConfigureAwait(false))
{ {
if (session == null) if (connection == null)
{ {
// failed to create session. remote host might not responding or gone. // failed to create connection. remote host might not responding or gone.
return; return;
} }
await session.InvokeAsync(nameof(IRemoteHostService.RegisterPrimarySolutionId), solutionId).ConfigureAwait(false); await connection.InvokeAsync(
nameof(IRemoteHostService.RegisterPrimarySolutionId), solutionId).ConfigureAwait(false);
await session.InvokeAsync( await connection.InvokeAsync(
nameof(IRemoteHostService.UpdateSolutionIdStorageLocation), solutionId, nameof(IRemoteHostService.UpdateSolutionIdStorageLocation), solutionId,
_workspace.DeferredState?.ProjectTracker.GetWorkingFolderPath(_workspace.CurrentSolution)).ConfigureAwait(false); _workspace.DeferredState?.ProjectTracker.GetWorkingFolderPath(_workspace.CurrentSolution)).ConfigureAwait(false);
} }
......
...@@ -73,28 +73,16 @@ private static async Task RegisterWorkspaceHostAsync(Workspace workspace, Remote ...@@ -73,28 +73,16 @@ private static async Task RegisterWorkspaceHostAsync(Workspace workspace, Remote
return; return;
} }
// don't block UI thread while initialize workspace host
var host = new WorkspaceHost(vsWorkspace, client);
// Initialize the remote side with whatever data we have currently for the workspace.
// As workspace changes happen, this host will get notified, and it can remote those
// changes appropriately over to the remote size.
await host.InitializeAsync().ConfigureAwait(false);
// RegisterWorkspaceHost is required to be called from UI thread so push the code // RegisterWorkspaceHost is required to be called from UI thread so push the code
// to UI thread to run. // to UI thread to run.
await Task.Factory.SafeStartNew(() => await Task.Factory.SafeStartNew(() =>
{ {
vsWorkspace.GetProjectTrackerAndInitializeIfNecessary(Shell.ServiceProvider.GlobalProvider).RegisterWorkspaceHost(host); var projectTracker = vsWorkspace.GetProjectTrackerAndInitializeIfNecessary(Shell.ServiceProvider.GlobalProvider);
// There may have been notifications fired by the workspace between the time we var host = new WorkspaceHost(vsWorkspace, client);
// were created and now when we let it know about us. Because of that, we need
// to do another initialization pass to make sure all the current workpsace projectTracker.RegisterWorkspaceHost(host);
// state is pushed over to the remote side. projectTracker.StartSendingEventsToWorkspaceHost(host);
//
// We can do this in a fire and forget manner. We don't want to block the UI
// thread while we're pushing this data over.
Task.Run(() => host.InitializeAsync());
}, CancellationToken.None, ForegroundThreadAffinitizedObject.CurrentForegroundThreadData.TaskScheduler).ConfigureAwait(false); }, CancellationToken.None, ForegroundThreadAffinitizedObject.CurrentForegroundThreadData.TaskScheduler).ConfigureAwait(false);
} }
......
...@@ -12,7 +12,7 @@ namespace Roslyn.VisualStudio.IntegrationTests.CSharp ...@@ -12,7 +12,7 @@ namespace Roslyn.VisualStudio.IntegrationTests.CSharp
[Collection(nameof(SharedIntegrationHostFixture))] [Collection(nameof(SharedIntegrationHostFixture))]
public class CSharpSendToInteractive : AbstractInteractiveWindowTest public class CSharpSendToInteractive : AbstractInteractiveWindowTest
{ {
private const string FileName = "test.cs"; private const string FileName = "Program.cs";
public CSharpSendToInteractive(VisualStudioInstanceFactory instanceFactory) public CSharpSendToInteractive(VisualStudioInstanceFactory instanceFactory)
: base(instanceFactory) : base(instanceFactory)
...@@ -21,16 +21,16 @@ public CSharpSendToInteractive(VisualStudioInstanceFactory instanceFactory) ...@@ -21,16 +21,16 @@ public CSharpSendToInteractive(VisualStudioInstanceFactory instanceFactory)
var project = new Project(ProjectName); var project = new Project(ProjectName);
VisualStudio.SolutionExplorer.AddProject(project, WellKnownProjectTemplates.ConsoleApplication, Microsoft.CodeAnalysis.LanguageNames.CSharp); VisualStudio.SolutionExplorer.AddProject(project, WellKnownProjectTemplates.ConsoleApplication, Microsoft.CodeAnalysis.LanguageNames.CSharp);
VisualStudio.SolutionExplorer.AddFile( VisualStudio.SolutionExplorer.UpdateFile(
project, ProjectName,
FileName, FileName,
@"using System; @"using System;
namespace TestProj namespace TestProj
{ {
public class Program1 public class Program
{ {
public static void Main1(string[] args) public static void Main(string[] args)
{ {
/* 1 */int x = 1;/* 2 */ /* 1 */int x = 1;/* 2 */
...@@ -51,7 +51,9 @@ public string M() ...@@ -51,7 +51,9 @@ public string M()
return ""C.M()""; return ""C.M()"";
} }
} }
}"); }
",
open: true);
VisualStudio.InteractiveWindow.SubmitText("using System;"); VisualStudio.InteractiveWindow.SubmitText("using System;");
} }
...@@ -214,7 +216,7 @@ public void AddAssemblyReferenceAndTypesToInteractive() ...@@ -214,7 +216,7 @@ public void AddAssemblyReferenceAndTypesToInteractive()
VisualStudio.Workspace.WaitForAsyncOperations(FeatureAttribute.SolutionCrawler); VisualStudio.Workspace.WaitForAsyncOperations(FeatureAttribute.SolutionCrawler);
} }
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/19441")] [Fact]
public void ResetInteractiveFromProjectAndVerify() public void ResetInteractiveFromProjectAndVerify()
{ {
var assembly = new ProjectUtils.AssemblyReference("System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); var assembly = new ProjectUtils.AssemblyReference("System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
......
...@@ -286,41 +286,42 @@ private string GetProjectTemplatePath(string projectTemplate, string languageNam ...@@ -286,41 +286,42 @@ private string GetProjectTemplatePath(string projectTemplate, string languageNam
} }
public void CleanUpOpenSolution() public void CleanUpOpenSolution()
{ => InvokeOnUIThread(() =>
var dte = GetDTE();
dte.Documents.CloseAll(EnvDTE.vsSaveChanges.vsSaveChangesNo);
if (dte.Solution != null)
{ {
var directoriesToDelete = new List<string>(); var dte = GetDTE();
dte.Documents.CloseAll(EnvDTE.vsSaveChanges.vsSaveChangesNo);
// Save the full path to each project in the solution. This is so we can if (dte.Solution != null)
// cleanup any folders after the solution is closed.
foreach (EnvDTE.Project project in dte.Solution.Projects)
{ {
if (!string.IsNullOrEmpty(project.FullName)) var directoriesToDelete = new List<string>();
// Save the full path to each project in the solution. This is so we can
// cleanup any folders after the solution is closed.
foreach (EnvDTE.Project project in dte.Solution.Projects)
{ {
directoriesToDelete.Add(Path.GetDirectoryName(project.FullName)); if (!string.IsNullOrEmpty(project.FullName))
{
directoriesToDelete.Add(Path.GetDirectoryName(project.FullName));
}
} }
}
// Save the full path to the solution. This is so we can cleanup any folders after the solution is closed. // Save the full path to the solution. This is so we can cleanup any folders after the solution is closed.
// The solution might be zero-impact and thus has no name, so deal with that // The solution might be zero-impact and thus has no name, so deal with that
var solutionFullName = dte.Solution.FullName; var solutionFullName = dte.Solution.FullName;
if (!string.IsNullOrEmpty(solutionFullName)) if (!string.IsNullOrEmpty(solutionFullName))
{ {
directoriesToDelete.Add(Path.GetDirectoryName(solutionFullName)); directoriesToDelete.Add(Path.GetDirectoryName(solutionFullName));
} }
dte.Solution.Close(SaveFirst: false); dte.Solution.Close(SaveFirst: false);
foreach (var directoryToDelete in directoriesToDelete) foreach (var directoryToDelete in directoriesToDelete)
{ {
IntegrationHelper.TryDeleteDirectoryRecursively(directoryToDelete); IntegrationHelper.TryDeleteDirectoryRecursively(directoryToDelete);
}
} }
} });
}
private EnvDTE.Project GetProject(string nameOrFileName) private EnvDTE.Project GetProject(string nameOrFileName)
=> _solution.Projects.OfType<EnvDTE.Project>().First(p => _solution.Projects.OfType<EnvDTE.Project>().First(p
......
...@@ -90,6 +90,9 @@ public void OpenFileWithDesigner(ProjectUtils.Project project, string fileName) ...@@ -90,6 +90,9 @@ public void OpenFileWithDesigner(ProjectUtils.Project project, string fileName)
public void OpenFile(ProjectUtils.Project project, string fileName) public void OpenFile(ProjectUtils.Project project, string fileName)
=> _inProc.OpenFile(project.Name, fileName); => _inProc.OpenFile(project.Name, fileName);
public void UpdateFile(string projectName, string fileName, string contents, bool open = false)
=> _inProc.UpdateFile(projectName, fileName, contents, open);
public void RenameFile(ProjectUtils.Project project, string oldFileName, string newFileName) public void RenameFile(ProjectUtils.Project project, string oldFileName, string newFileName)
=> _inProc.RenameFile(project.Name, oldFileName, newFileName); => _inProc.RenameFile(project.Name, oldFileName, newFileName);
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // 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;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Shared.Utilities;
...@@ -15,6 +18,7 @@ ...@@ -15,6 +18,7 @@
namespace Microsoft.CodeAnalysis.FindSymbols namespace Microsoft.CodeAnalysis.FindSymbols
{ {
using SymbolAndProjectIdSet = HashSet<SymbolAndProjectId<INamedTypeSymbol>>; using SymbolAndProjectIdSet = HashSet<SymbolAndProjectId<INamedTypeSymbol>>;
using RelatedTypeCache = ConditionalWeakTable<Solution, ConcurrentDictionary<(SymbolKey, IImmutableSet<Project>), AsyncLazy<ImmutableArray<(SymbolKey, ProjectId)>>>>;
/// <summary> /// <summary>
/// Provides helper methods for finding dependent types (derivations, implementations, /// Provides helper methods for finding dependent types (derivations, implementations,
...@@ -39,17 +43,104 @@ internal static class DependentTypeFinder ...@@ -39,17 +43,104 @@ internal static class DependentTypeFinder
private static readonly ObjectPool<SymbolAndProjectIdSet> s_setPool = new ObjectPool<SymbolAndProjectIdSet>( private static readonly ObjectPool<SymbolAndProjectIdSet> s_setPool = new ObjectPool<SymbolAndProjectIdSet>(
() => new SymbolAndProjectIdSet(SymbolAndProjectIdComparer<INamedTypeSymbol>.SymbolEquivalenceInstance)); () => new SymbolAndProjectIdSet(SymbolAndProjectIdComparer<INamedTypeSymbol>.SymbolEquivalenceInstance));
// Caches from a types to their related types (in the context of a specific solution).
// Kept as a cache so that clients who make many calls into us won't end up computing
// the same data over and over again. Will be let go the moment the solution they're
// based off of is no longer alive.
//
// Importantly, the caches only store SymbolKeys and Ids. As such, they will not hold
// any Symbols or Compilations alive.
private static readonly RelatedTypeCache s_typeToImmediatelyDerivedClassesMap = new RelatedTypeCache();
private static readonly RelatedTypeCache s_typeToTransitivelyDerivedClassesMap = new RelatedTypeCache();
private static readonly RelatedTypeCache s_typeToTransitivelyImplementingTypesMap = new RelatedTypeCache();
private static readonly RelatedTypeCache s_typeToImmediatelyDerivedAndImplementingTypesMap = new RelatedTypeCache();
public static async Task<ImmutableArray<SymbolAndProjectId<INamedTypeSymbol>>> FindTypesFromCacheOrComputeAsync(
INamedTypeSymbol type,
Solution solution,
IImmutableSet<Project> projects,
RelatedTypeCache cache,
Func<CancellationToken, Task<ImmutableArray<SymbolAndProjectId<INamedTypeSymbol>>>> findAsync,
CancellationToken cancellationToken)
{
var dictionary = cache.GetOrCreateValue(solution);
var result = default(ImmutableArray<SymbolAndProjectId<INamedTypeSymbol>>);
// Do a quick lookup first to avoid the allocation. If it fails, go through the
// slower allocating path.
var key = (type.GetSymbolKey(), projects);
if (!dictionary.TryGetValue(key, out var lazy))
{
lazy = dictionary.GetOrAdd(key,
new AsyncLazy<ImmutableArray<(SymbolKey, ProjectId)>>(
async c =>
{
// If we're the code that is actually computing the symbols, then just
// take our result and store it in the outer frame. That way the caller
// doesn't need to incur the cost of deserializing the symbol keys that
// we're create right below this.
result = await findAsync(c).ConfigureAwait(false);
return result.SelectAsArray(t => (t.Symbol.GetSymbolKey(), t.ProjectId));
},
cacheResult: true));
}
// If we were the caller that actually computed the symbols, then we can just return
// the values we got.
if (!result.IsDefault)
{
return result;
}
// Otherwise, someone else computed the symbols and cached the results as symbol
// keys. Convert those symbol keys back to symbols and return.
var symbolKeys = await lazy.GetValueAsync(cancellationToken).ConfigureAwait(false);
var builder = ArrayBuilder<SymbolAndProjectId<INamedTypeSymbol>>.GetInstance();
// Group by projectId so that we only process one project/compilation at a time.
// Also, process in dependency order so taht previous compilations are ready if
// they're referenced by later compilations.
var dependencyOrder = solution.GetProjectDependencyGraph()
.GetTopologicallySortedProjects()
.Select((id, index) => (id, index))
.ToDictionary(t => t.id, t => t.index);
var orderedGroups = symbolKeys.GroupBy(t => t.Item2).OrderBy(g => dependencyOrder[g.Key]);
foreach (var group in orderedGroups)
{
var project = solution.GetProject(group.Key);
var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
foreach (var (symbolKey, _) in group)
{
var resolvedSymbol = symbolKey.Resolve(compilation, cancellationToken: cancellationToken).GetAnySymbol();
if (resolvedSymbol is INamedTypeSymbol namedType)
{
builder.Add(new SymbolAndProjectId<INamedTypeSymbol>(namedType, project.Id));
}
}
}
return builder.ToImmutableAndFree();
}
/// <summary> /// <summary>
/// Used for implementing the Inherited-By relation for progression. /// Used for implementing the Inherited-By relation for progression.
/// </summary> /// </summary>
internal static Task<ImmutableArray<SymbolAndProjectId<INamedTypeSymbol>>> FindImmediatelyDerivedClassesAsync( public static Task<ImmutableArray<SymbolAndProjectId<INamedTypeSymbol>>> FindImmediatelyDerivedClassesAsync(
INamedTypeSymbol type, INamedTypeSymbol type,
Solution solution, Solution solution,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
return FindDerivedClassesAsync( return FindTypesFromCacheOrComputeAsync(
SymbolAndProjectId.Create(type, projectId: null), solution, projects: null, type, solution, projects: null,
transitive: false, cancellationToken: cancellationToken); cache: s_typeToImmediatelyDerivedClassesMap,
findAsync: c => FindDerivedClassesAsync(
SymbolAndProjectId.Create(type, projectId: null), solution, projects: null,
transitive: false, cancellationToken: c),
cancellationToken: cancellationToken);
} }
/// <summary> /// <summary>
...@@ -61,9 +152,12 @@ internal static class DependentTypeFinder ...@@ -61,9 +152,12 @@ internal static class DependentTypeFinder
IImmutableSet<Project> projects, IImmutableSet<Project> projects,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
return FindDerivedClassesAsync( return FindTypesFromCacheOrComputeAsync(
SymbolAndProjectId.Create(type, projectId: null), solution, projects, type, solution, projects, s_typeToTransitivelyDerivedClassesMap,
transitive: true, cancellationToken: cancellationToken); c => FindDerivedClassesAsync(
SymbolAndProjectId.Create(type, projectId: null), solution, projects,
transitive: true, cancellationToken: c),
cancellationToken);
} }
private static Task<ImmutableArray<SymbolAndProjectId<INamedTypeSymbol>>> FindDerivedClassesAsync( private static Task<ImmutableArray<SymbolAndProjectId<INamedTypeSymbol>>> FindDerivedClassesAsync(
...@@ -96,7 +190,19 @@ internal static class DependentTypeFinder ...@@ -96,7 +190,19 @@ internal static class DependentTypeFinder
/// Implementation of <see cref="SymbolFinder.FindImplementationsAsync(SymbolAndProjectId, Solution, IImmutableSet{Project}, CancellationToken)"/> for /// Implementation of <see cref="SymbolFinder.FindImplementationsAsync(SymbolAndProjectId, Solution, IImmutableSet{Project}, CancellationToken)"/> for
/// <see cref="INamedTypeSymbol"/>s /// <see cref="INamedTypeSymbol"/>s
/// </summary> /// </summary>
public static async Task<ImmutableArray<SymbolAndProjectId<INamedTypeSymbol>>> FindTransitivelyImplementingTypesAsync( public static Task<ImmutableArray<SymbolAndProjectId<INamedTypeSymbol>>> FindTransitivelyImplementingTypesAsync(
INamedTypeSymbol type,
Solution solution,
IImmutableSet<Project> projects,
CancellationToken cancellationToken)
{
return FindTypesFromCacheOrComputeAsync(
type, solution, projects, s_typeToTransitivelyImplementingTypesMap,
c => FindTransitivelyImplementingTypesWorkerAsync(type, solution, projects, c),
cancellationToken);
}
private static async Task<ImmutableArray<SymbolAndProjectId<INamedTypeSymbol>>> FindTransitivelyImplementingTypesWorkerAsync(
INamedTypeSymbol type, INamedTypeSymbol type,
Solution solution, Solution solution,
IImmutableSet<Project> projects, IImmutableSet<Project> projects,
...@@ -114,14 +220,18 @@ internal static class DependentTypeFinder ...@@ -114,14 +220,18 @@ internal static class DependentTypeFinder
/// <summary> /// <summary>
/// Used for implementing the Inherited-By relation for progression. /// Used for implementing the Inherited-By relation for progression.
/// </summary> /// </summary>
internal static Task<ImmutableArray<SymbolAndProjectId<INamedTypeSymbol>>> FindImmediatelyDerivedAndImplementingTypesAsync( public static Task<ImmutableArray<SymbolAndProjectId<INamedTypeSymbol>>> FindImmediatelyDerivedAndImplementingTypesAsync(
INamedTypeSymbol type, INamedTypeSymbol type,
Solution solution, Solution solution,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
return FindDerivedAndImplementingTypesAsync( return FindTypesFromCacheOrComputeAsync(
SymbolAndProjectId.Create(type, projectId: null), solution, projects: null, type, solution, projects: null,
transitive: false, cancellationToken: cancellationToken); cache: s_typeToImmediatelyDerivedAndImplementingTypesMap,
findAsync: c => FindDerivedAndImplementingTypesAsync(
SymbolAndProjectId.Create(type, projectId: null), solution, projects: null,
transitive: false, cancellationToken: c),
cancellationToken: cancellationToken);
} }
private static Task<ImmutableArray<SymbolAndProjectId<INamedTypeSymbol>>> FindDerivedAndImplementingTypesAsync( private static Task<ImmutableArray<SymbolAndProjectId<INamedTypeSymbol>>> FindDerivedAndImplementingTypesAsync(
...@@ -554,7 +664,11 @@ internal static class DependentTypeFinder ...@@ -554,7 +664,11 @@ internal static class DependentTypeFinder
var typesToSearchFor = CreateSymbolAndProjectIdSet(); var typesToSearchFor = CreateSymbolAndProjectIdSet();
typesToSearchFor.AddAll(sourceAndMetadataTypes); typesToSearchFor.AddAll(sourceAndMetadataTypes);
var inheritanceQuery = new InheritanceQuery(sourceAndMetadataTypes); var comparer = project.LanguageServices.GetService<ISyntaxFactsService>().StringComparer;
var inheritanceQuery = new InheritanceQuery(sourceAndMetadataTypes, comparer);
var schedulerPair = new ConcurrentExclusiveSchedulerPair(
TaskScheduler.Default, maxConcurrencyLevel: Math.Max(1, Environment.ProcessorCount));
// As long as there are new types to search for, keep looping. // As long as there are new types to search for, keep looping.
while (typesToSearchFor.Count > 0) while (typesToSearchFor.Count > 0)
...@@ -563,10 +677,14 @@ internal static class DependentTypeFinder ...@@ -563,10 +677,14 @@ internal static class DependentTypeFinder
inheritanceQuery.TypeNames.AddRange(typesToSearchFor.Select(c => c.Symbol.Name)); inheritanceQuery.TypeNames.AddRange(typesToSearchFor.Select(c => c.Symbol.Name));
// Search all the documents of this project in parallel. // Search all the documents of this project in parallel.
var tasks = project.Documents.Select(d => FindImmediatelyInheritingTypesInDocumentAsync( var tasks = project.Documents.Select(
d, typesToSearchFor, inheritanceQuery, d => Task.Factory.StartNew(
cachedModels, cachedInfos, () => FindImmediatelyInheritingTypesInDocumentAsync(
sourceTypeImmediatelyMatches, cancellationToken)).ToArray(); d, typesToSearchFor, inheritanceQuery, cachedModels,
cachedInfos, sourceTypeImmediatelyMatches, cancellationToken),
cancellationToken,
TaskCreationOptions.None,
schedulerPair.ConcurrentScheduler).Unwrap()).ToArray();
await Task.WhenAll(tasks).ConfigureAwait(false); await Task.WhenAll(tasks).ConfigureAwait(false);
...@@ -618,8 +736,8 @@ internal static class DependentTypeFinder ...@@ -618,8 +736,8 @@ internal static class DependentTypeFinder
Document document, Document document,
SymbolAndProjectIdSet typesToSearchFor, SymbolAndProjectIdSet typesToSearchFor,
InheritanceQuery inheritanceQuery, InheritanceQuery inheritanceQuery,
ConcurrentSet<SemanticModel> cachedModels, ConcurrentSet<SemanticModel> cachedModels,
ConcurrentSet<SyntaxTreeIndex> cachedInfos, ConcurrentSet<SyntaxTreeIndex> cachedInfos,
Func<SymbolAndProjectIdSet, INamedTypeSymbol, bool> typeImmediatelyMatches, Func<SymbolAndProjectIdSet, INamedTypeSymbol, bool> typeImmediatelyMatches,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
...@@ -721,13 +839,13 @@ private class InheritanceQuery ...@@ -721,13 +839,13 @@ private class InheritanceQuery
public readonly HashSet<string> TypeNames; public readonly HashSet<string> TypeNames;
public InheritanceQuery(SymbolAndProjectIdSet sourceAndMetadataTypes) public InheritanceQuery(SymbolAndProjectIdSet sourceAndMetadataTypes, StringComparer comparer)
{ {
DerivesFromSystemObject = sourceAndMetadataTypes.Any(t => t.Symbol.SpecialType == SpecialType.System_Object); DerivesFromSystemObject = sourceAndMetadataTypes.Any(t => t.Symbol.SpecialType == SpecialType.System_Object);
DerivesFromSystemValueType = sourceAndMetadataTypes.Any(t => t.Symbol.SpecialType == SpecialType.System_ValueType); DerivesFromSystemValueType = sourceAndMetadataTypes.Any(t => t.Symbol.SpecialType == SpecialType.System_ValueType);
DerivesFromSystemEnum = sourceAndMetadataTypes.Any(t => t.Symbol.SpecialType == SpecialType.System_Enum); DerivesFromSystemEnum = sourceAndMetadataTypes.Any(t => t.Symbol.SpecialType == SpecialType.System_Enum);
DerivesFromSystemMulticastDelegate = sourceAndMetadataTypes.Any(t => t.Symbol.SpecialType == SpecialType.System_MulticastDelegate); DerivesFromSystemMulticastDelegate = sourceAndMetadataTypes.Any(t => t.Symbol.SpecialType == SpecialType.System_MulticastDelegate);
TypeNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase); TypeNames = new HashSet<string>(comparer);
} }
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册