提交 444eb496 编写于 作者: G Gen Lu

Add a command handler for pasting from interactive window

上级 af1126cc
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
extern alias InteractiveWindow;
using System;
using System.ComponentModel.Composition;
using System.Runtime.CompilerServices;
using System.Windows;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Operations;
using Microsoft.VisualStudio.Utilities;
using Microsoft.CodeAnalysis.Editor.Commands;
namespace Microsoft.CodeAnalysis.Editor.CommandHandlers
{
[ExportCommandHandler(PredefinedCommandHandlerNames.InteractivePaste, ContentTypeNames.RoslynContentType)]
[Order(After = PredefinedCommandHandlerNames.Rename)]
[Order(After = PredefinedCommandHandlerNames.FormatDocument)]
[Order(After = PredefinedCommandHandlerNames.Commit)]
[Order(Before = PredefinedCommandHandlerNames.Completion)]
internal sealed class InteractivePasteCommandHandler : ICommandHandler<PasteCommandArgs>
{
// Duplicated string, originally defined at `Microsoft.VisualStudio.InteractiveWindow.PredefinedInteractiveContentTypes`
private const string InteractiveContentTypeName = "Interactive Content";
// Duplicated string, originally defined at `Microsoft.VisualStudio.InteractiveWindow.InteractiveWindow`
private const string InteractiveClipboardFormat = "89344A36-9821-495A-8255-99A63969F87D";
private readonly IEditorOperationsFactoryService _editorOperationsFactoryService;
private readonly ITextUndoHistoryRegistry _textUndoHistoryRegistry;
// This is for unit test purpose only, do not explicitly set this field otherwise.
internal IRoslynClipboard RoslynClipBoard;
[ImportingConstructor]
public InteractivePasteCommandHandler(IEditorOperationsFactoryService editorOperationsFactoryService, ITextUndoHistoryRegistry textUndoHistoryRegistry)
{
_editorOperationsFactoryService = editorOperationsFactoryService;
_textUndoHistoryRegistry = textUndoHistoryRegistry;
RoslynClipBoard = new SystemClipboardWrapper();
}
public void ExecuteCommand(PasteCommandArgs args, Action nextHandler)
{
// InteractiveWindow handles pasting by itself, which including checks for buffer types, etc.
if (!args.TextView.TextBuffer.ContentType.IsOfType(InteractiveContentTypeName) &&
RoslynClipBoard.ContainsData(InteractiveClipboardFormat))
{
PasteInteractiveFormat(args.TextView);
}
else
{
nextHandler();
}
}
public CommandState GetCommandState(PasteCommandArgs args, Func<CommandState> nextHandler)
{
return nextHandler();
}
[MethodImpl(MethodImplOptions.NoInlining)] // Avoid loading InteractiveWindow unless necessary
private void PasteInteractiveFormat(ITextView textView)
{
var editorOperation = _editorOperationsFactoryService.GetEditorOperations(textView);
var blocks = InteractiveWindow::Microsoft.VisualStudio.InteractiveWindow.BufferBlock.Deserialize((string)RoslynClipBoard.GetData(InteractiveClipboardFormat));
using (var transaction = _textUndoHistoryRegistry.GetHistory(textView.TextBuffer).CreateTransaction(EditorFeaturesResources.InteractivePaste))
{
foreach (var block in blocks)
{
switch (block.Kind)
{
case InteractiveWindow::Microsoft.VisualStudio.InteractiveWindow.ReplSpanKind.Input:
case InteractiveWindow::Microsoft.VisualStudio.InteractiveWindow.ReplSpanKind.Output:
case InteractiveWindow::Microsoft.VisualStudio.InteractiveWindow.ReplSpanKind.StandardInput:
editorOperation.InsertText(block.Content);
break;
}
}
transaction.Complete();
}
}
// The mock clipboard used in tests will implement this interface
internal interface IRoslynClipboard
{
bool ContainsData(string format);
object GetData(string format);
}
// In product code, we use this simple wrapper around system clipboard.
// Maybe at some point we can elevate this class and interface so they could be shared among Roslyn code base.
private class SystemClipboardWrapper : IRoslynClipboard
{
public bool ContainsData(string format)
{
return Clipboard.ContainsData(format);
}
public object GetData(string format)
{
return Clipboard.GetData(format);
}
}
}
}
\ No newline at end of file
......@@ -41,6 +41,11 @@
<Project>{18F5FBB8-7570-4412-8CC7-0A86FF13B7BA}</Project>
<Name>TextEditorFeatures</Name>
</ProjectReference>
<ProjectReference Include="..\..\InteractiveWindow\Editor\InteractiveWindow.csproj">
<Project>{01E9BD68-0339-4A13-B42F-A3CA84D164F3}</Project>
<Name>InteractiveWindow</Name>
<Aliases>InteractiveWindow</Aliases>
</ProjectReference>
</ItemGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
......@@ -86,6 +91,7 @@
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Runtime.Remoting" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xaml" />
<Reference Include="System.Xml" />
......@@ -137,6 +143,7 @@
<Compile Include="CommandHandlers\AbstractIntelliSenseCommandHandler.cs" />
<Compile Include="CommandHandlers\CompletionCommandHandler.cs" />
<Compile Include="CommandHandlers\IntelliSenseCommandHandler.cs" />
<Compile Include="CommandHandlers\InteractivePasteCommandHandler.cs" />
<Compile Include="CommandHandlers\QuickInfoCommandHandlerAndSourceProvider.cs" />
<Compile Include="CommandHandlers\QuickInfoCommandHandlerAndSourceProvider.QuickInfoSource.cs" />
<Compile Include="CommandHandlers\SignatureHelpCommandHandler.cs" />
......
......@@ -1195,6 +1195,15 @@ internal class EditorFeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to Paste From Interactive Window.
/// </summary>
internal static string InteractivePaste {
get {
return ResourceManager.GetString("InteractivePaste", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Interface Parts.
/// </summary>
......
......@@ -745,4 +745,7 @@ Do you want to proceed?</value>
<data name="InlineRenameInstructions" xml:space="preserve">
<value>Modify any highlighted location to begin renaming.</value>
</data>
<data name="InteractivePaste" xml:space="preserve">
<value>Paste From Interactive Window</value>
</data>
</root>
\ No newline at end of file
......@@ -134,5 +134,10 @@ internal static class PredefinedCommandHandlerNames
/// handled by the <see cref="IntelliSense"/> command handler.
/// </summary>
public const string SignatureHelp = "Signature Help Command Handler";
/// <summary>
/// Command handler name for Paste Content in Interactive Format.
/// </summary>
public const string InteractivePaste = "Interactive Paste Command Handler";
}
}
......@@ -37,6 +37,10 @@
<Project>{2523D0E6-DF32-4A3E-8AE0-A19BFFAE2EF6}</Project>
<Name>BasicCodeAnalysis</Name>
</ProjectReference>
<ProjectReference Include="..\..\InteractiveWindow\Editor\InteractiveWindow.csproj">
<Project>{01e9bd68-0339-4a13-b42f-a3ca84d164f3}</Project>
<Name>InteractiveWindow</Name>
</ProjectReference>
<ProjectReference Include="..\..\Interactive\EditorFeatures\Core\InteractiveEditorFeatures.csproj">
<Project>{92412d1a-0f23-45b5-b196-58839c524917}</Project>
<Name>InteractiveEditorFeatures</Name>
......@@ -169,6 +173,7 @@
<Compile Include="Diagnostics\InMemoryAssemblyLoader.vb" />
<Compile Include="Diagnostics\UseAutoProperty\UseAutoPropertyTests.vb" />
<Compile Include="GoToImplementation\GoToImplementationTests.vb" />
<Compile Include="InteractivePaste\InteractivePasteCommandHandlerTests.vb" />
<Compile Include="LanguageServices\SyntaxFactsServiceTests.vb" />
<Compile Include="Simplification\SimplifierAPITests.vb" />
<Compile Include="Utilities\GoToHelpers\GoToTestHelpers.vb" />
......@@ -317,4 +322,4 @@
<Import Project="..\..\..\build\Targets\VSL.Imports.targets" />
<Import Project="..\..\..\build\Targets\Roslyn.Toolsets.Xunit.targets" />
</ImportGroup>
</Project>
</Project>
\ 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.
Imports System.Text
Imports System.Windows
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.Editor.Commands
Imports Microsoft.CodeAnalysis.Editor.Host
Imports Microsoft.CodeAnalysis.Editor.Implementation.Interactive
Imports Microsoft.CodeAnalysis.Editor.Shared.Extensions
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces
Imports Microsoft.CodeAnalysis.Text.Shared.Extensions
Imports Microsoft.VisualStudio.Text
Imports Microsoft.VisualStudio.Text.Editor
Imports Microsoft.VisualStudio.Text.Operations
Imports Microsoft.CodeAnalysis.Editor.CommandHandlers
Imports Microsoft.VisualStudio.InteractiveWindow
Namespace Microsoft.CodeAnalysis.Editor.UnitTests.InteractivePaste
Public Class InteractivePasteCommandhandlerTests
Private Function CreateCommandHandler(workspace As TestWorkspace) As InteractivePasteCommandHandler
Dim handler = New InteractivePasteCommandHandler(workspace.GetService(Of IEditorOperationsFactoryService),
workspace.GetService(Of ITextUndoHistoryRegistry))
handler.RoslynClipBoard = New MockClipboard()
Return handler
End Function
<WpfFact>
<Trait(Traits.Feature, Traits.Features.Interactive)>
Public Sub PasteCommandWithInteractiveFormat()
Using workspace = TestWorkspaceFactory.CreateWorkspace(
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document/>
</Project>
</Workspace>)
' Force initialization.
workspace.GetOpenDocumentIds().Select(Function(id) workspace.GetTestDocument(id).GetTextView()).ToList()
Dim textView = workspace.Documents.Single().GetTextView()
Dim handler = CreateCommandHandler(workspace)
Dim clipboard = DirectCast(handler.RoslynClipBoard, MockClipboard)
Dim blocks = New BufferBlock() _
{
New BufferBlock(ReplSpanKind.Output, "a" & vbCr & vbLf & "bc"),
New BufferBlock(ReplSpanKind.Prompt, "> "),
New BufferBlock(ReplSpanKind.Prompt, "< "),
New BufferBlock(ReplSpanKind.Input, "12"),
New BufferBlock(ReplSpanKind.StandardInput, "3")
}
CopyToClipboard(clipboard, blocks, includeRepl:=True)
handler.ExecuteCommand(New PasteCommandArgs(textView, textView.TextBuffer), Sub() Throw New Exception("The operation should have been handled."))
Assert.Equal("a" & vbCr & vbLf & "bc123", textView.TextBuffer.CurrentSnapshot.GetText())
End Using
End Sub
<WpfFact>
<Trait(Traits.Feature, Traits.Features.Interactive)>
Public Sub PasteCommandWithOutInteractiveFormat()
Using workspace = TestWorkspaceFactory.CreateWorkspace(
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document/>
</Project>
</Workspace>)
' Force initialization.
workspace.GetOpenDocumentIds().Select(Function(id) workspace.GetTestDocument(id).GetTextView()).ToList()
Dim textView = workspace.Documents.Single().GetTextView()
Dim editorOperations = workspace.GetService(Of IEditorOperationsFactoryService)().GetEditorOperations(textView)
Dim handler = CreateCommandHandler(workspace)
Dim clipboard = DirectCast(handler.RoslynClipBoard, MockClipboard)
Dim blocks = New BufferBlock() _
{
New BufferBlock(ReplSpanKind.Output, "a" & vbCr & vbLf & "bc"),
New BufferBlock(ReplSpanKind.Prompt, "> "),
New BufferBlock(ReplSpanKind.Prompt, "< "),
New BufferBlock(ReplSpanKind.Input, "12"),
New BufferBlock(ReplSpanKind.StandardInput, "3")
}
CopyToClipboard(clipboard, blocks, includeRepl:=False)
handler.ExecuteCommand(New PasteCommandArgs(textView, textView.TextBuffer), Sub() editorOperations.InsertText("p"))
Assert.Equal("p", textView.TextBuffer.CurrentSnapshot.GetText())
End Using
End Sub
Private Sub CopyToClipboard(clipboard As MockClipboard, blocks As BufferBlock(), includeRepl As Boolean)
clipboard.Clear()
Dim data = New DataObject()
Dim builder = New StringBuilder()
For Each block As BufferBlock In blocks
builder.Append(block.Content)
Next
Dim text = builder.ToString()
data.SetData(DataFormats.UnicodeText, text)
data.SetData(DataFormats.StringFormat, text)
If includeRepl Then
data.SetData(InteractiveWindow.ClipboardFormat, BufferBlock.Serialize(blocks))
End If
clipboard.SetDataObject(data)
End Sub
Private Class MockClipboard
Implements InteractivePasteCommandHandler.IRoslynClipboard
Private _data As DataObject
Friend Sub Clear()
_data = Nothing
End Sub
Friend Sub SetDataObject(data As Object)
_data = DirectCast(data, DataObject)
End Sub
Public Function ContainsData(format As String) As Boolean Implements InteractivePasteCommandHandler.IRoslynClipboard.ContainsData
Return _data IsNot Nothing And _data.GetData(format) IsNot Nothing
End Function
Public Function GetData(format As String) As Object Implements InteractivePasteCommandHandler.IRoslynClipboard.GetData
If _data Is Nothing Then
Return Nothing
Else
Return _data.GetData(format)
End If
End Function
End Class
End Class
End Namespace
......@@ -23,8 +23,10 @@
</PropertyGroup>
<ItemGroup>
<InternalsVisibleToTest Include="Roslyn.InteractiveWindow.UnitTests" />
<InternalsVisibleToTest Include="Roslyn.Services.Editor.UnitTests2"/>
<InternalsVisibleTo Include="Microsoft.VisualStudio.InteractiveServices" />
<InternalsVisibleTo Include="Microsoft.VisualStudio.VsInteractiveWindow" />
<InternalsVisibleTo Include="Microsoft.CodeAnalysis.EditorFeatures" />
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.CoreUtility, Version=$(VisualStudioReferenceAssemblyVersion), Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
......@@ -74,6 +76,7 @@
<Compile Include="Commands\CommandClassifier.cs" />
<Compile Include="Commands\CommandClassifierProvider.cs" />
<Compile Include="Commands\PredefinedInteractiveCommandsContentTypes.cs" />
<Compile Include="Commands\InteractiveCommandsFactory.cs" />
<Compile Include="Commands\InteractiveWindowCommandExtensions.cs" />
<Compile Include="Commands\InteractiveWindowCommands.cs" />
<Compile Include="Commands\HelpCommand.cs" />
......@@ -90,20 +93,17 @@
<Compile Include="IInteractiveWindow.cs" />
<Compile Include="IInteractiveWindowEditorFactoryService.cs" />
<Compile Include="IInteractiveWindowFactoryService.cs" />
<Compile Include="Commands\InteractiveCommandsFactory.cs" />
<Compile Include="IInteractiveWindowOperations.cs" />
<Compile Include="IInteractiveWindowOperations2.cs" />
<Compile Include="InteractiveContentTypeDefinitions.cs" />
<Compile Include="InteractiveWindowClipboard.cs" />
<Compile Include="InteractiveWindow.SpanRangeEdit.cs" />
<Compile Include="InteractiveWindow.SystemClipboard.cs" />
<Compile Include="ProjectionBufferExtensions.cs" />
<Compile Include="InteractiveWindow.PendingSubmission.cs" />
<Compile Include="InteractiveWindow.ReplSpanKind.cs" />
<Compile Include="InteractiveWindow.EditResolver.cs" />
<Compile Include="InteractiveWindow.State.cs" />
<Compile Include="InteractiveWindow.UIThreadOnly.cs" />
<Compile Include="PredefinedInteractiveContentTypes.cs" />
<Compile Include="InteractiveContentTypeDefinitions.cs" />
<Compile Include="InteractiveWindow.cs" />
<Compile Include="InteractiveWindowExtensions.cs" />
<Compile Include="InteractiveWindowOptions.cs" />
......@@ -120,7 +120,9 @@
<Compile Include="Output\ResizingAdorner.cs" />
<Compile Include="Output\SortedSpans.cs" />
<Compile Include="Output\ZoomableInlineAdornment.cs" />
<Compile Include="PredefinedInteractiveContentTypes.cs" />
<Compile Include="PredefinedInteractiveTextViewRoles.cs" />
<Compile Include="ProjectionBufferExtensions.cs" />
<Compile Include="SmartIndent\InteractiveSmartIndenter.cs" />
<Compile Include="SmartIndent\InteractiveSmartIndenterProvider.cs" />
<Compile Include="SmartUpDownOption.cs" />
......
......@@ -98,4 +98,4 @@
<Import Project="..\..\..\build\Targets\VSL.Imports.targets" />
<Import Project="..\..\..\build\Targets\Roslyn.Toolsets.Xunit.targets" />
</ImportGroup>
</Project>
</Project>
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册