From 276312348a21d8b5555bdc466152504c3aaef67c Mon Sep 17 00:00:00 2001 From: Alistair Chapman <alistair@cakebuild.net> Date: Mon, 12 Dec 2016 01:04:18 +1000 Subject: [PATCH] Add support for drag-and-drop with cake and dll files --- src/Cake.VisualStudio.csproj | 3 + src/Editor/CakeDropHandler.cs | 31 ++++++ src/Editor/CakeScriptDropHandler.cs | 100 ++++++++++++++++++++ src/Editor/CakeScriptDropHandlerProvider.cs | 42 ++++++++ 4 files changed, 176 insertions(+) create mode 100644 src/Editor/CakeDropHandler.cs create mode 100644 src/Editor/CakeScriptDropHandler.cs create mode 100644 src/Editor/CakeScriptDropHandlerProvider.cs diff --git a/src/Cake.VisualStudio.csproj b/src/Cake.VisualStudio.csproj index fc47377..84dac9f 100644 --- a/src/Cake.VisualStudio.csproj +++ b/src/Cake.VisualStudio.csproj @@ -72,6 +72,9 @@ <Compile Include="Editor\SmartIndentProvider.cs" /> <Compile Include="Editor\SmartIndent.cs" /> <Compile Include="ContentType\CakeLanguageService.cs" /> + <Compile Include="Editor\CakeDropHandler.cs" /> + <Compile Include="Editor\CakeScriptDropHandler.cs" /> + <Compile Include="Editor\CakeScriptDropHandlerProvider.cs" /> <Compile Include="Helpers\Constants.cs" /> <Compile Include="Helpers\Extensions.cs" /> <Compile Include="Helpers\PathHelpers.cs" /> diff --git a/src/Editor/CakeDropHandler.cs b/src/Editor/CakeDropHandler.cs new file mode 100644 index 0000000..588c58a --- /dev/null +++ b/src/Editor/CakeDropHandler.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.VisualStudio.Text.Editor.DragDrop; + +namespace Cake.VisualStudio.Editor +{ + internal abstract class CakeDropHandler : IDropHandler + { + public DragDropPointerEffects HandleDragStarted(DragDropInfo dragDropInfo) + { + return DragDropPointerEffects.All; + } + + public DragDropPointerEffects HandleDraggingOver(DragDropInfo dragDropInfo) + { + return DragDropPointerEffects.All; + } + + public abstract DragDropPointerEffects HandleDataDropped(DragDropInfo dragDropInfo); + + public abstract bool IsDropEnabled(DragDropInfo dragDropInfo); + + public void HandleDragCanceled() + { + + } + } +} diff --git a/src/Editor/CakeScriptDropHandler.cs b/src/Editor/CakeScriptDropHandler.cs new file mode 100644 index 0000000..b1ad0fa --- /dev/null +++ b/src/Editor/CakeScriptDropHandler.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Editor.DragDrop; + +namespace Cake.VisualStudio.Editor +{ + internal class CakeScriptDropHandler : CakeDropHandler + { + private readonly FileInfo _scriptFile; + private string _targetFileName; + private readonly IWpfTextView _textView; + private ITextDocument _document; + private readonly string[] _supportedFileExtensions = new[] {".cake", ".dll"}; + + public CakeScriptDropHandler(IWpfTextView textView, ITextDocument currentDocument) + { + _textView = textView; + _scriptFile = new FileInfo(currentDocument.FilePath); + _document = currentDocument; + } + + public override DragDropPointerEffects HandleDataDropped(DragDropInfo dragDropInfo) + { + try + { + var relativePath = PackageUtilities.MakeRelative(_scriptFile.FullName, _targetFileName) + .Replace("\\", "/"); + string insertString = null; + switch (Path.GetExtension(relativePath)) + { + case ".cake": + insertString = $"#load \"{relativePath}\""; + break; + case ".dll": + insertString = $"#r \"{relativePath}\""; + break; + } + + using (var edit = _textView.TextBuffer.CreateEdit()) + { + edit.Insert(GetInsertPosition(), insertString + Environment.NewLine); + edit.Apply(); + } + } + catch (Exception) + { + // ignored + } + + return DragDropPointerEffects.Copy; + } + + private int GetInsertPosition() + { + // need to improve logic here to find the first non-#load'ing line in the document. + return 0; + } + + public override bool IsDropEnabled(DragDropInfo dragDropInfo) + { + _targetFileName = GetScriptFileName(dragDropInfo); + if (_targetFileName == null) return false; + var ext = Path.GetExtension(_targetFileName); + return _supportedFileExtensions.Contains(ext, StringComparer.OrdinalIgnoreCase) && + (File.Exists(_targetFileName) || Directory.Exists(_targetFileName)); + } + + private static string GetScriptFileName(DragDropInfo dragDropInfo) + { + var data = new DataObject(dragDropInfo.Data); + + if (dragDropInfo.Data.GetDataPresent("FileDrop")) + { + var files = data.GetFileDropList(); + + if (files.Count == 1) + { + return files[0]; + } + } + else if (dragDropInfo.Data.GetDataPresent("CF_VSSTGPROJECTITEMS") || dragDropInfo.Data.GetDataPresent("CF_VSREFPROJECTITEMS")) + { + return data.GetText(); + } + else if (dragDropInfo.Data.GetDataPresent("MultiURL")) + { + return data.GetText(); + } + return null; + } + } +} diff --git a/src/Editor/CakeScriptDropHandlerProvider.cs b/src/Editor/CakeScriptDropHandlerProvider.cs new file mode 100644 index 0000000..c1ef6f1 --- /dev/null +++ b/src/Editor/CakeScriptDropHandlerProvider.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Cake.VisualStudio.Classifier.Languages; +using Cake.VisualStudio.Helpers; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Editor.DragDrop; +using Microsoft.VisualStudio.Utilities; + +namespace Cake.VisualStudio.Editor +{ + [Export(typeof(IDropHandlerProvider))] + [DropFormat("CF_VSSTGPROJECTITEMS")] + [DropFormat("CF_VSREFPROJECTITEMS")] + [DropFormat("FileDrop")] + [Name("CakeDropHandler")] + [ContentType(Constants.CakeContentType)] + [Order(Before = "DefaultFileDropHandler")] + class CakeScriptDropHandlerProvider : IDropHandlerProvider + { + [Import] + ITextDocumentFactoryService TextDocumentFactoryService { get; set; } + + public IDropHandler GetAssociatedDropHandler(IWpfTextView wpfTextView) + { + ITextDocument document; + + if (TextDocumentFactoryService.TryGetTextDocument(wpfTextView.TextBuffer, out document)) + { + return + wpfTextView.Properties.GetOrCreateSingletonProperty( + () => new CakeScriptDropHandler(wpfTextView, document)); + } + + return null; + } + } +}