diff options
author | Kirill Osenkov <github@osenkov.com> | 2017-12-13 08:56:41 +0300 |
---|---|---|
committer | Kirill Osenkov <github@osenkov.com> | 2017-12-13 08:56:41 +0300 |
commit | 662e5076a5ca1820e1c671d62665743eae594702 (patch) | |
tree | 2769058e8e054176e8fb0abba979b92049284bf7 /src | |
parent | d622288d6145af5bd99847a8b4a2a70dd52c1554 (diff) |
Updating to Editor 15.6.241-preview.
Diffstat (limited to 'src')
26 files changed, 665 insertions, 48 deletions
diff --git a/src/Core/Def/BaseUtility/TypeConversionAttribute.cs b/src/Core/Def/BaseUtility/TypeConversionAttribute.cs new file mode 100644 index 0000000..6d1923f --- /dev/null +++ b/src/Core/Def/BaseUtility/TypeConversionAttribute.cs @@ -0,0 +1,35 @@ +namespace Microsoft.VisualStudio.Utilities +{ + using System; + using System.ComponentModel.Composition; + + /// <summary> + /// Marks a class exported with a MEF <see cref="ExportAttribute"/> as a conversion from one type to another. + /// </summary> + public sealed class TypeConversionAttribute : SingletonBaseMetadataAttribute + { + private readonly Type from; + private readonly Type to; + + /// <summary> + /// Creates a new instance of <see cref="TypeConversionAttribute"/>. + /// </summary> + /// <param name="fromFullName">The <see cref="Type"/> being converted from.</param> + /// <param name="toFullName">The <see cref="Type"/> being converted to.</param> + public TypeConversionAttribute(Type from, Type to) + { + this.from = from ?? throw new ArgumentNullException(nameof(from)); + this.to = to ?? throw new ArgumentNullException(nameof(to)); + } + + /// <summary> + /// The name of the being converted from. + /// </summary> + public string FromFullName => this.from.AssemblyQualifiedName; + + /// <summary> + /// The name of the exact type being converted to. + /// </summary> + public string ToFullName => this.to.AssemblyQualifiedName; + } +} diff --git a/src/Core/Def/ImageId.cs b/src/Core/Def/ImageId.cs new file mode 100644 index 0000000..d540c16 --- /dev/null +++ b/src/Core/Def/ImageId.cs @@ -0,0 +1,35 @@ +namespace Microsoft.VisualStudio.Core.Imaging +{ + using System; + + /// <summary> + /// Unique identifier for Visual Studio image asset. + /// </summary> + /// <remarks> + /// On Windows systems, <see cref="ImageId"/> can be converted to and from + /// various other image representations via the ImageIdExtensions extension methods. + /// </remarks> + public struct ImageId + { + /// <summary> + /// The <see cref="Guid"/> identifying the group to which this image belongs. + /// </summary> + public readonly Guid Guid; + + /// <summary> + /// The <see cref="int"/> identifying the particular image from the group that this id maps to. + /// </summary> + public readonly int Id; + + /// <summary> + /// Creates a new instance of ImageId. + /// </summary> + /// <param name="guid">The <see cref="Guid"/> identifying the group to which this image belongs.</param> + /// <param name="id">The <see cref="int"/> identifying the particular image from the group that this id maps to.</param> + public ImageId(Guid guid, int id) + { + this.Guid = guid; + this.Id = id; + } + } +} diff --git a/src/Microsoft.VisualStudio.Text.Implementation.csproj b/src/Microsoft.VisualStudio.Text.Implementation.csproj index 36b258b..728f071 100644 --- a/src/Microsoft.VisualStudio.Text.Implementation.csproj +++ b/src/Microsoft.VisualStudio.Text.Implementation.csproj @@ -5,16 +5,16 @@ <SignAssembly>true</SignAssembly> <AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile> <DelaySign>false</DelaySign> - <Version>15.0.6-pre</Version> + <Version>15.0.7-pre</Version> <AssemblyVersion>15.0.0.0</AssemblyVersion> - <NuGetVersionEditor>15.6.162-preview</NuGetVersionEditor> + <NuGetVersionEditor>15.6.241-preview</NuGetVersionEditor> </PropertyGroup> <PropertyGroup> <GeneratePackageOnBuild>True</GeneratePackageOnBuild> <PackageId>Microsoft.VisualStudio.Text.Implementation</PackageId> - <Authors>vsideeng@microsoft.com</Authors> - <Owners>vsideeng@microsoft.com</Owners> + <Authors>Microsoft</Authors> + <Owners>Microsoft</Owners> <Company>Microsoft</Company> <Copyright>© Microsoft Corporation. All rights reserved.</Copyright> <LicenseUrl>https://aka.ms/pexunj</LicenseUrl> diff --git a/src/Microsoft.VisualStudio.Text.Implementation.nuspec b/src/Microsoft.VisualStudio.Text.Implementation.nuspec new file mode 100644 index 0000000..6299099 --- /dev/null +++ b/src/Microsoft.VisualStudio.Text.Implementation.nuspec @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd"> + <metadata> + <id>Microsoft.VisualStudio.Text.Implementation</id> + <version>15.0.7-pre</version> + <authors>Microsoft</authors> + <owners>Microsoft</owners> + <requireLicenseAcceptance>false</requireLicenseAcceptance> + <licenseUrl>https://github.com/Microsoft/vs-editor-api/blob/367d01a0b186f034178c5d5338c436e203eff8b4/LICENSE</licenseUrl> + <projectUrl>https://github.com/Microsoft/vs-editor-api</projectUrl> + <description>Microsoft® Visual Studio® Editor Platform</description> + <copyright>© Microsoft Corporation. All rights reserved.</copyright> + <repository url="https://github.com/Microsoft/vs-editor-api" /> + <dependencies> + <group targetFramework=".NETFramework4.6"> + <dependency id="Microsoft.VisualStudio.Text.Data" version="15.6.241-preview" exclude="Build,Analyzers" /> + <dependency id="Microsoft.VisualStudio.Text.Logic" version="15.6.241-preview" exclude="Build,Analyzers" /> + <dependency id="Microsoft.VisualStudio.Language.StandardClassification" version="15.6.241-preview" exclude="Build,Analyzers" /> + <dependency id="Microsoft.VisualStudio.CoreUtility" version="15.6.241-preview" exclude="Build,Analyzers" /> + <dependency id="Microsoft.VisualStudio.Text.UI" version="15.6.241-preview" exclude="Build,Analyzers" /> + <dependency id="System.Collections.Immutable" version="1.3.1" exclude="Build,Analyzers" /> + </group> + </dependencies> + </metadata> + <files> + <file src="bin\Release\Microsoft.VisualStudio.Text.Implementation\Microsoft.VisualStudio.Text.Implementation.dll" target="lib\net46" /> + <file src="bin\Release\Microsoft.VisualStudio.Text.Implementation\Microsoft.VisualStudio.Text.Implementation.pdb" target="lib\net46" /> + </files> +</package>
\ No newline at end of file diff --git a/src/Text/Def/TextLogic/TextLogic.csproj b/src/Text/Def/TextLogic/TextLogic.csproj index 638e3f5..522d638 100644 --- a/src/Text/Def/TextLogic/TextLogic.csproj +++ b/src/Text/Def/TextLogic/TextLogic.csproj @@ -15,6 +15,9 @@ <Reference Include="System.Core" /> </ItemGroup> <ItemGroup> + <PackageReference Include="System.Collections.Immutable" Version="1.3.1" /> + </ItemGroup> + <ItemGroup> <ProjectReference Include="..\TextData\TextData.csproj" /> <ProjectReference Include="..\..\..\Core\Def\CoreUtility.csproj" /> </ItemGroup> diff --git a/src/Text/Def/TextUI/Adornments/IToolTipProvider.cs b/src/Text/Def/TextUI/Adornments/IToolTipProvider.cs index c215ef7..f141e61 100644 --- a/src/Text/Def/TextUI/Adornments/IToolTipProvider.cs +++ b/src/Text/Def/TextUI/Adornments/IToolTipProvider.cs @@ -4,11 +4,13 @@ // namespace Microsoft.VisualStudio.Text.Adornments { + using System; using Microsoft.VisualStudio.Text; /// <summary> /// Creates and displays tooltips, using an arbitrary object as content. /// </summary> + [Obsolete("Use " + nameof(IToolTipService) + " instead")] public interface IToolTipProvider { /// <summary> @@ -42,4 +44,4 @@ namespace Microsoft.VisualStudio.Text.Adornments /// </summary> void ClearToolTip(); } -}
\ No newline at end of file +} diff --git a/src/Text/Def/TextUI/Adornments/IToolTipProviderFactory.cs b/src/Text/Def/TextUI/Adornments/IToolTipProviderFactory.cs index d257230..f9a8bbc 100644 --- a/src/Text/Def/TextUI/Adornments/IToolTipProviderFactory.cs +++ b/src/Text/Def/TextUI/Adornments/IToolTipProviderFactory.cs @@ -4,6 +4,7 @@ // namespace Microsoft.VisualStudio.Text.Adornments { + using System; using Microsoft.VisualStudio.Text.Editor; /// <summary> @@ -13,6 +14,7 @@ namespace Microsoft.VisualStudio.Text.Adornments /// <remarks>This is a MEF component part, and should be exported with the following attribute: /// [Export(typeof(IToolTipProviderFactory))] /// </remarks> + [Obsolete("Use " + nameof(IToolTipService) + " instead")] public interface IToolTipProviderFactory { /// <summary> diff --git a/src/Text/Def/TextUI/Adornments/ToolTipService/IToolTipPresenter.cs b/src/Text/Def/TextUI/Adornments/ToolTipService/IToolTipPresenter.cs new file mode 100644 index 0000000..57f5fc7 --- /dev/null +++ b/src/Text/Def/TextUI/Adornments/ToolTipService/IToolTipPresenter.cs @@ -0,0 +1,45 @@ +namespace Microsoft.VisualStudio.Text.Adornments +{ + using System; + using System.Collections.Generic; + using Microsoft.VisualStudio.Text; + + /// <summary> + /// A platform-specific ToolTip implementation. + /// </summary> + /// <remarks> + /// This type is proffered to the IDE via an <see cref="IToolTipPresenterFactory"/> and is + /// always constructed and called purely on the UI thread. Each <see cref="IToolTipPresenter"/> + /// is a single-use object that is responsible for converting the given content to + /// into platform-specific UI elements and displaying them in a popup UI. + /// </remarks> + public interface IToolTipPresenter + { + /// <summary> + /// Invoked upon dismissal of the ToolTip's popup view. + /// </summary> + /// <remarks> + /// This event should be fired regardless of the reason for the popup's dismissal. + /// </remarks> + event EventHandler Dismissed; + + /// <summary> + /// Constructs a popup containing a platform-specific UI representation of <paramref name="content"/>. + /// </summary> + /// <remarks> + /// This method can be called multiple times to refresh the content and applicableToSpan. + /// </remarks> + /// <param name="applicableToSpan">The span of text for which the tooltip is kept open.</param> + /// <param name="content"> + /// A platform independent representation of the tooltip content. <see cref="IToolTipPresenter"/>s + /// should use the <see cref="IViewElementFactoryService"/> to convert <paramref name="content"/> + /// to platform specific UI elements. + /// </param> + void StartOrUpdate(ITrackingSpan applicableToSpan, IEnumerable<object> content); + + /// <summary> + /// Dismisses the popup and causes <see cref="Dismissed"/> to be fired. + /// </summary> + void Dismiss(); + } +} diff --git a/src/Text/Def/TextUI/Adornments/ToolTipService/IToolTipPresenterFactory.cs b/src/Text/Def/TextUI/Adornments/ToolTipService/IToolTipPresenterFactory.cs new file mode 100644 index 0000000..cbeb4bf --- /dev/null +++ b/src/Text/Def/TextUI/Adornments/ToolTipService/IToolTipPresenterFactory.cs @@ -0,0 +1,33 @@ +namespace Microsoft.VisualStudio.Text.Adornments +{ + using Microsoft.VisualStudio.Text.Editor; + + /// <summary> + /// Proffers a platform-specific <see cref="IToolTipPresenter"/> to the IDE. + /// </summary> + /// <remarks> + /// This class will always be constructed and called purely from the UI thread. + /// Extenders can construct their own presenter and supersede the default + /// one via MEF ordering. Presenter providers should return a new ToolTip each + /// time they are called and should support multiple simultaneous open tips. + /// </remarks> + /// <example> + /// [Export(typeof(IToolTipPresenterFactory))] + /// [Name(nameof("super cool tooltip factory"))] + /// [Order(Before = "default")] + /// </example> + public interface IToolTipPresenterFactory + { + /// <summary> + /// Constructs a new instance of <see cref="IToolTipPresenter"/> for the current platform. + /// </summary> + /// <param name="textView"> + /// The view that owns the tooltip. + /// </param> + /// <param name="parameters"> + /// Parameters to create the tooltip with. Never null. + /// </param> + /// <returns>A <see cref="IToolTipPresenter"/> for the current platform.</returns> + IToolTipPresenter Create(ITextView textView, ToolTipParameters parameters); + } +} diff --git a/src/Text/Def/TextUI/Adornments/ToolTipService/IToolTipService.cs b/src/Text/Def/TextUI/Adornments/ToolTipService/IToolTipService.cs new file mode 100644 index 0000000..983ba1a --- /dev/null +++ b/src/Text/Def/TextUI/Adornments/ToolTipService/IToolTipService.cs @@ -0,0 +1,29 @@ +namespace Microsoft.VisualStudio.Text.Adornments +{ + using Microsoft.VisualStudio.Text.Editor; + + /// <summary> + /// Cross platform service for the creation and management of ToolTips. + /// </summary> + /// <remarks> + /// This class is a MEF component part and it can be imported via the code in the example. + /// </remarks> + /// <example> + /// [Import] + /// internal IToolTipService tooltipService; + /// </example> + public interface IToolTipService + { + /// <summary> + /// Creates a new non-visible ToolTip presenter. + /// </summary> + /// <param name="textView"> + /// The view that owns the tooltip. + /// </param> + /// <param name="parameters"> + /// Parameters to create the tooltip with. Default is mouse tracking. + /// </param> + /// <returns>A new non-visible <see cref="IToolTipPresenter"/>.</returns> + IToolTipPresenter CreatePresenter(ITextView textView, ToolTipParameters parameters = null); + } +} diff --git a/src/Text/Def/TextUI/Adornments/ToolTipService/IViewElementFactory.cs b/src/Text/Def/TextUI/Adornments/ToolTipService/IViewElementFactory.cs new file mode 100644 index 0000000..eaf2893 --- /dev/null +++ b/src/Text/Def/TextUI/Adornments/ToolTipService/IViewElementFactory.cs @@ -0,0 +1,38 @@ +namespace Microsoft.VisualStudio.Text.Adornments +{ + using System; + using Microsoft.VisualStudio.Text.Editor; + using Microsoft.VisualStudio.Utilities; + + /// <summary> + /// Converts from an object to its equivalent platform specific UI element. + /// </summary> + /// <remarks> + /// <para> + /// This type allows the same intermediate type to be rendered on different platforms through + /// the use of platform specific exports that live in that platform's UI layer. + /// </para> + /// <para> + /// You can supersede an existing <see cref="IViewElementFactory"/> for a (to, from) type + /// pair via MEF <see cref="OrderAttribute"/>s. + /// </para> + /// </remarks> + /// <example> + /// [Export(typeof(IViewElementFactory))] + /// [Name("object item")] + /// [Conversion(from: typeof(object), to: typeof(UIElement))] + /// [Order(After = "Foo", Before = "Bar")] + /// </example> + public interface IViewElementFactory + { + /// <summary> + /// Converts <paramref name="model"/> into an equivalent object of type <typeparamref name="TView"/>. + /// </summary> + /// <exception cref="ArgumentException">Thrown if the conversion is unknown or unsupported.</exception> + /// <typeparam name="TView">The base type of the view element on the specific platform.</typeparam> + /// <param name="textView">The view that owns the control that will host this view element.</param> + /// <param name="model">The object to convert to a view element.</param> + /// <returns>A new object of type <typeparamref name="TView"/>.</returns> + TView CreateViewElement<TView>(ITextView textView, object model) where TView : class; + } +} diff --git a/src/Text/Def/TextUI/Adornments/ToolTipService/IViewElementFactoryService.cs b/src/Text/Def/TextUI/Adornments/ToolTipService/IViewElementFactoryService.cs new file mode 100644 index 0000000..c5d324a --- /dev/null +++ b/src/Text/Def/TextUI/Adornments/ToolTipService/IViewElementFactoryService.cs @@ -0,0 +1,37 @@ +namespace Microsoft.VisualStudio.Text.Adornments +{ + using System.ComponentModel.Composition; + using Microsoft.VisualStudio.Text.Editor; + + /// <summary> + /// A service for converting from data objects to their platform specific UI representation. + /// </summary> + /// <remarks> + /// <para> + /// This is a MEF service that can be obtained via the <see cref="ImportAttribute"/> in a MEF exported class. + /// </para> + /// <para> + /// The editor supports <see cref="ClassifiedTextElement"/>s, <see cref="ImageElement"/>s, and <see cref="object"/> + /// on all platforms. Text and image elements are converted to colorized text and images respectively and + /// other objects are displayed as the <see cref="string"/> returned by <see cref="object.ToString()"/> + /// unless an extender exports a <see cref="IViewElementFactory"/> for that type. + /// </para> + /// On Windows only, <see cref="ITextBuffer"/>, <see cref="ITextView"/>, and UIElement are also directly + /// supported. + /// </remarks> + /// <example> + /// [Import] + /// internal IViewElementFactoryService viewElementFactoryService; + /// </example> + public interface IViewElementFactoryService + { + /// <summary> + /// Converts <paramref name="model"/> into an equivalent object of type <typeparamref name="TView"/>. + /// </summary> + /// <typeparam name="TView">The base type of the view element on the specific platform.</typeparam> + /// <param name="textView">The textView that owns the control that will host this view element.</param> + /// <param name="model">The object to convert to a view element.</param> + /// <returns>A new object of type <typeparamref name="TView"/> or null if the conversion is unknown.</returns> + TView CreateViewElement<TView>(ITextView textView, object model) where TView : class; + } +} diff --git a/src/Text/Def/TextUI/Adornments/ToolTipService/ToolTipParameters.cs b/src/Text/Def/TextUI/Adornments/ToolTipService/ToolTipParameters.cs new file mode 100644 index 0000000..2f7a31e --- /dev/null +++ b/src/Text/Def/TextUI/Adornments/ToolTipService/ToolTipParameters.cs @@ -0,0 +1,63 @@ +namespace Microsoft.VisualStudio.Text.Adornments +{ + using System; + + /// <summary> + /// Determines behavior for a <see cref="IToolTipPresenter"/>. + /// </summary> + public sealed class ToolTipParameters + { + private readonly Func<bool> keepOpenFunc; + + /// <summary> + /// Default options for a mouse tracking tooltip. + /// </summary> + public static readonly ToolTipParameters Default = new ToolTipParameters(); + + /// <summary> + /// Creates a new instance of <see cref="ToolTipParameters"/>. + /// </summary> + /// <param name="trackMouse"> + /// If true, dismisses the tooltip when the mouse leaves the applicable span. + /// </param> + /// <param name="ignoreBufferChange"> + /// If true, and if the tooltip is mouse tracking, does not dismiss when the buffer changes. + /// </param> + /// <param name="keepOpenFunc"> + /// A callback function that determines wehther or not to keep open the tooltip + /// in mouse tracking sessions, despite the mouse being outside the tooltip. + /// </param> + public ToolTipParameters( + bool trackMouse = true, + bool ignoreBufferChange = false, + Func<bool> keepOpenFunc = null) + { + this.TrackMouse = trackMouse; + this.IgnoreBufferChange = ignoreBufferChange; + + if (!trackMouse && ignoreBufferChange) + { + throw new ArgumentException($"{nameof(ignoreBufferChange)} can only be true if {nameof(trackMouse)} is false"); + } + + this.keepOpenFunc = keepOpenFunc; + } + + /// <summary> + /// Gets whether or not the tooltip can be dismissed by the mouse leaving the + /// applicable span. + /// </summary> + public bool TrackMouse { get; } + + /// <summary> + /// Gets whether or not the tooltip is closed when the buffer changes. + /// </summary> + public bool IgnoreBufferChange { get; } + + /// <summary> + /// Gets whether or not the tooltip should stay open even if the + /// mouse is outside of the tip. + /// </summary> + public bool KeepOpen => this.keepOpenFunc?.Invoke() ?? false; + } +} diff --git a/src/Text/Def/TextUI/Adornments/ToolTipService/ViewElementFactories/ClassifiedTextElement.cs b/src/Text/Def/TextUI/Adornments/ToolTipService/ViewElementFactories/ClassifiedTextElement.cs new file mode 100644 index 0000000..f003e81 --- /dev/null +++ b/src/Text/Def/TextUI/Adornments/ToolTipService/ViewElementFactories/ClassifiedTextElement.cs @@ -0,0 +1,41 @@ +namespace Microsoft.VisualStudio.Text.Adornments +{ + using System; + using System.Collections.Generic; + using System.Collections.Immutable; + + /// <summary> + /// Represents a block of classified text in an <see cref="IToolTipService"/> <see cref="IToolTipPresenter"/>. + /// </summary> + /// <remarks> + /// Classified text is a span of text with a corresponding classification type name. On + /// <see cref="IToolTipPresenter.StartOrUpdate(ITrackingSpan, System.Collections.Generic.IEnumerable{object})"/>, + /// the classified text is converted to a platform-specific block of runs of formatted (colorized) text via + /// the <see cref="IViewElementFactoryService"/> and is displayed. + /// </remarks> + public sealed class ClassifiedTextElement + { + /// <summary> + /// Creates a new instance of classified text. + /// </summary> + /// <param name="runs">A sequence of zero or more runs of classified text.</param> + public ClassifiedTextElement(params ClassifiedTextRun[] runs) + { + this.Runs = runs?.ToImmutableList() ?? throw new ArgumentNullException(nameof(runs)); + } + + /// <summary> + /// Creates a new instance of classified text. + /// </summary> + /// <param name="runs">A sequence of zero or more runs of classified text.</param> + public ClassifiedTextElement(IEnumerable<ClassifiedTextRun> runs) + { + this.Runs = runs?.ToImmutableList() ?? throw new ArgumentNullException(nameof(runs)); + } + + /// <summary> + /// A sequence of classified runs of text. + /// </summary> + public IEnumerable<ClassifiedTextRun> Runs { get; } + } +} diff --git a/src/Text/Def/TextUI/Adornments/ToolTipService/ViewElementFactories/ClassifiedTextRun.cs b/src/Text/Def/TextUI/Adornments/ToolTipService/ViewElementFactories/ClassifiedTextRun.cs new file mode 100644 index 0000000..11f10db --- /dev/null +++ b/src/Text/Def/TextUI/Adornments/ToolTipService/ViewElementFactories/ClassifiedTextRun.cs @@ -0,0 +1,45 @@ +namespace Microsoft.VisualStudio.Text.Adornments +{ + using System; + using Microsoft.VisualStudio.Text.Classification; + + /// <summary> + /// Represents a contiguous run of classified text in an <see cref="IToolTipService"/> <see cref="IToolTipPresenter"/>. + /// </summary> + /// <remarks> + /// Classified text runs live in <see cref="ClassifiedTextElement"/>s and are a string, classification pair. On + /// <see cref="IToolTipPresenter.StartOrUpdate(ITrackingSpan, System.Collections.Generic.IEnumerable{object})"/>, + /// the classified text is converted to a platform-specific run of formatted (colorized) text via + /// the <see cref="IViewElementFactoryService"/> and is displayed. + /// </remarks> + public sealed class ClassifiedTextRun + { + /// <summary> + /// Creates a new run of classified text. + /// </summary> + /// <param name="classificationTypeName"> + /// A name indicating a <see cref="IClassificationType"/> that maps to a format that will be applied to the text. + /// </param> + /// <param name="text">The text rendered by this run.</param> + /// <remarks> + /// Classification types can be platform specific. Only classifications defined in PredefinedClassificationTypeNames + /// are supported cross platform. + /// </remarks> + public ClassifiedTextRun(string classificationTypeName, string text) + { + this.ClassificationTypeName = classificationTypeName + ?? throw new ArgumentNullException(nameof(classificationTypeName)); + this.Text = text ?? throw new ArgumentNullException(nameof(text)); + } + + /// <summary> + /// The name of the classification which maps to formatting properties that will be applied to this text. + /// </summary> + public string ClassificationTypeName { get; } + + /// <summary> + /// The text that will be formatted by <see cref="ClassificationTypeName"/>'s corresponding formatting. + /// </summary> + public string Text { get; } + } +} diff --git a/src/Text/Def/TextUI/Adornments/ToolTipService/ViewElementFactories/ContainerElement.cs b/src/Text/Def/TextUI/Adornments/ToolTipService/ViewElementFactories/ContainerElement.cs new file mode 100644 index 0000000..22d0b42 --- /dev/null +++ b/src/Text/Def/TextUI/Adornments/ToolTipService/ViewElementFactories/ContainerElement.cs @@ -0,0 +1,49 @@ +namespace Microsoft.VisualStudio.Text.Adornments +{ + using System; + using System.Collections.Generic; + using System.Collections.Immutable; + + /// <summary> + /// Represents a container of zero or more elements for display in an <see cref="IToolTipPresenter"/>. + /// </summary> + /// <remarks> + /// Elements are translated to platform-specific UI constructs via the <see cref="IViewElementFactoryService"/>. + /// </remarks> + public sealed class ContainerElement + { + /// <summary> + /// Constructs a new container. + /// </summary> + /// <exception cref="ArgumentNullException">Thrown if <paramref name="elements"/> is <c>null</c>.</exception> + /// <param name="style">The layout style for the container.</param> + /// <param name="elements">The <see cref="IViewElementFactoryService"/> elements to display.</param> + public ContainerElement(ContainerElementStyle style, IEnumerable<object> elements) + { + this.Style = style; + this.Elements = elements?.ToImmutableList() ?? throw new ArgumentNullException(nameof(elements)); + } + + /// <summary> + /// Constructs a new container. + /// </summary> + /// <exception cref="ArgumentNullException">Thrown if <paramref name="elements"/> is <c>null</c>.</exception> + /// <param name="style">The layout style for the container.</param> + /// <param name="elements">The elements to translate to UI and display via the <see cref="IViewElementFactoryService"/>.</param> + public ContainerElement(ContainerElementStyle style, params object[] elements) + { + this.Style = style; + this.Elements = elements?.ToImmutableList() ?? throw new ArgumentNullException(nameof(elements)); + } + + /// <summary> + /// The elements to be displayed in the container. + /// </summary> + public IEnumerable<object> Elements { get; } + + /// <summary> + /// The layout style for the container. + /// </summary> + public ContainerElementStyle Style { get; } + } +} diff --git a/src/Text/Def/TextUI/Adornments/ToolTipService/ViewElementFactories/ContainerElementStyle.cs b/src/Text/Def/TextUI/Adornments/ToolTipService/ViewElementFactories/ContainerElementStyle.cs new file mode 100644 index 0000000..d9f8b22 --- /dev/null +++ b/src/Text/Def/TextUI/Adornments/ToolTipService/ViewElementFactories/ContainerElementStyle.cs @@ -0,0 +1,18 @@ +namespace Microsoft.VisualStudio.Text.Adornments +{ + /// <summary> + /// The layout style for a <see cref="ContainerElement"/>. + /// </summary> + public enum ContainerElementStyle + { + /// <summary> + /// Contents are end-to-end, and wrapped when the control becomes too wide. + /// </summary> + Wrapped, + + /// <summary> + /// Contents are stacked vertically. + /// </summary> + Stacked + } +} diff --git a/src/Text/Def/TextUI/Adornments/ToolTipService/ViewElementFactories/ImageElement.cs b/src/Text/Def/TextUI/Adornments/ToolTipService/ViewElementFactories/ImageElement.cs new file mode 100644 index 0000000..f4c477c --- /dev/null +++ b/src/Text/Def/TextUI/Adornments/ToolTipService/ViewElementFactories/ImageElement.cs @@ -0,0 +1,29 @@ +namespace Microsoft.VisualStudio.Text.Adornments +{ + using Microsoft.VisualStudio.Core.Imaging; + + /// <summary> + /// Represents an image in an <see cref="IToolTipService"/> <see cref="IToolTipPresenter"/>. + /// </summary> + /// + /// <remarks> + /// <see cref="ImageElement"/>s should be constructed with <see cref="Microsoft.VisualStudio.Core.Imaging.ImageId"/>s + /// that correspond to an image on that platform. + /// </remarks> + public sealed class ImageElement + { + /// <summary> + /// Creates a new instance of an image element. + /// </summary> + /// <param name="iamgeId"> A unique identifier for an image.</param> + public ImageElement(ImageId imageId) + { + this.ImageId = imageId; + } + + /// <summary> + /// A unique identifier for an image. + /// </summary> + public ImageId ImageId { get; } + } +} diff --git a/src/Text/Def/TextUI/Operations/IEditorOperations.cs b/src/Text/Def/TextUI/Operations/IEditorOperations.cs index 9668e55..55c7c67 100644 --- a/src/Text/Def/TextUI/Operations/IEditorOperations.cs +++ b/src/Text/Def/TextUI/Operations/IEditorOperations.cs @@ -891,7 +891,6 @@ namespace Microsoft.VisualStudio.Text.Operations /// Returns an empty string if the provided <paramref name="point"/> is not in virtual space. /// </remarks> string GetWhitespaceForVirtualSpace(VirtualSnapshotPoint point); - #endregion #region Properties diff --git a/src/Text/Def/TextUI/Operations/IEditorOperations3.cs b/src/Text/Def/TextUI/Operations/IEditorOperations3.cs index e176fde..eceb30d 100644 --- a/src/Text/Def/TextUI/Operations/IEditorOperations3.cs +++ b/src/Text/Def/TextUI/Operations/IEditorOperations3.cs @@ -35,5 +35,16 @@ namespace Microsoft.VisualStudio.Text.Operations /// </para> /// </remarks> bool TrimTrailingWhiteSpace(); + + /// <summary> + /// Duplicates the current selection, or the whole line (if there is no selection), without changing the clipboard. + /// </summary> + /// <returns> + /// <c>true</c> if the edit succeeded, otherwise <c>false</c>. + /// </returns> + /// <remarks> + /// Multiple selection cases like block selection will treat each selection independently. + /// </remarks> + bool DuplicateSelection(); } } diff --git a/src/Text/Def/TextUI/TextUI.csproj b/src/Text/Def/TextUI/TextUI.csproj index ef71217..18cf4e5 100644 --- a/src/Text/Def/TextUI/TextUI.csproj +++ b/src/Text/Def/TextUI/TextUI.csproj @@ -22,19 +22,4 @@ <None Include="Diagrams\BasePrimitives.cd" /> <None Include="Diagrams\Editor.cd" /> </ItemGroup> - <ItemGroup> - <EmbeddedResource Update="Strings.resx"> - <Generator>ResXFileCodeGenerator</Generator> - <LastGenOutput>Strings.Designer.cs</LastGenOutput> - <LogicalName>Microsoft.VisualStudio.Text.Editor.Strings.resources</LogicalName> - <ManifestResourceName>Microsoft.VisualStudio.Text.Editor.Strings.resources</ManifestResourceName> - </EmbeddedResource> - </ItemGroup> - <ItemGroup> - <Compile Update="Strings.Designer.cs"> - <DesignTime>True</DesignTime> - <AutoGen>True</AutoGen> - <DependentUpon>Strings.resx</DependentUpon> - </Compile> - </ItemGroup> </Project> diff --git a/src/Text/Impl/EditorOperations/EditorOperations.cs b/src/Text/Impl/EditorOperations/EditorOperations.cs index 50582ff..5ac282a 100644 --- a/src/Text/Impl/EditorOperations/EditorOperations.cs +++ b/src/Text/Impl/EditorOperations/EditorOperations.cs @@ -4583,6 +4583,83 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation return !edit.Canceled; } } + + private void DuplicateLine(SnapshotPoint triggerPoint, ITextEdit edit, bool insertBelow = false) + { + var line = triggerPoint.GetContainingLine(); + string textToInsert = line.GetText(); + int whereToInsert; + + if (insertBelow) + { + whereToInsert = line.End; + edit.Insert(whereToInsert, TextBufferOperationHelpers.GetNewLineCharacterToInsert(line, _editorOptions)); + edit.Insert(whereToInsert, textToInsert); + } + else + { + whereToInsert = line.Start; + edit.Insert(whereToInsert, textToInsert); + edit.Insert(whereToInsert, TextBufferOperationHelpers.GetNewLineCharacterToInsert(line, _editorOptions)); + } + } + + public bool DuplicateSelection() + { + Func<bool> func = () => + { + using (ITextEdit edit = _textView.TextBuffer.CreateEdit()) + { + if (_textView.Selection.IsEmpty) + { + DuplicateLine(_textView.Caret.Position.BufferPosition, edit); + } + else + { + var virtualSelectedSpans = _textView.Selection.VirtualSelectedSpans; + + // This is used only in the case of zero width block selection in order to maintain the block selection after + // our edit. On the first span we want to insert the copied text after the current row so that the top of the block + // selection doesn't move. An all others, we want to insert above so the bottom of the block selection does move. + bool insertBelow = virtualSelectedSpans.Count > 1; + foreach (var virtualSpan in virtualSelectedSpans) + { + if (virtualSpan.Length > 0) + { + if (!virtualSpan.IsInVirtualSpace) + { + edit.Insert(virtualSpan.Start.Position, virtualSpan.GetText()); + } + else + { + // Is this all in virtual space, if so, do nothing + if (!virtualSpan.Start.IsInVirtualSpace) + { + // Ok, we need to pad with whitespace as well as duplicate. Append the amount of virtual space as spaces after the inserted text + // since we're inserting before the caret. + edit.Insert(virtualSpan.Start.Position, virtualSpan.GetText()); + + int whiteSpaceSize = virtualSpan.Length - virtualSpan.SnapshotSpan.Length; + string insertedSpace = GetWhiteSpaceForPositionAndVirtualSpace(virtualSpan.SnapshotSpan.End, whiteSpaceSize, useBufferPrimitives: true); + edit.Insert(virtualSpan.Start.Position, insertedSpace); + } + } + } + else + { + // This must be a zero-width block selection, treat like several instances of no-selection and just duplicate the lines. + DuplicateLine(virtualSpan.Start.Position, edit, insertBelow); + insertBelow = false; + } + } + } + + edit.Apply(); + return !(edit.HasFailedChanges || edit.Canceled); + } + }; + return ExecuteAction(Strings.DuplicateSelection, func, ensureVisible: true); + } } /// <summary> diff --git a/src/Text/Impl/EditorOperations/Strings.Designer.cs b/src/Text/Impl/EditorOperations/Strings.Designer.cs index 71c4a07..a7ebcea 100644 --- a/src/Text/Impl/EditorOperations/Strings.Designer.cs +++ b/src/Text/Impl/EditorOperations/Strings.Designer.cs @@ -223,6 +223,15 @@ namespace Microsoft.VisualStudio.Text.Operations.Implementation { } /// <summary> + /// Looks up a localized string similar to Duplicate Selection. + /// </summary> + internal static string DuplicateSelection { + get { + return ResourceManager.GetString("DuplicateSelection", resourceCulture); + } + } + + /// <summary> /// Looks up a localized string similar to Increase line indent. /// </summary> internal static string IncreaseLineIndent { diff --git a/src/Text/Impl/EditorOperations/Strings.resx b/src/Text/Impl/EditorOperations/Strings.resx index fd8f056..3246ccd 100644 --- a/src/Text/Impl/EditorOperations/Strings.resx +++ b/src/Text/Impl/EditorOperations/Strings.resx @@ -258,4 +258,7 @@ <data name="TrimTrailingWhitespace" xml:space="preserve"> <value>Trim trailing whitespace</value> </data> -</root> + <data name="DuplicateSelection" xml:space="preserve"> + <value>Duplicate Selection</value> + </data> +</root>
\ No newline at end of file diff --git a/src/Text/Impl/TextModel/Storage/Page.cs b/src/Text/Impl/TextModel/Storage/Page.cs index 5256373..5721fb0 100644 --- a/src/Text/Impl/TextModel/Storage/Page.cs +++ b/src/Text/Impl/TextModel/Storage/Page.cs @@ -40,9 +40,8 @@ namespace Microsoft.VisualStudio.Text.Implementation _uncompressedContents.SetTarget(contents); } - this.Manager.UpdateMRU(this); + this.Manager.UpdateMRU(this, contents); return contents; - } } } diff --git a/src/Text/Impl/TextModel/Storage/PageManager.cs b/src/Text/Impl/TextModel/Storage/PageManager.cs index eca1287..1d19ac2 100644 --- a/src/Text/Impl/TextModel/Storage/PageManager.cs +++ b/src/Text/Impl/TextModel/Storage/PageManager.cs @@ -6,8 +6,7 @@ // Use at your own risk. // using System; -using System.Collections.Immutable; -using System.Diagnostics; +using System.Collections.Generic; using System.Threading; using Microsoft.VisualStudio.Text.Utilities; @@ -15,45 +14,47 @@ namespace Microsoft.VisualStudio.Text.Implementation { internal class PageManager { - // this class inherits from page so that it participates in the MRU list, of which it is the sentinel node. - private ImmutableList<Page> _mru = ImmutableList<Page>.Empty; + // .Item1 == topemost item in the real MRU (.Item2). It is called out as a special case because updating the + // MRU to put the topmost item at the top of the MRU is a very hot path & has showed up in perf traces. + private Tuple<Page, List<Tuple<Page, char[]>>> _mru; private readonly int _maxPages; public PageManager() { _maxPages = TextModelOptions.CompressedStorageMaxLoadedPages; + _mru = Tuple.Create((Page)null, new List<Tuple<Page, char[]>>(_maxPages)); } - public void UpdateMRU(Page page) + public void UpdateMRU(Page page, char[] contents) { var oldMRU = Volatile.Read(ref _mru); while (true) { - ImmutableList<Page> newMRU; + if (oldMRU.Item1 == page) + { + // This is the very hot path so return immediately if the new page is already topmost. + return; + } - int index = oldMRU.IndexOf(page); - if (index >= 0) + int index = oldMRU.Item2.Count - 1; // Intentionally skip checking the topmost item (we know, due to the check above, that it isn't page). + while (--index >= 0) { - if (index == (oldMRU.Count - 1)) + if (oldMRU.Item2[index].Item1 == page) { - // Page is already at the top of the MRU so nothing needs to be done. - return; + break; } - - // Was in the list, but not at the top. Remove it in preparation for adding it later. - newMRU = oldMRU.RemoveAt(index); - } - else if (oldMRU.Count >= _maxPages) - { - // Wasn't in the list and the list is full. Remove the oldest in preparation for adding it later. - newMRU = oldMRU.RemoveAt(0); - } - else - { - newMRU = oldMRU; } - newMRU = newMRU.Add(page); + var newMRUList = new List<Tuple<Page, char[]>>(_maxPages); + newMRUList.AddRange(oldMRU.Item2); + if (index >= 0) + newMRUList.RemoveAt(index); + else if (newMRUList.Count >= _maxPages) + newMRUList.RemoveAt(0); + + newMRUList.Add(Tuple.Create(page, contents)); + + var newMRU = Tuple.Create(page, newMRUList); var result = Interlocked.CompareExchange(ref _mru, newMRU, oldMRU); if (result == oldMRU) |