diff --git a/RetailCoder.VBE/App.cs b/RetailCoder.VBE/App.cs
index 374f7b87b4..78f6f1f1d6 100644
--- a/RetailCoder.VBE/App.cs
+++ b/RetailCoder.VBE/App.cs
@@ -15,11 +15,13 @@
using System.Windows.Forms;
using Rubberduck.UI.Command;
using Rubberduck.UI.Command.MenuItems.CommandBars;
+using Rubberduck.VBEditor.Events;
using Rubberduck.VBEditor.SafeComWrappers;
using Rubberduck.VBEditor.SafeComWrappers.Abstract;
using Rubberduck.VBEditor.SafeComWrappers.MSForms;
using Rubberduck.VBEditor.SafeComWrappers.Office.Core.Abstract;
using Rubberduck.VersionCheck;
+using Application = System.Windows.Forms.Application;
namespace Rubberduck
{
@@ -61,7 +63,9 @@ public App(IVBE vbe,
_version = version;
_checkVersionCommand = checkVersionCommand;
- _hooks.MessageReceived += _hooks_MessageReceived;
+ VBEEvents.SelectionChanged += _vbe_SelectionChanged;
+ VBEEvents.WindowFocusChange += _vbe_FocusChanged;
+
_configService.SettingsChanged += _configService_SettingsChanged;
_parser.State.StateChanged += Parser_StateChanged;
_parser.State.StatusMessageUpdate += State_StatusMessageUpdate;
@@ -81,17 +85,57 @@ private void State_StatusMessageUpdate(object sender, RubberduckStatusMessageEve
_stateBar.SetStatusLabelCaption(message, _parser.State.ModuleExceptions.Count);
}
- private void _hooks_MessageReceived(object sender, HookEventArgs e)
+ private void _vbe_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
- RefreshSelection();
+ RefreshSelection(e.CodePane);
+ }
+
+ private void _vbe_FocusChanged(object sender, WindowChangedEventArgs e)
+ {
+ if (e.EventType == WindowChangedEventArgs.FocusType.GotFocus)
+ {
+ switch (e.Window.Type)
+ {
+ case WindowKind.Designer:
+ RefreshSelection(e.Window);
+ break;
+ case WindowKind.CodeWindow:
+ RefreshSelection(e.CodePane);
+ break;
+ }
+ }
}
private ParserState _lastStatus;
private Declaration _lastSelectedDeclaration;
-
- private void RefreshSelection()
+ private void RefreshSelection(ICodePane pane)
{
+ Declaration selectedDeclaration = null;
+ if (!pane.IsWrappingNullReference)
+ {
+ selectedDeclaration = _parser.State.FindSelectedDeclaration(pane);
+ var refCount = selectedDeclaration == null ? 0 : selectedDeclaration.References.Count();
+ var caption = _stateBar.GetContextSelectionCaption(_vbe.ActiveCodePane, selectedDeclaration);
+ _stateBar.SetContextSelectionCaption(caption, refCount);
+ }
+
+ var currentStatus = _parser.State.Status;
+ if (ShouldEvaluateCanExecute(selectedDeclaration, currentStatus))
+ {
+ _appMenus.EvaluateCanExecute(_parser.State);
+ _stateBar.EvaluateCanExecute(_parser.State);
+ }
+
+ _lastStatus = currentStatus;
+ _lastSelectedDeclaration = selectedDeclaration;
+ }
+ private void RefreshSelection(IWindow window)
+ {
+ if (window.IsWrappingNullReference || window.Type != WindowKind.Designer)
+ {
+ return;
+ }
var caption = String.Empty;
var refCount = 0;
@@ -103,7 +147,7 @@ private void RefreshSelection()
//TODO - I doubt this is the best way to check if the SelectedVBComponent and the ActiveCodePane are the same component.
if (windowKind == WindowKind.CodeWindow || (!_vbe.SelectedVBComponent.IsWrappingNullReference
- && component.ParentProject.ProjectId == pane.CodeModule.Parent.ParentProject.ProjectId
+ && component.ParentProject.ProjectId == pane.CodeModule.Parent.ParentProject.ProjectId
&& component.Name == pane.CodeModule.Parent.Name))
{
selectedDeclaration = _parser.State.FindSelectedDeclaration(pane);
@@ -120,13 +164,13 @@ private void RefreshSelection()
{
//The user might have selected the project node in Project Explorer
//If they've chosen a folder, we'll return the project anyway
- caption = !_vbe.ActiveVBProject.IsWrappingNullReference
+ caption = !_vbe.ActiveVBProject.IsWrappingNullReference
? _vbe.ActiveVBProject.Name
: null;
}
else
{
- caption = component.Type == ComponentType.UserForm && component.HasOpenDesigner
+ caption = component.Type == ComponentType.UserForm && component.HasOpenDesigner
? GetComponentControlsCaption(component)
: String.Format("{0}.{1} ({2})", component.ParentProject.Name, component.Name, component.Type);
}
@@ -322,10 +366,8 @@ public void Dispose()
_parser.State.StatusMessageUpdate -= State_StatusMessageUpdate;
}
- if (_hooks != null)
- {
- _hooks.MessageReceived -= _hooks_MessageReceived;
- }
+ VBEEvents.SelectionChanged += _vbe_SelectionChanged;
+ VBEEvents.WindowFocusChange += _vbe_FocusChanged;
if (_configService != null)
{
diff --git a/RetailCoder.VBE/Common/WinAPI/RawInput.cs b/RetailCoder.VBE/Common/WinAPI/RawInput.cs
index 014aab4585..003189daaa 100644
--- a/RetailCoder.VBE/Common/WinAPI/RawInput.cs
+++ b/RetailCoder.VBE/Common/WinAPI/RawInput.cs
@@ -3,7 +3,7 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.InteropServices;
-using Rubberduck.UI;
+using Rubberduck.VBEditor.WindowsApi;
namespace Rubberduck.Common.WinAPI
{
diff --git a/RetailCoder.VBE/Common/WinAPI/User32.cs b/RetailCoder.VBE/Common/WinAPI/User32.cs
index f4887ba4e3..c3d6866d06 100644
--- a/RetailCoder.VBE/Common/WinAPI/User32.cs
+++ b/RetailCoder.VBE/Common/WinAPI/User32.cs
@@ -190,36 +190,5 @@ public static class User32
public delegate int WindowEnumProc(IntPtr hwnd, IntPtr lparam);
[DllImport("user32.dll")]
public static extern bool EnumChildWindows(IntPtr hwnd, WindowEnumProc func, IntPtr lParam);
-
- ///
- /// A helper function that returns true when the specified handle is that of the foreground window.
- ///
- /// The handle for the VBE's MainWindow.
- ///
- public static bool IsVbeWindowActive(IntPtr mainWindowHandle)
- {
- uint vbeThread;
- GetWindowThreadProcessId(mainWindowHandle, out vbeThread);
-
- uint hThread;
- GetWindowThreadProcessId(GetForegroundWindow(), out hThread);
-
- return (IntPtr)hThread == (IntPtr)vbeThread;
- }
-
- public enum WindowType
- {
- Indeterminate,
- VbaWindow,
- DesignerWindow
- }
-
- public static WindowType ToWindowType(this IntPtr hwnd)
- {
- var name = new StringBuilder(128);
- GetClassName(hwnd, name, name.Capacity);
- WindowType id;
- return Enum.TryParse(name.ToString(), out id) ? id : WindowType.Indeterminate;
- }
}
}
diff --git a/RetailCoder.VBE/Extension.cs b/RetailCoder.VBE/Extension.cs
index 9b5b2e3c26..b53012ff34 100644
--- a/RetailCoder.VBE/Extension.cs
+++ b/RetailCoder.VBE/Extension.cs
@@ -18,6 +18,7 @@
using NLog;
using Rubberduck.Settings;
using Rubberduck.SettingsProvider;
+using Rubberduck.VBEditor.Events;
using Rubberduck.VBEditor.SafeComWrappers.Abstract;
namespace Rubberduck
@@ -53,8 +54,9 @@ public void OnConnection(object Application, ext_ConnectMode ConnectMode, object
{
if (Application is Microsoft.Vbe.Interop.VBE)
{
- var vbe = (Microsoft.Vbe.Interop.VBE) Application;
+ var vbe = (Microsoft.Vbe.Interop.VBE) Application;
_ide = new VBEditor.SafeComWrappers.VBA.VBE(vbe);
+ VBEEvents.HookEvents(_ide);
var addin = (Microsoft.Vbe.Interop.AddIn)AddInInst;
_addin = new VBEditor.SafeComWrappers.VBA.AddIn(addin) { Object = this };
@@ -87,7 +89,7 @@ public void OnConnection(object Application, ext_ConnectMode ConnectMode, object
Assembly LoadFromSameFolder(object sender, ResolveEventArgs args)
{
- var folderPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+ var folderPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? string.Empty;
var assemblyPath = Path.Combine(folderPath, new AssemblyName(args.Name).Name + ".dll");
if (!File.Exists(assemblyPath))
{
@@ -219,6 +221,8 @@ private void Startup()
private void ShutdownAddIn()
{
+ VBEEvents.UnhookEvents();
+
var currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyResolve -= LoadFromSameFolder;
diff --git a/RetailCoder.VBE/Inspections/QuickFixes/PassParameterByReferenceQuickFix.cs b/RetailCoder.VBE/Inspections/QuickFixes/PassParameterByReferenceQuickFix.cs
index c53ed13793..60c606608b 100644
--- a/RetailCoder.VBE/Inspections/QuickFixes/PassParameterByReferenceQuickFix.cs
+++ b/RetailCoder.VBE/Inspections/QuickFixes/PassParameterByReferenceQuickFix.cs
@@ -1,9 +1,11 @@
using Antlr4.Runtime;
+using Antlr4.Runtime.Tree;
using Rubberduck.Inspections.Abstract;
using Rubberduck.Inspections.Resources;
using Rubberduck.Parsing.Grammar;
+using Rubberduck.Parsing.Symbols;
using Rubberduck.VBEditor;
-using System.Text.RegularExpressions;
+using System.Linq;
namespace Rubberduck.Inspections.QuickFixes
{
@@ -12,31 +14,75 @@ namespace Rubberduck.Inspections.QuickFixes
///
public class PassParameterByReferenceQuickFix : QuickFixBase
{
- public PassParameterByReferenceQuickFix(ParserRuleContext context, QualifiedSelection selection)
- : base(context, selection, InspectionsUI.PassParameterByReferenceQuickFix)
+ private Declaration _target;
+
+ public PassParameterByReferenceQuickFix(Declaration target, QualifiedSelection selection)
+ : base(target.Context, selection, InspectionsUI.PassParameterByReferenceQuickFix)
{
+ _target = target;
}
public override void Fix()
{
- var parameter = Context.GetText();
+ var argCtxt = GetArgContextForIdentifier(Context.Parent.Parent, _target.IdentifierName);
- var parts = parameter.Split(new char[]{' '},2);
- if (1 != parts.GetUpperBound(0))
- {
- return;
- }
- parts[0] = parts[0].Replace(Tokens.ByVal, Tokens.ByRef);
- var newContent = parts[0] + " " + parts[1];
+ var terminalNode = argCtxt.BYVAL();
- var selection = Selection.Selection;
+ var replacementLine = GenerateByRefReplacementLine(terminalNode);
+
+ ReplaceModuleLine(terminalNode.Symbol.Line, replacementLine);
+
+ }
+ private VBAParser.ArgContext GetArgContextForIdentifier(RuleContext context, string identifier)
+ {
+ var argList = GetArgListForContext(context);
+ return argList.arg().SingleOrDefault(parameter =>
+ Identifier.GetName(parameter).Equals(identifier));
+ }
+ private string GenerateByRefReplacementLine(ITerminalNode terminalNode)
+ {
+ var module = Selection.QualifiedName.Component.CodeModule;
+ var byValTokenLine = module.GetLines(terminalNode.Symbol.Line, 1);
+ return ReplaceAtIndex(byValTokenLine, Tokens.ByVal, Tokens.ByRef, terminalNode.Symbol.Column);
+ }
+ private void ReplaceModuleLine(int lineNumber, string replacementLine)
+ {
var module = Selection.QualifiedName.Component.CodeModule;
+ module.DeleteLines(lineNumber);
+ module.InsertLines(lineNumber, replacementLine);
+ }
+ private string ReplaceAtIndex(string input, string toReplace, string replacement, int startIndex)
+ {
+ int stopIndex = startIndex + toReplace.Length;
+ var prefix = input.Substring(0, startIndex);
+ var suffix = input.Substring(stopIndex + 1);
+ var tokenToBeReplaced = input.Substring(startIndex, stopIndex - startIndex + 1);
+ return prefix + tokenToBeReplaced.Replace(toReplace, replacement) + suffix;
+ }
+ private VBAParser.ArgListContext GetArgListForContext(RuleContext context)
+ {
+ if (context is VBAParser.SubStmtContext)
+ {
+ return ((VBAParser.SubStmtContext)context).argList();
+ }
+ else if (context is VBAParser.FunctionStmtContext)
+ {
+ return ((VBAParser.FunctionStmtContext)context).argList();
+ }
+ else if (context is VBAParser.PropertyLetStmtContext)
+ {
+ return ((VBAParser.PropertyLetStmtContext)context).argList();
+ }
+ else if (context is VBAParser.PropertyGetStmtContext)
+ {
+ return ((VBAParser.PropertyGetStmtContext)context).argList();
+ }
+ else if (context is VBAParser.PropertySetStmtContext)
{
- var lines = module.GetLines(selection.StartLine, selection.LineCount);
- var result = lines.Replace(parameter, newContent);
- module.ReplaceLine(selection.StartLine, result);
+ return ((VBAParser.PropertySetStmtContext)context).argList();
}
+ return null;
}
}
}
\ No newline at end of file
diff --git a/RetailCoder.VBE/Inspections/Results/AssignedByValParameterInspectionResult.cs b/RetailCoder.VBE/Inspections/Results/AssignedByValParameterInspectionResult.cs
index d622bec60e..0010f208b6 100644
--- a/RetailCoder.VBE/Inspections/Results/AssignedByValParameterInspectionResult.cs
+++ b/RetailCoder.VBE/Inspections/Results/AssignedByValParameterInspectionResult.cs
@@ -28,7 +28,7 @@ public override IEnumerable QuickFixes
return _quickFixes ?? (_quickFixes = new QuickFixBase[]
{
new AssignedByValParameterQuickFix(Target, QualifiedSelection),
- new PassParameterByReferenceQuickFix(Target.Context, QualifiedSelection),
+ new PassParameterByReferenceQuickFix(Target, QualifiedSelection),
new IgnoreOnceQuickFix(Context, QualifiedSelection, Inspection.AnnotationName)
});
}
diff --git a/RetailCoder.VBE/Rubberduck.csproj b/RetailCoder.VBE/Rubberduck.csproj
index 21c0b9aa32..cacdcfb629 100644
--- a/RetailCoder.VBE/Rubberduck.csproj
+++ b/RetailCoder.VBE/Rubberduck.csproj
@@ -370,7 +370,6 @@
-
@@ -500,7 +499,6 @@
-
diff --git a/RetailCoder.VBE/UI/DockableToolwindowPresenter.cs b/RetailCoder.VBE/UI/DockableToolwindowPresenter.cs
index 4264db2308..047e66ca4f 100644
--- a/RetailCoder.VBE/UI/DockableToolwindowPresenter.cs
+++ b/RetailCoder.VBE/UI/DockableToolwindowPresenter.cs
@@ -130,16 +130,6 @@ protected virtual void Dispose(bool disposing)
}
if (disposing && _window != null)
{
- if (_userControlObject != null)
- {
- ((_DockableWindowHost)_userControlObject).Dispose();
- }
- _userControlObject = null;
-
- if (_userControl != null)
- {
- _userControl.Dispose();
- }
// cleanup unmanaged resource wrappers
_window.Close();
_window.Release(true);
diff --git a/RetailCoder.VBE/UI/DockableWindowHost.cs b/RetailCoder.VBE/UI/DockableWindowHost.cs
index b2b5442331..d7239a710f 100644
--- a/RetailCoder.VBE/UI/DockableWindowHost.cs
+++ b/RetailCoder.VBE/UI/DockableWindowHost.cs
@@ -5,6 +5,8 @@
using System.Windows.Forms;
using Rubberduck.Common.WinAPI;
using Rubberduck.VBEditor;
+using Rubberduck.VBEditor.WindowsApi;
+using User32 = Rubberduck.Common.WinAPI.User32;
namespace Rubberduck.UI
{
@@ -52,6 +54,7 @@ private struct LParam
private IntPtr _parentHandle;
private ParentWindow _subClassingWindow;
+ private GCHandle _thisHandle;
internal void AddUserControl(UserControl control, IntPtr vbeHwnd)
{
@@ -63,7 +66,7 @@ internal void AddUserControl(UserControl control, IntPtr vbeHwnd)
//since we have to inherit from UserControl we don't have to keep handling window messages until the VBE gets
//around to destroying the control's host or it results in an access violation when the base class is disposed.
//We need to manually call base.Dispose() ONLY in response to a WM_DESTROY message.
- GC.KeepAlive(this);
+ _thisHandle = GCHandle.Alloc(this, GCHandleType.Normal);
if (control != null)
{
@@ -143,7 +146,7 @@ protected override void DefWndProc(ref Message m)
//See the comment in the ctor for why we have to listen for this.
if (m.Msg == (int) WM.DESTROY)
{
- base.Dispose(true);
+ _thisHandle.Free();
return;
}
base.DefWndProc(ref m);
diff --git a/Rubberduck.Parsing/Symbols/Identifier.cs b/Rubberduck.Parsing/Symbols/Identifier.cs
index 1386e73442..ba3021e9d4 100644
--- a/Rubberduck.Parsing/Symbols/Identifier.cs
+++ b/Rubberduck.Parsing/Symbols/Identifier.cs
@@ -7,6 +7,11 @@ namespace Rubberduck.Parsing.Symbols
{
public static class Identifier
{
+ public static string GetName(VBAParser.ArgContext context)
+ {
+ return GetName(context.unrestrictedIdentifier());
+ }
+
public static string GetName(VBAParser.FunctionNameContext context)
{
return GetName(context.identifier());
diff --git a/Rubberduck.Parsing/VBA/RubberduckParserState.cs b/Rubberduck.Parsing/VBA/RubberduckParserState.cs
index e8637d9965..c4c8cd2af6 100644
--- a/Rubberduck.Parsing/VBA/RubberduckParserState.cs
+++ b/Rubberduck.Parsing/VBA/RubberduckParserState.cs
@@ -72,7 +72,7 @@ internal void RefreshFinder(IHostApplication host)
DeclarationFinder = new DeclarationFinder(AllDeclarations, AllAnnotations, host);
}
- private IVBE _vbe;
+ private readonly IVBE _vbe;
public RubberduckParserState(IVBE vbe)
{
var values = Enum.GetValues(typeof(ParserState));
@@ -82,32 +82,27 @@ public RubberduckParserState(IVBE vbe)
}
_vbe = vbe;
-
- if (_vbe != null && _vbe.VBProjects != null)
- {
- VBProjects.ProjectAdded += Sinks_ProjectAdded;
- VBProjects.ProjectRemoved += Sinks_ProjectRemoved;
- VBProjects.ProjectRenamed += Sinks_ProjectRenamed;
- foreach (var project in _vbe.VBProjects.Where(proj => proj.VBComponents != null))
- {
- AddComponentEventHandlers(project);
- }
- }
-
+ AddEventHandlers();
IsEnabled = true;
}
#region Event Handling
- private void AddComponentEventHandlers(IVBProject project)
+ private void AddEventHandlers()
{
+ VBProjects.ProjectAdded += Sinks_ProjectAdded;
+ VBProjects.ProjectRemoved += Sinks_ProjectRemoved;
+ VBProjects.ProjectRenamed += Sinks_ProjectRenamed;
VBComponents.ComponentAdded += Sinks_ComponentAdded;
VBComponents.ComponentRemoved += Sinks_ComponentRemoved;
VBComponents.ComponentRenamed += Sinks_ComponentRenamed;
}
- private void RemoveComponentEventHandlers(IVBProject project)
+ private void RemoveEventHandlers()
{
+ VBProjects.ProjectAdded += Sinks_ProjectAdded;
+ VBProjects.ProjectRemoved += Sinks_ProjectRemoved;
+ VBProjects.ProjectRenamed += Sinks_ProjectRenamed;
VBComponents.ComponentAdded -= Sinks_ComponentAdded;
VBComponents.ComponentRemoved -= Sinks_ComponentRemoved;
VBComponents.ComponentRenamed -= Sinks_ComponentRenamed;
@@ -118,8 +113,6 @@ private void Sinks_ProjectAdded(object sender, ProjectEventArgs e)
if (!e.Project.VBE.IsInDesignMode) { return; }
Logger.Debug("Project '{0}' was added.", e.ProjectId);
- AddComponentEventHandlers(e.Project);
-
RefreshProjects(_vbe); // note side-effect: assigns ProjectId/HelpFile
OnParseRequested(sender);
}
@@ -129,8 +122,6 @@ private void Sinks_ProjectRemoved(object sender, ProjectEventArgs e)
if (!e.Project.VBE.IsInDesignMode) { return; }
Debug.Assert(e.ProjectId != null);
- RemoveComponentEventHandlers(e.Project);
-
RemoveProject(e.ProjectId, true);
OnParseRequested(sender);
}
@@ -1110,16 +1101,7 @@ public void Dispose()
CoClasses.Clear();
}
- if (_vbe != null && _vbe.VBProjects != null)
- {
- VBProjects.ProjectAdded -= Sinks_ProjectAdded;
- VBProjects.ProjectRemoved -= Sinks_ProjectRemoved;
- VBProjects.ProjectRenamed -= Sinks_ProjectRenamed;
- foreach (var project in _vbe.VBProjects.Where(proj => proj.VBComponents != null))
- {
- RemoveComponentEventHandlers(project);
- }
- }
+ RemoveEventHandlers();
_moduleStates.Clear();
_declarationSelections.Clear();
diff --git a/Rubberduck.SmartIndenter/AbsoluteCodeLine.cs b/Rubberduck.SmartIndenter/AbsoluteCodeLine.cs
index 19d746e85b..e0cd943e37 100644
--- a/Rubberduck.SmartIndenter/AbsoluteCodeLine.cs
+++ b/Rubberduck.SmartIndenter/AbsoluteCodeLine.cs
@@ -10,8 +10,6 @@ namespace Rubberduck.SmartIndenter
internal class AbsoluteCodeLine
{
private const string StupidLineEnding = ": _";
- private static readonly Regex StringReplaceRegex = new Regex(StringLiteralAndBracketEscaper.StringPlaceholder.ToString(CultureInfo.InvariantCulture));
- private static readonly Regex BracketReplaceRegex = new Regex(StringLiteralAndBracketEscaper.BracketPlaceholder.ToString(CultureInfo.InvariantCulture));
private static readonly Regex LineNumberRegex = new Regex(@"^(?(-?\d+)|(&H[0-9A-F]{1,8}))\s+(?.*)", RegexOptions.ExplicitCapture);
private static readonly Regex EndOfLineCommentRegex = new Regex(@"^(?!(Rem\s)|('))(?[^']*)(\s(?'.*))$", RegexOptions.ExplicitCapture);
private static readonly Regex ProcedureStartRegex = new Regex(@"^(Public\s|Private\s|Friend\s)?(Static\s)?(Sub|Function|Property\s(Let|Get|Set))\s");
@@ -33,8 +31,6 @@ internal class AbsoluteCodeLine
private readonly bool _stupidLineEnding;
private readonly string[] _segments;
private readonly StringLiteralAndBracketEscaper _escaper;
- //private List _strings;
- //private List _brackets;
public AbsoluteCodeLine(string code, IIndenterSettings settings) : this(code, settings, null) { }
@@ -61,8 +57,6 @@ public AbsoluteCodeLine(string code, IIndenterSettings settings, AbsoluteCodeLin
ExtractLineNumber();
ExtractEndOfLineComment();
- _code = Regex.Replace(_code, StringLiteralAndBracketEscaper.StringPlaceholder + "+", StringLiteralAndBracketEscaper.StringPlaceholder.ToString(CultureInfo.InvariantCulture));
- _code = Regex.Replace(_code, StringLiteralAndBracketEscaper.BracketPlaceholder + "+", StringLiteralAndBracketEscaper.BracketPlaceholder.ToString(CultureInfo.InvariantCulture)).Trim();
_segments = _code.Split(new[] { ": " }, StringSplitOptions.None);
}
@@ -267,38 +261,29 @@ public string Indent(int indents, bool atProcStart, bool absolute = false)
}
var code = string.Join(": ", _segments);
- if (_escaper.EscapedStrings.Any())
- {
- code = _escaper.EscapedStrings.Aggregate(code, (current, literal) => StringReplaceRegex.Replace(current, literal, 1));
- }
- if (_escaper.EscapedBrackets.Any())
- {
- code = _escaper.EscapedBrackets.Aggregate(code, (current, expr) => BracketReplaceRegex.Replace(current, expr, 1));
- }
-
code = string.Join(string.Empty, number, new string(' ', gap), code);
if (string.IsNullOrEmpty(EndOfLineComment))
{
- return code + (_stupidLineEnding ? StupidLineEnding : string.Empty);
+ return _escaper.UnescapeIndented(code + (_stupidLineEnding ? StupidLineEnding : string.Empty));
}
var position = Original.LastIndexOf(EndOfLineComment, StringComparison.Ordinal);
switch (_settings.EndOfLineCommentStyle)
{
case EndOfLineCommentStyle.Absolute:
- return string.Format("{0}{1}{2}{3}", code, new string(' ', Math.Max(position - code.Length, 1)),
- EndOfLineComment, _stupidLineEnding ? StupidLineEnding : string.Empty);
+ return _escaper.UnescapeIndented(string.Format("{0}{1}{2}{3}", code, new string(' ', Math.Max(position - code.Length, 1)),
+ EndOfLineComment, _stupidLineEnding ? StupidLineEnding : string.Empty));
case EndOfLineCommentStyle.SameGap:
var uncommented = Original.Substring(0, position - 1);
- return string.Format("{0}{1}{2}{3}", code, new string(' ', uncommented.Length - uncommented.TrimEnd().Length + 1),
- EndOfLineComment, _stupidLineEnding ? StupidLineEnding : string.Empty);
+ return _escaper.UnescapeIndented(string.Format("{0}{1}{2}{3}", code, new string(' ', uncommented.Length - uncommented.TrimEnd().Length + 1),
+ EndOfLineComment, _stupidLineEnding ? StupidLineEnding : string.Empty));
case EndOfLineCommentStyle.StandardGap:
- return string.Format("{0}{1}{2}{3}", code, new string(' ', _settings.IndentSpaces * 2), EndOfLineComment,
- _stupidLineEnding ? StupidLineEnding : string.Empty);
+ return _escaper.UnescapeIndented(string.Format("{0}{1}{2}{3}", code, new string(' ', _settings.IndentSpaces * 2), EndOfLineComment,
+ _stupidLineEnding ? StupidLineEnding : string.Empty));
case EndOfLineCommentStyle.AlignInColumn:
var align = _settings.EndOfLineCommentColumnSpaceAlignment - code.Length;
- return string.Format("{0}{1}{2}{3}", code, new string(' ', Math.Max(align - 1, 1)), EndOfLineComment,
- _stupidLineEnding ? StupidLineEnding : string.Empty);
+ return _escaper.UnescapeIndented(string.Format("{0}{1}{2}{3}", code, new string(' ', Math.Max(align - 1, 1)), EndOfLineComment,
+ _stupidLineEnding ? StupidLineEnding : string.Empty));
default:
throw new InvalidEnumArgumentException();
}
diff --git a/Rubberduck.SmartIndenter/Rubberduck.SmartIndenter.csproj b/Rubberduck.SmartIndenter/Rubberduck.SmartIndenter.csproj
index 901240fb30..30c01cd295 100644
--- a/Rubberduck.SmartIndenter/Rubberduck.SmartIndenter.csproj
+++ b/Rubberduck.SmartIndenter/Rubberduck.SmartIndenter.csproj
@@ -56,7 +56,6 @@
-
diff --git a/Rubberduck.SmartIndenter/Selection.cs b/Rubberduck.SmartIndenter/Selection.cs
deleted file mode 100644
index 2370096e29..0000000000
--- a/Rubberduck.SmartIndenter/Selection.cs
+++ /dev/null
@@ -1,3 +0,0 @@
-namespace Rubberduck.SmartIndenter
-{
-}
diff --git a/Rubberduck.SmartIndenter/StringLiteralAndBracketEscaper.cs b/Rubberduck.SmartIndenter/StringLiteralAndBracketEscaper.cs
index 7b98f8a26e..2ee10f2e8e 100644
--- a/Rubberduck.SmartIndenter/StringLiteralAndBracketEscaper.cs
+++ b/Rubberduck.SmartIndenter/StringLiteralAndBracketEscaper.cs
@@ -1,12 +1,16 @@
using System.Collections.Generic;
using System.Linq;
+using System.Text.RegularExpressions;
namespace Rubberduck.SmartIndenter
{
internal class StringLiteralAndBracketEscaper
{
public const char StringPlaceholder = '\a';
- public const char BracketPlaceholder = '\x2';
+ public const char BracketPlaceholder = '\x02';
+
+ private static readonly Regex StringReplaceRegex = new Regex("\a+");
+ private static readonly Regex BracketReplaceRegex = new Regex("\x02+");
private readonly List _strings = new List();
private readonly List _brackets = new List();
@@ -18,6 +22,20 @@ internal class StringLiteralAndBracketEscaper
public IEnumerable EscapedStrings { get { return _strings; } }
public IEnumerable EscapedBrackets { get { return _brackets; } }
+ public string UnescapeIndented(string indented)
+ {
+ var code = indented;
+ if (_strings.Any())
+ {
+ code = _strings.Aggregate(code, (current, literal) => StringReplaceRegex.Replace(current, literal, 1));
+ }
+ if (_brackets.Any())
+ {
+ code = _brackets.Aggregate(code, (current, expr) => BracketReplaceRegex.Replace(current, expr, 1));
+ }
+ return code;
+ }
+
public StringLiteralAndBracketEscaper(string code)
{
_unescaped = code;
@@ -63,8 +81,8 @@ public StringLiteralAndBracketEscaper(string code)
continue;
}
bracketed = false;
- _brackets.Add(new string(chars.Skip(brkpos).Take(c - brkpos).ToArray()));
- for (var e = brkpos; e < c; e++)
+ _brackets.Add(new string(chars.Skip(brkpos).Take(c - brkpos + 1).ToArray()));
+ for (var e = brkpos; e <= c; e++)
{
chars[e] = BracketPlaceholder;
}
diff --git a/Rubberduck.VBEEditor/Events/SelectionChangedEventArgs.cs b/Rubberduck.VBEEditor/Events/SelectionChangedEventArgs.cs
new file mode 100644
index 0000000000..7cff78714e
--- /dev/null
+++ b/Rubberduck.VBEEditor/Events/SelectionChangedEventArgs.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Rubberduck.VBEditor.SafeComWrappers.Abstract;
+
+namespace Rubberduck.VBEditor.Events
+{
+ public class SelectionChangedEventArgs : EventArgs
+ {
+ public ICodePane CodePane { get; private set; }
+
+ public SelectionChangedEventArgs(ICodePane pane)
+ {
+ CodePane = pane;
+ }
+ }
+}
diff --git a/Rubberduck.VBEEditor/Events/VBEEvents.cs b/Rubberduck.VBEEditor/Events/VBEEvents.cs
new file mode 100644
index 0000000000..fcebb59ce6
--- /dev/null
+++ b/Rubberduck.VBEEditor/Events/VBEEvents.cs
@@ -0,0 +1,190 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using Rubberduck.VBEditor.SafeComWrappers.Abstract;
+using Rubberduck.VBEditor.SafeComWrappers.MSForms;
+using Rubberduck.VBEditor.WindowsApi;
+
+namespace Rubberduck.VBEditor.Events
+{
+ public static class VBEEvents
+ {
+ private static User32.WinEventProc _eventProc;
+ private static IntPtr _eventHandle;
+ private static IVBE _vbe;
+
+ public struct WindowInfo
+ {
+ private readonly IntPtr _handle;
+ private readonly IWindow _window;
+ private readonly IWindowEventProvider _subclass;
+
+ public IntPtr Hwnd { get { return _handle; } }
+ public IWindow Window { get { return _window; } }
+ internal IWindowEventProvider Subclass { get { return _subclass; } }
+
+ internal WindowInfo(IntPtr handle, IWindow window, IWindowEventProvider source)
+ {
+ _handle = handle;
+ _window = window;
+ _subclass = source;
+ }
+ }
+
+ //This *could* be a ConcurrentDictionary, but there other operations that need the lock around it anyway.
+ private static readonly Dictionary TrackedWindows = new Dictionary();
+ private static readonly object ThreadLock = new object();
+
+ private static uint _threadId;
+
+ public static void HookEvents(IVBE vbe)
+ {
+ _vbe = vbe;
+ if (_eventHandle == IntPtr.Zero)
+ {
+ _eventProc = VbeEventCallback;
+ _threadId = User32.GetWindowThreadProcessId(new IntPtr(_vbe.MainWindow.HWnd), IntPtr.Zero);
+ _eventHandle = User32.SetWinEventHook((uint)WinEvent.Min, (uint)WinEvent.Max, IntPtr.Zero, _eventProc, 0, _threadId, WinEventFlags.OutOfContext);
+ }
+ }
+
+ public static void UnhookEvents()
+ {
+ lock (ThreadLock)
+ {
+ User32.UnhookWinEvent(_eventHandle);
+ foreach (var info in TrackedWindows.Values)
+ {
+ info.Subclass.FocusChange -= FocusDispatcher;
+ info.Subclass.Dispose();
+ }
+ }
+ }
+
+ public static void VbeEventCallback(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild,
+ uint dwEventThread, uint dwmsEventTime)
+ {
+ if (hwnd != IntPtr.Zero && idObject == (int)ObjId.Caret && eventType == (uint)WinEvent.ObjectLocationChange && hwnd.ToWindowType() == WindowType.VbaWindow)
+ {
+ OnSelectionChanged(hwnd);
+ }
+ else if (idObject == (int)ObjId.Window &&
+ (eventType == (uint)WinEvent.ObjectCreate || eventType == (uint)WinEvent.ObjectDestroy) &&
+ hwnd.ToWindowType() != WindowType.Indeterminate)
+ {
+ if (eventType == (uint) WinEvent.ObjectCreate)
+ {
+ AttachWindow(hwnd);
+ }
+ else if (eventType == (uint)WinEvent.ObjectDestroy)
+ {
+ DetachWindow(hwnd);
+ }
+ }
+ }
+
+ private static void AttachWindow(IntPtr hwnd)
+ {
+ lock (ThreadLock)
+ {
+ Debug.Assert(!TrackedWindows.ContainsKey(hwnd));
+ var window = GetWindowFromHwnd(hwnd);
+ if (window == null) return;
+ var source = window.Type == WindowKind.CodeWindow
+ ? new CodePaneSubclass(hwnd, GetCodePaneFromHwnd(hwnd)) as IWindowEventProvider
+ : new DesignerWindowSubclass(hwnd);
+ var info = new WindowInfo(hwnd, window, source);
+ source.FocusChange += FocusDispatcher;
+ TrackedWindows.Add(hwnd, info);
+ }
+ }
+
+ private static void DetachWindow(IntPtr hwnd)
+ {
+ lock (ThreadLock)
+ {
+ Debug.Assert(TrackedWindows.ContainsKey(hwnd));
+ var info = TrackedWindows[hwnd];
+ info.Subclass.FocusChange -= FocusDispatcher;
+ info.Subclass.Dispose();
+ TrackedWindows.Remove(hwnd);
+ }
+ }
+
+ private static void FocusDispatcher(object sender, WindowChangedEventArgs eventArgs)
+ {
+ OnWindowFocusChange(sender, eventArgs);
+ }
+
+ public static WindowInfo? GetWindowInfoFromHwnd(IntPtr hwnd)
+ {
+ lock (ThreadLock)
+ {
+ if (!TrackedWindows.ContainsKey(hwnd))
+ {
+ return null;
+ }
+ return TrackedWindows[hwnd];
+ }
+ }
+
+ public static event EventHandler SelectionChanged;
+ private static void OnSelectionChanged(IntPtr hwnd)
+ {
+ if (SelectionChanged != null)
+ {
+ var pane = GetCodePaneFromHwnd(hwnd);
+ SelectionChanged.Invoke(_vbe, new SelectionChangedEventArgs(pane));
+ }
+ }
+
+ public static event EventHandler WindowFocusChange;
+ private static void OnWindowFocusChange(object sender, WindowChangedEventArgs eventArgs)
+ {
+ if (WindowFocusChange != null)
+ {
+ WindowFocusChange.Invoke(sender, eventArgs);
+ }
+ }
+
+ private static ICodePane GetCodePaneFromHwnd(IntPtr hwnd)
+ {
+ var caption = hwnd.GetWindowText();
+ return _vbe.CodePanes.FirstOrDefault(x => x.Window.Caption.Equals(caption));
+ }
+
+ private static IWindow GetWindowFromHwnd(IntPtr hwnd)
+ {
+ var caption = hwnd.GetWindowText();
+ return _vbe.Windows.FirstOrDefault(x => x.Caption.Equals(caption));
+ }
+
+ ///
+ /// A helper function that returns true when the specified handle is that of the foreground window.
+ ///
+ /// True if the active thread is on the VBE's thread.
+ public static bool IsVbeWindowActive()
+ {
+ uint hThread;
+ User32.GetWindowThreadProcessId(User32.GetForegroundWindow(), out hThread);
+ return (IntPtr)hThread == (IntPtr)_threadId;
+ }
+
+ public enum WindowType
+ {
+ Indeterminate,
+ VbaWindow,
+ DesignerWindow
+ }
+
+ public static WindowType ToWindowType(this IntPtr hwnd)
+ {
+ var name = new StringBuilder(128);
+ User32.GetClassName(hwnd, name, name.Capacity);
+ WindowType id;
+ return Enum.TryParse(name.ToString(), out id) ? id : WindowType.Indeterminate;
+ }
+ }
+}
diff --git a/Rubberduck.VBEEditor/Events/WindowChangedEventArgs.cs b/Rubberduck.VBEEditor/Events/WindowChangedEventArgs.cs
new file mode 100644
index 0000000000..0d0bf99611
--- /dev/null
+++ b/Rubberduck.VBEEditor/Events/WindowChangedEventArgs.cs
@@ -0,0 +1,27 @@
+using System;
+using Rubberduck.VBEditor.SafeComWrappers.Abstract;
+
+namespace Rubberduck.VBEditor.Events
+{
+ public class WindowChangedEventArgs : EventArgs
+ {
+ public enum FocusType
+ {
+ GotFocus,
+ LostFocus
+ }
+
+ public IntPtr Hwnd { get; private set; }
+ public IWindow Window { get; private set; }
+ public ICodePane CodePane { get; private set; }
+ public FocusType EventType { get; private set; }
+
+ public WindowChangedEventArgs(IntPtr hwnd, IWindow window, ICodePane pane, FocusType type)
+ {
+ Hwnd = hwnd;
+ Window = window;
+ CodePane = pane;
+ EventType = type;
+ }
+ }
+}
diff --git a/Rubberduck.VBEEditor/Rubberduck.VBEditor.csproj b/Rubberduck.VBEEditor/Rubberduck.VBEditor.csproj
index 0f086f1ba7..bb6b162fe0 100644
--- a/Rubberduck.VBEEditor/Rubberduck.VBEditor.csproj
+++ b/Rubberduck.VBEEditor/Rubberduck.VBEditor.csproj
@@ -124,6 +124,9 @@
+
+
+
@@ -223,7 +226,12 @@
-
+
+
+
+
+
+
@@ -253,6 +261,11 @@
+
+
+
+
+
diff --git a/Rubberduck.VBEEditor/SafeComWrappers/VB6/CodePane.cs b/Rubberduck.VBEEditor/SafeComWrappers/VB6/CodePane.cs
index 4851c72bb5..9d962a9648 100644
--- a/Rubberduck.VBEEditor/SafeComWrappers/VB6/CodePane.cs
+++ b/Rubberduck.VBEEditor/SafeComWrappers/VB6/CodePane.cs
@@ -1,5 +1,6 @@
using System;
using Rubberduck.VBEditor.SafeComWrappers.Abstract;
+using Rubberduck.VBEditor.WindowsApi;
using VB = Microsoft.VB6.Interop.VBIDE;
namespace Rubberduck.VBEditor.SafeComWrappers.VB6
@@ -101,7 +102,7 @@ private void ForceFocus()
var window = VBE.MainWindow;
var mainWindowHandle = window.Handle();
var caption = Window.Caption;
- var childWindowFinder = new NativeMethods.ChildWindowFinder(caption);
+ var childWindowFinder = new ChildWindowFinder(caption);
NativeMethods.EnumChildWindows(mainWindowHandle, childWindowFinder.EnumWindowsProcToChildWindowByCaption);
var handle = childWindowFinder.ResultHandle;
diff --git a/Rubberduck.VBEEditor/SafeComWrappers/VBA/CodePane.cs b/Rubberduck.VBEEditor/SafeComWrappers/VBA/CodePane.cs
index ec9ab7c663..4fd8b87dd5 100644
--- a/Rubberduck.VBEEditor/SafeComWrappers/VBA/CodePane.cs
+++ b/Rubberduck.VBEEditor/SafeComWrappers/VBA/CodePane.cs
@@ -1,5 +1,6 @@
using System;
using Rubberduck.VBEditor.SafeComWrappers.Abstract;
+using Rubberduck.VBEditor.WindowsApi;
using VB = Microsoft.Vbe.Interop;
namespace Rubberduck.VBEditor.SafeComWrappers.VBA
@@ -105,7 +106,7 @@ private void ForceFocus()
var window = VBE.MainWindow;
var mainWindowHandle = window.Handle();
var caption = Window.Caption;
- var childWindowFinder = new NativeMethods.ChildWindowFinder(caption);
+ var childWindowFinder = new ChildWindowFinder(caption);
NativeMethods.EnumChildWindows(mainWindowHandle, childWindowFinder.EnumWindowsProcToChildWindowByCaption);
var handle = childWindowFinder.ResultHandle;
diff --git a/Rubberduck.VBEEditor/WindowsApi/ChildWindowFinder.cs b/Rubberduck.VBEEditor/WindowsApi/ChildWindowFinder.cs
new file mode 100644
index 0000000000..3d55a01d13
--- /dev/null
+++ b/Rubberduck.VBEEditor/WindowsApi/ChildWindowFinder.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Text;
+
+namespace Rubberduck.VBEditor.WindowsApi
+{
+ internal class ChildWindowFinder
+ {
+ private IntPtr _resultHandle = IntPtr.Zero;
+ private readonly string _caption;
+
+ internal ChildWindowFinder(string caption)
+ {
+ _caption = caption;
+ }
+
+ public int EnumWindowsProcToChildWindowByCaption(IntPtr windowHandle, IntPtr param)
+ {
+ // By default it will continue enumeration after this call
+ var result = 1;
+ var caption = windowHandle.GetWindowText();
+
+ if (_caption == caption)
+ {
+ // Found
+ _resultHandle = windowHandle;
+
+ // Stop enumeration after this call
+ result = 0;
+ }
+ return result;
+ }
+
+ public IntPtr ResultHandle
+ {
+ get
+ {
+ return _resultHandle;
+ }
+ }
+
+
+
+ }
+}
diff --git a/Rubberduck.VBEEditor/WindowsApi/CodePaneSubclass.cs b/Rubberduck.VBEEditor/WindowsApi/CodePaneSubclass.cs
new file mode 100644
index 0000000000..5eee5941bf
--- /dev/null
+++ b/Rubberduck.VBEEditor/WindowsApi/CodePaneSubclass.cs
@@ -0,0 +1,29 @@
+using System;
+using Rubberduck.VBEditor.Events;
+using Rubberduck.VBEditor.SafeComWrappers.Abstract;
+
+namespace Rubberduck.VBEditor.WindowsApi
+{
+ //Stub for code pane replacement. :-)
+ internal class CodePaneSubclass : FocusSource
+ {
+ private readonly ICodePane _pane;
+
+ public ICodePane CodePane { get { return _pane; } }
+
+ internal CodePaneSubclass(IntPtr hwnd, ICodePane pane) : base(hwnd)
+ {
+ _pane = pane;
+ }
+
+ protected override void DispatchFocusEvent(WindowChangedEventArgs.FocusType type)
+ {
+ var window = VBEEvents.GetWindowInfoFromHwnd(Hwnd);
+ if (window == null)
+ {
+ return;
+ }
+ OnFocusChange(new WindowChangedEventArgs(window.Value.Hwnd, window.Value.Window, _pane, type));
+ }
+ }
+}
diff --git a/Rubberduck.VBEEditor/WindowsApi/DesignerWindowSubclass.cs b/Rubberduck.VBEEditor/WindowsApi/DesignerWindowSubclass.cs
new file mode 100644
index 0000000000..9edf56ec2b
--- /dev/null
+++ b/Rubberduck.VBEEditor/WindowsApi/DesignerWindowSubclass.cs
@@ -0,0 +1,10 @@
+using System;
+
+namespace Rubberduck.VBEditor.WindowsApi
+{
+ internal class DesignerWindowSubclass : FocusSource
+ {
+ //Stub for designer window replacement. :-)
+ internal DesignerWindowSubclass(IntPtr hwnd) : base(hwnd) { }
+ }
+}
diff --git a/Rubberduck.VBEEditor/WindowsApi/FocusSource.cs b/Rubberduck.VBEEditor/WindowsApi/FocusSource.cs
new file mode 100644
index 0000000000..9c6539a543
--- /dev/null
+++ b/Rubberduck.VBEEditor/WindowsApi/FocusSource.cs
@@ -0,0 +1,45 @@
+using System;
+using Rubberduck.Common.WinAPI;
+using Rubberduck.VBEditor.Events;
+
+namespace Rubberduck.VBEditor.WindowsApi
+{
+ internal abstract class FocusSource : SubclassingWindow, IWindowEventProvider
+ {
+ protected FocusSource(IntPtr hwnd) : base(hwnd, hwnd) { }
+
+ public event EventHandler FocusChange;
+ protected void OnFocusChange(WindowChangedEventArgs eventArgs)
+ {
+ if (FocusChange != null)
+ {
+ FocusChange.Invoke(this, eventArgs);
+ }
+ }
+
+ protected virtual void DispatchFocusEvent(WindowChangedEventArgs.FocusType type)
+ {
+ var window = VBEEvents.GetWindowInfoFromHwnd(Hwnd);
+ if (window == null)
+ {
+ return;
+ }
+ OnFocusChange(new WindowChangedEventArgs(Hwnd, window.Value.Window, null, type));
+ }
+
+ public override int SubClassProc(IntPtr hWnd, IntPtr msg, IntPtr wParam, IntPtr lParam, IntPtr uIdSubclass, IntPtr dwRefData)
+ {
+ switch ((uint)msg)
+ {
+ case (uint)WM.SETFOCUS:
+
+ DispatchFocusEvent(WindowChangedEventArgs.FocusType.GotFocus);
+ break;
+ case (uint)WM.KILLFOCUS:
+ DispatchFocusEvent(WindowChangedEventArgs.FocusType.LostFocus);
+ break;
+ }
+ return base.SubClassProc(hWnd, msg, wParam, lParam, uIdSubclass, dwRefData);
+ }
+ }
+}
diff --git a/Rubberduck.VBEEditor/WindowsApi/IWindowEventProvider.cs b/Rubberduck.VBEEditor/WindowsApi/IWindowEventProvider.cs
new file mode 100644
index 0000000000..4f12cb7764
--- /dev/null
+++ b/Rubberduck.VBEEditor/WindowsApi/IWindowEventProvider.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Rubberduck.VBEditor.Events;
+
+namespace Rubberduck.VBEditor.WindowsApi
+{
+ public interface IWindowEventProvider : IDisposable
+ {
+ event EventHandler FocusChange;
+ }
+}
diff --git a/Rubberduck.VBEEditor/NativeMethods.cs b/Rubberduck.VBEEditor/WindowsApi/NativeMethods.cs
similarity index 79%
rename from Rubberduck.VBEEditor/NativeMethods.cs
rename to Rubberduck.VBEEditor/WindowsApi/NativeMethods.cs
index 1bf10c7635..43f7dd7846 100644
--- a/Rubberduck.VBEEditor/NativeMethods.cs
+++ b/Rubberduck.VBEEditor/WindowsApi/NativeMethods.cs
@@ -3,7 +3,7 @@
using System.Runtime.InteropServices;
using System.Text;
-namespace Rubberduck.VBEditor
+namespace Rubberduck.VBEditor.WindowsApi
{
///
/// Collection of WinAPI methods and extensions to handle native windows.
@@ -48,18 +48,12 @@ public static class NativeMethods
[DllImport("user32", EntryPoint = "GetWindowTextW", ExactSpelling = true, CharSet = CharSet.Unicode)]
internal static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
- /// Gets the parent window of this item.
- ///
- /// The window handle.
- /// The parent window IntPtr handle.
- [DllImport("User32.dll")]
- internal static extern IntPtr GetParent(IntPtr hWnd);
/// Gets window caption text by handle.
///
/// Handle of the window to be activated.
/// The window caption text.
- internal static string GetWindowTextByHwnd(IntPtr windowHandle)
+ public static string GetWindowText(this IntPtr windowHandle)
{
const int MAX_BUFFER = 300;
@@ -75,6 +69,15 @@ internal static string GetWindowTextByHwnd(IntPtr windowHandle)
return result;
}
+ /// Gets the parent window of this item.
+ ///
+ /// The window handle.
+ /// The parent window IntPtr handle.
+ [DllImport("User32.dll")]
+ internal static extern IntPtr GetParent(IntPtr hWnd);
+
+
+
/// Activates the window by simulating a click.
///
/// Handle of the window to be activated.
@@ -96,42 +99,5 @@ internal static void EnumChildWindows(IntPtr parentWindowHandle, EnumChildWindow
Debug.WriteLine("EnumChildWindows failed");
}
}
-
- internal class ChildWindowFinder
- {
- private IntPtr _resultHandle = IntPtr.Zero;
- private readonly string _caption;
-
- internal ChildWindowFinder(string caption)
- {
- _caption = caption;
- }
-
- public int EnumWindowsProcToChildWindowByCaption(IntPtr windowHandle, IntPtr param)
- {
- // By default it will continue enumeration after this call
- var result = 1;
- var caption = GetWindowTextByHwnd(windowHandle);
-
-
- if (_caption == caption)
- {
- // Found
- _resultHandle = windowHandle;
-
- // Stop enumeration after this call
- result = 0;
- }
- return result;
- }
-
- public IntPtr ResultHandle
- {
- get
- {
- return _resultHandle;
- }
- }
- }
}
}
diff --git a/RetailCoder.VBE/UI/SubclassingWindow.cs b/Rubberduck.VBEEditor/WindowsApi/SubclassingWindow.cs
similarity index 82%
rename from RetailCoder.VBE/UI/SubclassingWindow.cs
rename to Rubberduck.VBEEditor/WindowsApi/SubclassingWindow.cs
index c9ab5dc12c..e1072b2e29 100644
--- a/RetailCoder.VBE/UI/SubclassingWindow.cs
+++ b/Rubberduck.VBEEditor/WindowsApi/SubclassingWindow.cs
@@ -4,7 +4,7 @@
using System.Runtime.InteropServices;
using Rubberduck.Common.WinAPI;
-namespace Rubberduck.UI
+namespace Rubberduck.VBEditor.WindowsApi
{
public abstract class SubclassingWindow : IDisposable
{
@@ -13,8 +13,7 @@ public abstract class SubclassingWindow : IDisposable
private readonly SubClassCallback _wndProc;
private bool _listening;
- private static readonly ConcurrentBag RubberduckProcs = new ConcurrentBag();
- private static readonly object SubclassLock = new object();
+ private readonly object _subclassLock = new object();
public delegate int SubClassCallback(IntPtr hWnd, IntPtr msg, IntPtr wParam, IntPtr lParam, IntPtr uIdSubclass, IntPtr dwRefData);
@@ -31,7 +30,7 @@ public abstract class SubclassingWindow : IDisposable
[DllImport("ComCtl32.dll", CharSet = CharSet.Auto)]
private static extern int DefSubclassProc(IntPtr hWnd, IntPtr msg, IntPtr wParam, IntPtr lParam);
- public IntPtr Hwnd { get; set; }
+ public IntPtr Hwnd { get { return _hwnd; } }
protected SubclassingWindow(IntPtr subclassId, IntPtr hWnd)
{
@@ -40,10 +39,6 @@ protected SubclassingWindow(IntPtr subclassId, IntPtr hWnd)
_wndProc = SubClassProc;
AssignHandle();
}
- ~SubclassingWindow()
- {
- Debug.Assert(false, "Dispose() not called.");
- }
public void Dispose()
{
@@ -53,9 +48,8 @@ public void Dispose()
private void AssignHandle()
{
- lock (SubclassLock)
+ lock (_subclassLock)
{
- RubberduckProcs.Add(_wndProc);
var result = SetWindowSubclass(_hwnd, _wndProc, _subclassId, IntPtr.Zero);
if (result != 1)
{
@@ -67,13 +61,13 @@ private void AssignHandle()
private void ReleaseHandle()
{
- if (!_listening)
+ lock (_subclassLock)
{
- return;
- }
+ if (!_listening)
+ {
+ return;
+ }
- lock (SubclassLock)
- {
var result = RemoveWindowSubclass(_hwnd, _wndProc, _subclassId);
if (result != 1)
{
@@ -91,8 +85,9 @@ public virtual int SubClassProc(IntPtr hWnd, IntPtr msg, IntPtr wParam, IntPtr l
}
Debug.Assert(IsWindow(_hwnd));
+ //TODO: This should change to WM.DESTROY once subclassing\hooking consolidation is complete.
if ((uint)msg == (uint)WM.RUBBERDUCK_SINKING)
- {
+ {
ReleaseHandle();
}
return DefSubclassProc(hWnd, msg, wParam, lParam);
diff --git a/Rubberduck.VBEEditor/WindowsApi/User32.cs b/Rubberduck.VBEEditor/WindowsApi/User32.cs
new file mode 100644
index 0000000000..dc6a2f1170
--- /dev/null
+++ b/Rubberduck.VBEEditor/WindowsApi/User32.cs
@@ -0,0 +1,70 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace Rubberduck.VBEditor.WindowsApi
+{
+ public static class User32
+ {
+ #region WinEvents
+
+ //https://msdn.microsoft.com/en-us/library/windows/desktop/dd373885(v=vs.85).aspx
+ public delegate void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
+
+ //https://msdn.microsoft.com/en-us/library/windows/desktop/dd373640(v=vs.85).aspx
+ [DllImport("user32.dll")]
+ public static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventProc lpfnWinEventProc, uint idProcess, uint idThread, WinEventFlags dwFlags);
+
+ ///
+ /// Removes event hooks set with SetWinEventHook.
+ /// https://msdn.microsoft.com/en-us/library/windows/desktop/dd373671(v=vs.85).aspx
+ ///
+ /// The hook handle to unregister.
+ ///
+ [DllImport("user32.dll")]
+ public static extern bool UnhookWinEvent(IntPtr hWinEventHook);
+
+ #endregion
+
+ ///
+ /// Returns the thread ID for thread that created the passed hWnd.
+ /// https://msdn.microsoft.com/en-us/library/windows/desktop/ms633522(v=vs.85).aspx
+ ///
+ /// The window handle to get the thread ID for.
+ /// This is actually an out parameter in the API, but we don't care about it. Should always be IntPtr.Zero.
+ /// Unmanaged thread ID
+ [DllImport("user32.dll")]
+ public static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr processId);
+
+ ///
+ /// Retrieves the identifier of the thread that created the specified window and, optionally,
+ /// the identifier of the process that created the window.
+ ///
+ /// A handle to the window.
+ /// A pointer to a variable that receives the process identifier.
+ /// If this parameter is not NULL, GetWindowThreadProcessId copies the identifier of the process to the variable; otherwise, it does not.
+ /// The return value is the identifier of the thread that created the window.
+ [DllImport("user32.dll", SetLastError = true)]
+ public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
+
+ ///
+ /// Retrieves a handle to the foreground window (the window with which the user is currently working).
+ /// The system assigns a slightly higher priority to the thread that creates the foreground window than it does to other threads.
+ ///
+ /// The return value is a handle to the foreground window.
+ /// The foreground window can be NULL in certain circumstances, such as when a window is losing activation.
+ [DllImport("user32.dll")]
+ public static extern IntPtr GetForegroundWindow();
+
+ ///
+ /// Gets the underlying class name for a window handle.
+ /// https://msdn.microsoft.com/en-us/library/windows/desktop/ms633582(v=vs.85).aspx
+ ///
+ /// The handle to retrieve the name for.
+ /// Buffer for returning the class name.
+ /// Buffer size in characters, including the null terminator.
+ /// The length of the returned class name (without the null terminator), zero on error.
+ [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
+ public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
+ }
+}
diff --git a/RetailCoder.VBE/Common/WinAPI/WM.cs b/Rubberduck.VBEEditor/WindowsApi/WM.cs
similarity index 100%
rename from RetailCoder.VBE/Common/WinAPI/WM.cs
rename to Rubberduck.VBEEditor/WindowsApi/WM.cs
diff --git a/Rubberduck.VBEEditor/WindowsApi/WinEvent.cs b/Rubberduck.VBEEditor/WindowsApi/WinEvent.cs
new file mode 100644
index 0000000000..d67c584de8
--- /dev/null
+++ b/Rubberduck.VBEEditor/WindowsApi/WinEvent.cs
@@ -0,0 +1,87 @@
+namespace Rubberduck.VBEditor.WindowsApi
+{
+ public enum WinEvent
+ {
+ Min = 0x0001,
+ SystemSound = 0x0001,
+ SystemAlert = 0x0002,
+ SystemForeground = 0x0003,
+ SystemMenuStart = 0x0004,
+ SystemMenuEnd = 0x0005,
+ SystemMenuPopupStart = 0x0006,
+ SystemMenuPopupEnd = 0x0007,
+ SystemCaptureStart = 0x0008,
+ SystemCaptureEnd = 0x0009,
+ SystemMoveSizeStart = 0x000A,
+ SystemMoveSizeEnd = 0x000B,
+ SystemContextHelpStart = 0x000C,
+ SystemContextHelpEnd = 0x000D,
+ SystemDragDropStart = 0x000E,
+ SystemDragDropEnd = 0x000F,
+ SystemDialogStart = 0x0010,
+ SystemDialogEnd = 0x0011,
+ SystemScrollingStart = 0x0012,
+ SystemScrollingEnd = 0x0013,
+ SystemSwitchStart = 0x0014,
+ SystemSwitchEnd = 0x0015,
+ SystemMinimizeStart = 0x0016,
+ SystemMinimizeEnd = 0x0017,
+ SystemDesktopSwitch = 0x0020,
+ SystemEnd = 0x00FF,
+ ObjectCreate = 0x8000,
+ ObjectDestroy = 0x8001,
+ ObjectShow = 0x8002,
+ ObjectHide = 0x8003,
+ ObjectReorder = 0x8004,
+ ObjectFocus = 0x8005,
+ ObjectSelection = 0x8006,
+ ObjectSelectionAdd = 0x8007,
+ ObjectSelectionRemove = 0x8008,
+ ObjectSelectionWithin = 0x8009,
+ ObjectStateChange = 0x800A,
+ ObjectLocationChange = 0x800B,
+ ObjectNameChange = 0x800C,
+ ObjectDescriptionChange = 0x800D,
+ ObjectValueChange = 0x800E,
+ ObjectParentChange = 0x800F,
+ ObjectHelpChange = 0x8010,
+ ObjectDefactionChange = 0x8011,
+ ObjectInvoked = 0x8013,
+ ObjectTextSelectionChanged = 0x8014,
+ SystemArrangmentPreview = 0x8016,
+ ObjectLiveRegionChanged = 0x8019,
+ ObjectHostedObjectsInvalidated = 0x8020,
+ ObjectDragStart = 0x8021,
+ ObjectDragCancel = 0x8022,
+ ObjectDragComplete = 0x8023,
+ ObjectDragEnter = 0x8024,
+ ObjectDragLeave = 0x8025,
+ ObjectDragDropped = 0x8026,
+ ObjectImeShow = 0x8027,
+ ObjectImeHide = 0x8028,
+ ObjectImeChange = 0x8029,
+ ObjectTexteditConversionTargetChanged = 0x8030,
+ ObjectEnd = 0x80FF,
+ AiaStart = 0xA000,
+ AiaEnd = 0xAFFF,
+ Max = 0x7FFFFFFF
+ }
+
+ public enum ObjId
+ {
+ Window = 0,
+ SysMenu = -1,
+ TitleBar = -2,
+ Menu = -3,
+ Client = -4,
+ VScroll = -5,
+ HScroll = -6,
+ SizeGrip = -7,
+ Caret = -8,
+ Cursor = -9,
+ Alert = -10,
+ Sount = -11,
+ QueryClasNameIdx = -12,
+ NativeOM = -16
+ }
+}
diff --git a/Rubberduck.VBEEditor/WindowsApi/WinEventFlags.cs b/Rubberduck.VBEEditor/WindowsApi/WinEventFlags.cs
new file mode 100644
index 0000000000..7f5dffc2f7
--- /dev/null
+++ b/Rubberduck.VBEEditor/WindowsApi/WinEventFlags.cs
@@ -0,0 +1,17 @@
+using System;
+
+namespace Rubberduck.VBEditor.WindowsApi
+{
+ [Flags]
+ public enum WinEventFlags
+ {
+ //Asynchronous events.
+ OutOfContext = 0x0000,
+ //No events raised from caller thread. Must be combined with OutOfContext or InContext.
+ SkipOwnThread = 0x0001,
+ //No events raised from caller process. Must be combined with OutOfContext or InContext.
+ SkipOwnProcess = 0x0002,
+ //Synchronous events - injects into *all* processes.
+ InContext = 0x0004
+ }
+}
diff --git a/RubberduckTests/Inspections/AssignedByValParameterInspectionTests.cs b/RubberduckTests/Inspections/AssignedByValParameterInspectionTests.cs
index 75a2d680ad..b1019a32e6 100644
--- a/RubberduckTests/Inspections/AssignedByValParameterInspectionTests.cs
+++ b/RubberduckTests/Inspections/AssignedByValParameterInspectionTests.cs
@@ -97,19 +97,152 @@ Dim var1 As Integer
[TestCategory("Inspections")]
public void AssignedByValParameter_QuickFixWorks()
{
- const string inputCode =
-@"Public Sub Foo(ByVal barByVal As String)
+
+ string inputCode =
+@"Public Sub Foo(Optional ByVal barByVal As String = ""XYZ"")
Let barByVal = ""test""
End Sub";
- const string expectedCode =
-@"Public Sub Foo(ByRef barByVal As String)
+ string expectedCode =
+@"Public Sub Foo(Optional ByRef barByVal As String = ""XYZ"")
Let barByVal = ""test""
End Sub";
var quickFixResult = ApplyPassParameterByReferenceQuickFixToVBAFragment(inputCode);
Assert.AreEqual(expectedCode, quickFixResult);
+
+ //check when ByVal argument is one of several parameters
+ inputCode =
+@"Public Sub Foo(ByRef firstArg As Long, Optional ByVal barByVal As String = """", secondArg as Double)
+ Let barByVal = ""test""
+End Sub";
+ expectedCode =
+@"Public Sub Foo(ByRef firstArg As Long, Optional ByRef barByVal As String = """", secondArg as Double)
+ Let barByVal = ""test""
+End Sub";
+
+ quickFixResult = ApplyPassParameterByReferenceQuickFixToVBAFragment(inputCode);
+ Assert.AreEqual(expectedCode, quickFixResult);
+
+ inputCode =
+@"
+Private Sub Foo(Optional ByVal _
+ bar _
+ As _
+ Long = 4, _
+ ByVal _
+ barTwo _
+ As _
+ Long)
+bar = 42
+End Sub
+"
+;
+ expectedCode =
+@"
+Private Sub Foo(Optional ByRef _
+ bar _
+ As _
+ Long = 4, _
+ ByVal _
+ barTwo _
+ As _
+ Long)
+bar = 42
+End Sub
+"
+;
+ quickFixResult = ApplyPassParameterByReferenceQuickFixToVBAFragment(inputCode);
+ Assert.AreEqual(expectedCode, quickFixResult);
+
+ inputCode =
+@"Private Sub Foo(ByVal barByVal As Long, ByVal _xByValbar As Long, ByVal _
+ barTwo _
+ As _
+ Long)
+barTwo = 42
+End Sub
+";
+ expectedCode =
+@"Private Sub Foo(ByVal barByVal As Long, ByVal _xByValbar As Long, ByRef _
+ barTwo _
+ As _
+ Long)
+barTwo = 42
+End Sub
+";
+
+ quickFixResult = ApplyPassParameterByReferenceQuickFixToVBAFragment(inputCode);
+ Assert.AreEqual(expectedCode, quickFixResult);
+
+ inputCode =
+@"Private Sub Foo(ByVal barByVal As Long, ByVal barTwoon As Long, ByVal _
+ barTwo _
+ As _
+ Long)
+barTwo = 42
+End Sub
+";
+ expectedCode =
+@"Private Sub Foo(ByVal barByVal As Long, ByVal barTwoon As Long, ByRef _
+ barTwo _
+ As _
+ Long)
+barTwo = 42
+End Sub
+";
+
+ quickFixResult = ApplyPassParameterByReferenceQuickFixToVBAFragment(inputCode);
+ Assert.AreEqual(expectedCode, quickFixResult);
+
+ inputCode =
+@"Private Sub Foo(ByVal barByVal As Long, ByVal barTwoon As Long, ByVal barTwo _
+ As _
+ Long)
+barTwo = 42
+End Sub
+";
+ expectedCode =
+@"Private Sub Foo(ByVal barByVal As Long, ByVal barTwoon As Long, ByRef barTwo _
+ As _
+ Long)
+barTwo = 42
+End Sub
+";
+
+ quickFixResult = ApplyPassParameterByReferenceQuickFixToVBAFragment(inputCode);
+ Assert.AreEqual(expectedCode, quickFixResult);
+
+ inputCode =
+@"Sub DoSomething(_
+ ByVal foo As Long, _
+ ByRef _
+ bar, _
+ ByRef barbecue _
+ )
+ foo = 4
+ bar = barbecue * _
+ bar + foo / barbecue
+End Sub
+";
+
+ expectedCode =
+@"Sub DoSomething(_
+ ByRef foo As Long, _
+ ByRef _
+ bar, _
+ ByRef barbecue _
+ )
+ foo = 4
+ bar = barbecue * _
+ bar + foo / barbecue
+End Sub
+";
+ quickFixResult = ApplyPassParameterByReferenceQuickFixToVBAFragment(inputCode);
+ Assert.AreEqual(expectedCode, quickFixResult);
+
}
+
[TestMethod]
[TestCategory("Inspections")]
public void AssignedByValParameter_IgnoreQuickFixWorks()
@@ -507,9 +640,6 @@ private Mock BuildMockVBEStandardModuleForVBAFragment(string inputCode)
{
var builder = new MockVbeBuilder();
IVBComponent component;
- //TODO: removal of the two lines below have no effect on the outcome of any test...remove?
- //var mockHost = new Mock();
- //mockHost.SetupAllProperties();
return builder.BuildFromSingleStandardModule(inputCode, out component);
}
diff --git a/RubberduckTests/SmartIndenter/MiscAndCornerCaseTests.cs b/RubberduckTests/SmartIndenter/MiscAndCornerCaseTests.cs
index 347e023475..fb54cc6459 100644
--- a/RubberduckTests/SmartIndenter/MiscAndCornerCaseTests.cs
+++ b/RubberduckTests/SmartIndenter/MiscAndCornerCaseTests.cs
@@ -746,6 +746,31 @@ public void BracketedIdentifiersWork()
Assert.IsTrue(expected.SequenceEqual(actual));
}
+ //https://github.com/rubberduck-vba/Rubberduck/issues/2696
+ [TestMethod]
+ // Broken in VB6 SmartIndenter.
+ [TestCategory("Indenter")]
+ public void BracketsInEndOfLineCommentsWork()
+ {
+ var code = new[]
+ {
+ "Public Sub Test()",
+ "Debug.Print \"foo\" \'update [foo].[bar] in the frob.",
+ "End Sub"
+ };
+
+ var expected = new[]
+ {
+ "Public Sub Test()",
+ " Debug.Print \"foo\" 'update [foo].[bar] in the frob.",
+ "End Sub"
+ };
+
+ var indenter = new Indenter(null, () => IndenterSettingsTests.GetMockIndenterSettings());
+ var actual = indenter.Indent(code);
+ Assert.IsTrue(expected.SequenceEqual(actual));
+ }
+
//https://github.com/rubberduck-vba/Rubberduck/issues/2604
[TestMethod]
[TestCategory("Indenter")]