diff --git a/RetailCoder.VBE/Inspections/ObjectVariableNotSetInspection.cs b/RetailCoder.VBE/Inspections/ObjectVariableNotSetInspection.cs index d56f91bc18..b244c3bd69 100644 --- a/RetailCoder.VBE/Inspections/ObjectVariableNotSetInspection.cs +++ b/RetailCoder.VBE/Inspections/ObjectVariableNotSetInspection.cs @@ -14,12 +14,12 @@ public sealed class ObjectVariableNotSetInspectionResult : InspectionResultBase private readonly IEnumerable _quickFixes; public ObjectVariableNotSetInspectionResult(IInspection inspection, IdentifierReference reference) - :base(inspection, reference.QualifiedModuleName, reference.Context) + : base(inspection, reference.QualifiedModuleName, reference.Context) { _reference = reference; _quickFixes = new CodeInspectionQuickFix[] { - new SetObjectVariableQuickFix(_reference), + new SetObjectVariableQuickFix(_reference), new IgnoreOnceQuickFix(Context, QualifiedSelection, Inspection.AnnotationName), }; } @@ -36,7 +36,7 @@ public sealed class SetObjectVariableQuickFix : CodeInspectionQuickFix { public SetObjectVariableQuickFix(IdentifierReference reference) : base(context: reference.Context.Parent.Parent as ParserRuleContext, // ImplicitCallStmt_InStmtContext - selection: new QualifiedSelection(reference.QualifiedModuleName, reference.Selection), + selection: new QualifiedSelection(reference.QualifiedModuleName, reference.Selection), description: InspectionsUI.SetObjectVariableQuickFix) { } @@ -96,13 +96,25 @@ public override IEnumerable GetInspectionResults() (item.DeclarationType == DeclarationType.Variable || item.DeclarationType == DeclarationType.Parameter)); + var interestingMembers = + State.AllUserDeclarations.Where(item => + (item.DeclarationType == DeclarationType.Function || item.DeclarationType == DeclarationType.PropertyGet) + && item.IsTypeSpecified + && !ValueTypes.Contains(item.AsTypeName)); + var interestingReferences = interestingDeclarations - .SelectMany(declaration => - declaration.References.Where(reference => - { - var setStmtContext = ParserRuleContextHelper.GetParent(reference.Context); - return reference.IsAssignment && setStmtContext != null && setStmtContext.LET() == null; - })); + .Union(interestingMembers.SelectMany(item => + item.References.Where(reference => + reference.ParentScoping == item && reference.IsAssignment + ).Select(reference => reference.Declaration)) + ) + .SelectMany(declaration => + declaration.References.Where(reference => + { + var setStmtContext = ParserRuleContextHelper.GetParent(reference.Context); + return reference.IsAssignment && setStmtContext != null && setStmtContext.LET() == null; + }) + ); return interestingReferences.Select(reference => new ObjectVariableNotSetInspectionResult(this, reference)); diff --git a/RetailCoder.VBE/UI/Command/MenuItems/ParentMenus/ParentMenuItemBase.cs b/RetailCoder.VBE/UI/Command/MenuItems/ParentMenus/ParentMenuItemBase.cs index bb9f4d2661..fa8ee68834 100644 --- a/RetailCoder.VBE/UI/Command/MenuItems/ParentMenus/ParentMenuItemBase.cs +++ b/RetailCoder.VBE/UI/Command/MenuItems/ParentMenus/ParentMenuItemBase.cs @@ -18,6 +18,7 @@ public abstract class ParentMenuItemBase : IParentMenuItem private readonly int? _beforeIndex; private readonly IDictionary _items; private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + private static bool? _useClipboardForMenuIcons; protected ParentMenuItemBase(string key, IEnumerable items, int? beforeIndex = null) { @@ -178,6 +179,20 @@ public static void SetButtonImage(CommandBarButton button, Image image, Image ma return; } + if (!_useClipboardForMenuIcons.HasValue) + { + _useClipboardForMenuIcons = !HasPictureProperty(button); + } + + if ((bool)_useClipboardForMenuIcons) + { + Bitmap bitMask = MaskedImage(image, mask); + Clipboard.SetImage(bitMask); + button.PasteFace(); + Clipboard.Clear(); + return; + } + try { button.Picture = AxHostConverter.ImageToPictureDisp(image); @@ -189,6 +204,41 @@ public static void SetButtonImage(CommandBarButton button, Image image, Image ma } } + private static Bitmap MaskedImage(Image image, Image mask) + { + //HACK - just blend image with buttonface color (mask is ignored) + //TODO - a real solution would use clipboard formats "Toolbar Button Face" AND "Toolbar Button Mask" + //because PasteFace apparently needs both to be present on the clipboard + //However, the clipboard formats are apparently only accessible in English versions of Office + //https://social.msdn.microsoft.com/Forums/office/en-US/33e97c32-9fc2-4531-b208-67c39ccfb048/question-about-toolbar-button-face-in-pasteface-?forum=vsto + + Bitmap output = new Bitmap(image.Width, image.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb); + + using (Graphics g = Graphics.FromImage(output)) + { + g.Clear(SystemColors.ButtonFace); + g.DrawImage(image, 0, 0); + } + return output; + } + + private static bool HasPictureProperty(CommandBarButton button) + { + try + { + dynamic control = button; + object picture = control.Picture; + return true; + } + + catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException exception) + { + Logger.Debug("Button image cannot be set for button [" + button.Caption + "], because Host VBE CommandBars are too old.\n" + exception); + } + + return false; + } + private class AxHostConverter : AxHost { private AxHostConverter() : base(string.Empty) { } diff --git a/Rubberduck.VBEEditor/Extensions/VbeExtensions.cs b/Rubberduck.VBEEditor/Extensions/VbeExtensions.cs index 24d6c36dfe..7ec8776ce9 100644 --- a/Rubberduck.VBEEditor/Extensions/VbeExtensions.cs +++ b/Rubberduck.VBEEditor/Extensions/VbeExtensions.cs @@ -81,6 +81,8 @@ public static IHostApplication HostApplication(this VBE vbe) return new AutoCADApp(); case "CorelDRAW": return new CorelDRAWApp(); + case "SolidWorks": + return new SolidWorksApp(vbe); } } return null; @@ -111,6 +113,8 @@ public static IHostApplication HostApplication(this VBE vbe) return new AutoCADApp(); case "CorelDRAW": return new CorelDRAWApp(vbe); + case "SolidWorks": + return new SolidWorksApp(vbe); } } @@ -143,6 +147,7 @@ public static bool HostSupportsUnitTests(this VBE vbe) case "Microsoft Visio": case "AutoCAD": case "CorelDRAW": + case "SolidWorks": return true; default: return false; @@ -164,6 +169,7 @@ public static bool HostSupportsUnitTests(this VBE vbe) case "Visio": case "AutoCAD": case "CorelDRAW": + case "SolidWorks": return true; } } diff --git a/Rubberduck.VBEEditor/Rubberduck.VBEditor.csproj b/Rubberduck.VBEEditor/Rubberduck.VBEditor.csproj index b6ecb21ba6..71c5acaa2d 100644 --- a/Rubberduck.VBEEditor/Rubberduck.VBEditor.csproj +++ b/Rubberduck.VBEEditor/Rubberduck.VBEditor.csproj @@ -31,6 +31,14 @@ 4 + + ..\libs\Interop.SldWorks.Extensibility.dll + True + + + ..\libs\Interop.SldWorks.Types.dll + True + False @@ -127,6 +135,7 @@ + diff --git a/Rubberduck.VBEEditor/VBEHost/SolidWorksApp.cs b/Rubberduck.VBEEditor/VBEHost/SolidWorksApp.cs new file mode 100644 index 0000000000..5f0e125c9e --- /dev/null +++ b/Rubberduck.VBEEditor/VBEHost/SolidWorksApp.cs @@ -0,0 +1,24 @@ +using Interop.SldWorks.Types; +using Microsoft.Vbe.Interop; + +namespace Rubberduck.VBEditor.VBEHost +{ + public class SolidWorksApp : HostApplicationBase + { + public SolidWorksApp() : base("SolidWorks") { } + public SolidWorksApp(VBE vbe) : base(vbe, "SolidWorks") { } + + public override void Run(QualifiedMemberName qualifiedMemberName) + { + var projectFileName = qualifiedMemberName.QualifiedModuleName.Project.FileName; + var moduleName = qualifiedMemberName.QualifiedModuleName.ComponentName; + var memberName = qualifiedMemberName.MemberName; + + if (Application != null) + { + SldWorks runner = (SldWorks)Application.SldWorks; + runner.RunMacro(projectFileName, moduleName, memberName); + } + } + } +} diff --git a/RubberduckTests/Inspections/ObjectVariableNotSetInpsectionTests.cs b/RubberduckTests/Inspections/ObjectVariableNotSetInpsectionTests.cs index 4289b979ef..ff62d7840f 100644 --- a/RubberduckTests/Inspections/ObjectVariableNotSetInpsectionTests.cs +++ b/RubberduckTests/Inspections/ObjectVariableNotSetInpsectionTests.cs @@ -215,5 +215,89 @@ Dim target As Range Assert.AreEqual(expectedCode, module.Lines()); } + + [TestMethod] + [TestCategory("Inspections")] + public void ObjectVariableNotSet_ForFunctionAssignment_ReturnsResult() + { + const string inputCode = @" +Private Function CombineRanges(ByVal source As Range, ByVal toCombine As Range) As Range + If source Is Nothing Then + CombineRanges = toCombine 'no inspection result (but there should be one!) + Else + CombineRanges = Union(source, toCombine) 'no inspection result (but there should be one!) + End If +End Function"; + + const string expectedCode = @" +Private Function CombineRanges(ByVal source As Range, ByVal toCombine As Range) As Range + If source Is Nothing Then + Set CombineRanges = toCombine 'no inspection result (but there should be one!) + Else + Set CombineRanges = Union(source, toCombine) 'no inspection result (but there should be one!) + End If +End Function"; + + //Arrange + var builder = new MockVbeBuilder(); + VBComponent component; + var vbe = builder.BuildFromSingleStandardModule(inputCode, out component); + var module = vbe.Object.VBProjects.Item(0).VBComponents.Item(0).CodeModule; + var mockHost = new Mock(); + mockHost.SetupAllProperties(); + var parser = MockParser.Create(vbe.Object, new RubberduckParserState(new Mock().Object)); + + parser.Parse(new CancellationTokenSource()); + if (parser.State.Status >= ParserState.Error) { Assert.Inconclusive("Parser Error"); } + + var inspection = new ObjectVariableNotSetInspection(parser.State); + var inspectionResults = inspection.GetInspectionResults(); + + Assert.AreEqual(2, inspectionResults.Count()); + foreach (var fix in inspectionResults.SelectMany(result => result.QuickFixes.Where(s => s is SetObjectVariableQuickFix))) + { + fix.Fix(); + } + Assert.AreEqual(expectedCode, module.Lines()); + } + + [TestMethod] + [TestCategory("Inspections")] + public void ObjectVariableNotSet_ForPropertyGetAssignment_ReturnsResults() + { + const string inputCode = @" +Private example As MyObject +Public Property Get Example() As MyObject + Example = example +End Property +"; + const string expectedCode = @" +Private example As MyObject +Public Property Get Example() As MyObject + Set Example = example +End Property +"; + //Arrange + var builder = new MockVbeBuilder(); + VBComponent component; + var vbe = builder.BuildFromSingleStandardModule(inputCode, out component); + var module = vbe.Object.VBProjects.Item(0).VBComponents.Item(0).CodeModule; + var mockHost = new Mock(); + mockHost.SetupAllProperties(); + var parser = MockParser.Create(vbe.Object, new RubberduckParserState(new Mock().Object)); + + parser.Parse(new CancellationTokenSource()); + if (parser.State.Status >= ParserState.Error) { Assert.Inconclusive("Parser Error"); } + + var inspection = new ObjectVariableNotSetInspection(parser.State); + var inspectionResults = inspection.GetInspectionResults(); + + Assert.AreEqual(1, inspectionResults.Count()); + foreach (var fix in inspectionResults.SelectMany(result => result.QuickFixes.Where(s => s is SetObjectVariableQuickFix))) + { + fix.Fix(); + } + Assert.AreEqual(expectedCode, module.Lines()); + } } } diff --git a/libs/Interop.SldWorks.Extensibility.dll b/libs/Interop.SldWorks.Extensibility.dll new file mode 100644 index 0000000000..0e2a221581 Binary files /dev/null and b/libs/Interop.SldWorks.Extensibility.dll differ diff --git a/libs/Interop.SldWorks.Types.dll b/libs/Interop.SldWorks.Types.dll new file mode 100644 index 0000000000..390d1713a8 Binary files /dev/null and b/libs/Interop.SldWorks.Types.dll differ