diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index ce308d0dfa..86dc22dfd3 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -61,15 +61,15 @@ Our Ducky has come quite the long way and is intimidatingly large for a first-ti
To make it easier to find something you can do, all issues with that label are additionally labelled with a difficulty.
That difficulty level is a best-guess by the dev-team how complex or evil an issue will be.
-If you're new to C#, we recommend you start with \[difficulty-01-duckling\]. The next step \[difficulty-02-ducky\] requires some knowledge about C#, but not so much about how Rubberduck works
+If you're new to C#, we recommend you start with \[difficulty-01-duckling\]. The next step \[difficulty-02-ducky\] requires some knowledge about C#, but not so much about how Rubberduck works - 02/ducky issues usually make a good introduction to using Rubberduck's internal API.
-Then there's \[difficulty-03-duck\] which requires a good handle on C# and at least an idea of how the components of Rubberduck play toghether.
+Then there's \[difficulty-03-duck\] which requires a good handle on C# and how the components of Rubberduck play toghether.
And finally there's \[difficulty-04-quackhead\] for when you really want to bang your head against the wall.
-They require both good knowledge of C# and a deep understanding of how Rubberduck works.
+They require both good knowledge of C# and a deep understanding of how not only Rubberduck, but also COM and well, the VBE, works.
In addition to this, a few people have put together some helpful resources about how Rubberduck works internally and help contributors tell left from right in the ~250k Lines of Code.
-You can find their work in the [github wiki](https://github.com/rubberduck-vba/Rubberduck/wiki). If something in there doesn't quite seem to match, it's probably because nobody got around to updating it yet. You're very welcome to **improve** the wiki by adding and correcting information. (see right above)
+You can find their work in the [GitHub wiki](https://github.com/rubberduck-vba/Rubberduck/wiki). If something in there doesn't quite seem to match, it's probably because nobody got around to updating it yet. You're very welcome to **improve** the wiki by adding and correcting information. (see right above)
In case this doesn't quite help you or the information you need hasn't been added to the wiki, you can **always** ask questions.
Please do so in the ["War room"](https://chat.stackexchange.com/rooms/14929).
@@ -79,21 +79,21 @@ Other than that, you can also open an issue with the label \[support\], but it m
In that room the core team talks about the duck and whatever else comes up.
N.B.: The rules of the Stack Exchange Network apply to everything you say in that room. But basically those are the same rules as those in the [CoC](#Code_of_Conduct).
-Whether you create an issue or a pull request, please avoid using `[` square brackets `]` in the title, and try to be as descriptive (but succinct) as possible. Square brackets in titles confuse our chat-bot, and end up rendering in weird broken ways in SE chat. Avoid riddles, bad puns and other would-be funny (or NSFW) titles that don't really describe the issue or PR.
+Whether you create an issue or a pull request, please avoid using `[` square brackets `]` in the title, and try to be as descriptive (but succinct) as possible. Square brackets in titles confuse our chat-bot, and end up rendering in weird broken ways in SE chat. Avoid riddles, bad puns and other would-be funny (or NSFW) titles that don't really *describe the issue or PR*.
### Translations
-Rubberduck is Localized in multiple languages.
+Rubberduck is localized in multiple languages.
All these translations have been provided by volunteers.
We welcome both new translations as well as improvements to current translations very much.
-The resource files are RESX/XML files easily editable in any text editor, but comparing resource keys across languages/translations is much easier done with @Vogel612's [Translation Helper](https://github.com/Vogel612/TranslationHelper) tool, which automatically adds missing keys and highlights entries that need a new translation.
+The resource files are RESX/XML files easily editable in any text editor, but our recommendation goes to the [ResXManager](https://marketplace.visualstudio.com/items?itemName=TomEnglert.ResXManager) Visual Studio plug-in:
-If you contribute a translation for a brand new previously unsupported language, keep in touch with the dev team so that new resource keys can be translated when a new release is coming up; abandoned languages/translations will end up getting dropped.
+
+
+If you contribute a translation for a brand new previously unsupported language, please keep in touch with the dev team so that new resource keys can be translated when a new release is coming up; abandoned languages/translations will end up getting dropped.
## What comes out of it for me?
-Well ... the eternal gratitude of all Rubberduck users for one :wink:
+Well... the eternal gratitude of all Rubberduck users for one :wink:
Aside from that, all contributors are explicitly listed by name (or alias) in the "About Rubberduck" window.
-
-We ran a [fundraiser](https://gofundme.com/rubberduckvba) last summer, and had t-sihrts, mugs, pens, and stickers made - the goods are now unfortunately depleted; we'll probably run another campaign next year!
diff --git a/README.md b/README.md
index 4be14947cb..51e6a74056 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
-
-
+
+
- 2.2
+ 2.3.0
@@ -65,6 +65,7 @@
2.7.6684
+
1.8.4
@@ -81,7 +82,20 @@
2.0.20525
-
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
@@ -98,38 +112,4 @@
-
-
-
- Designer
- MSBuild:Compile
-
-
- Code
- %(Filename)
-
-
-
- Resources.resx
- true
- true
-
-
- Resources.Designer.cs
- ResXFileCodeGenerator
-
-
-
- True
- True
- Settings.settings
- True
-
-
- SettingsSingleFileGenerator
- Settings.Designer.cs
-
-
-
-
\ No newline at end of file
diff --git a/Rubberduck.Core/Settings/GeneralSettings.cs b/Rubberduck.Core/Settings/GeneralSettings.cs
index 8446eed04c..a16d961004 100644
--- a/Rubberduck.Core/Settings/GeneralSettings.cs
+++ b/Rubberduck.Core/Settings/GeneralSettings.cs
@@ -19,6 +19,9 @@ public interface IGeneralSettings
bool UserEditedLogLevel { get; set; }
int MinimumLogLevel { get; set; }
List EnableExperimentalFeatures { get; set; }
+ int RecentReferencesTracked { get; set; }
+ List RecentReferences { get; set; }
+ List PinnedReferences { get; set; }
}
[SettingsSerializeAs(SettingsSerializeAs.Xml)]
@@ -58,6 +61,10 @@ public int MinimumLogLevel
public List EnableExperimentalFeatures { get; set; } = new List();
+ public int RecentReferencesTracked { get; set; }
+ public List RecentReferences { get; set; }
+ public List PinnedReferences { get; set; }
+
public GeneralSettings()
{
//Enforce non-default default value for members
@@ -78,8 +85,11 @@ public bool Equals(GeneralSettings other)
AutoSavePeriod == other.AutoSavePeriod &&
UserEditedLogLevel == other.UserEditedLogLevel &&
MinimumLogLevel == other.MinimumLogLevel &&
- EnableExperimentalFeatures.All(a => other.EnableExperimentalFeatures.Contains(a)) &&
- EnableExperimentalFeatures.Count == other.EnableExperimentalFeatures.Count;
+ RecentReferencesTracked == other.RecentReferencesTracked &&
+ EnableExperimentalFeatures.Count == other.EnableExperimentalFeatures.Count &&
+ EnableExperimentalFeatures.All(other.EnableExperimentalFeatures.Contains) &&
+ RecentReferences.SequenceEqual(other.RecentReferences, StringComparer.OrdinalIgnoreCase) &&
+ PinnedReferences.OrderBy(x => x).SequenceEqual(other.PinnedReferences.OrderBy(x => x), StringComparer.OrdinalIgnoreCase);
}
}
}
\ No newline at end of file
diff --git a/Rubberduck.Core/Templates/ITemplate.cs b/Rubberduck.Core/Templates/ITemplate.cs
new file mode 100644
index 0000000000..370930f80c
--- /dev/null
+++ b/Rubberduck.Core/Templates/ITemplate.cs
@@ -0,0 +1,12 @@
+namespace Rubberduck.Templates
+{
+ public interface ITemplate
+ {
+ string Name { get; }
+ bool IsUserDefined { get; }
+ string Caption { get; }
+ string Description { get; }
+ string Read();
+ void Write(string content);
+ }
+}
\ No newline at end of file
diff --git a/Rubberduck.Core/Templates/Template.cs b/Rubberduck.Core/Templates/Template.cs
new file mode 100644
index 0000000000..5059c35d8a
--- /dev/null
+++ b/Rubberduck.Core/Templates/Template.cs
@@ -0,0 +1,69 @@
+namespace Rubberduck.Templates
+{
+ ///
+ /// Template can be either built-in or user-defined. For a built-in template, the
+ /// metadata should be stored in the
+ /// resource, with specific entries, currently Name, Caption, Description and Code.
+ /// Due to the fact that we cannot strong-type the reference to the resource entries
+ /// the class has unit tests to validate that the crucial elements are present in the
+ /// resource to guard against runtime errors/unexpected behavior due to missing/malformed
+ /// entries in the resources.
+ ///
+ public class Template : ITemplate
+ {
+ private readonly ITemplateFileHandler _handler;
+ public Template(string name, ITemplateFileHandler handler)
+ {
+ _handler = handler;
+
+ Name = name;
+ IsUserDefined = VerifyIfUserDefined(name);
+
+ if (IsUserDefined)
+ {
+ //TODO: Devise a way for users to define their captions/descriptions simply
+ Caption = Name;
+ Description = Name;
+ }
+ else
+ {
+ VerifyFile(name, handler);
+ (Caption, Description) = GetBuiltInMetaData(name);
+ }
+ }
+
+ public string Name { get; }
+ public bool IsUserDefined { get; }
+ public string Caption { get; }
+ public string Description { get; }
+
+ public string Read() => _handler.Read();
+
+ public void Write(string content) => _handler.Write(content);
+
+ private static bool VerifyIfUserDefined(string name)
+ {
+ var builtInName = Resources.Templates.ResourceManager.GetString(name + "_Name");
+ return builtInName == null || builtInName != name;
+ }
+
+ private static void VerifyFile(string name, ITemplateFileHandler handler)
+ {
+ if (handler.Exists)
+ {
+ return;
+ }
+
+ var content = Resources.Templates.ResourceManager.GetString(name + "_Code");
+ handler.Write(content);
+ }
+
+ private static (string caption, string description) GetBuiltInMetaData(string name)
+ {
+ var caption = Resources.Templates.ResourceManager.GetString(name + "_Caption");
+ var description = Resources.Templates.ResourceManager.GetString(name + "_Description");
+
+ return (caption, description);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Rubberduck.Core/Templates/TemplateFileHandler.cs b/Rubberduck.Core/Templates/TemplateFileHandler.cs
new file mode 100644
index 0000000000..b093b114d9
--- /dev/null
+++ b/Rubberduck.Core/Templates/TemplateFileHandler.cs
@@ -0,0 +1,74 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Rubberduck.SettingsProvider;
+
+namespace Rubberduck.Templates
+{
+ public interface ITemplateFileHandlerProvider
+ {
+ ITemplateFileHandler CreateTemplateFileHandler(string templateName);
+ IEnumerable GetTemplateNames();
+ }
+
+ public class TemplateFileHandlerProvider : ITemplateFileHandlerProvider
+ {
+ private readonly string _rootPath;
+
+ public TemplateFileHandlerProvider(IPersistancePathProvider pathProvider)
+ {
+ _rootPath = pathProvider.DataFolderPath("Templates");
+ }
+
+ public ITemplateFileHandler CreateTemplateFileHandler(string templateName)
+ {
+ if (!Directory.Exists(_rootPath))
+ {
+ Directory.CreateDirectory(_rootPath);
+ }
+
+ var fullPath = Path.Combine(_rootPath, templateName);
+ if (!Directory.Exists(Path.GetDirectoryName(fullPath)))
+ {
+ throw new InvalidOperationException("Cannot provide a path for where the parent directory do not exist");
+ }
+ return new TemplateFileHandler(fullPath);
+ }
+
+ public IEnumerable GetTemplateNames()
+ {
+ var info = new DirectoryInfo(_rootPath);
+ return info.GetFiles().Select(file => file.Name).ToList();
+ }
+ }
+
+ public interface ITemplateFileHandler
+ {
+ bool Exists { get; }
+ string Read();
+ void Write(string content);
+ }
+
+ public class TemplateFileHandler : ITemplateFileHandler
+ {
+ private readonly string _fullPath;
+
+ public TemplateFileHandler(string fullPath)
+ {
+ _fullPath = fullPath + (fullPath.EndsWith(".rdt") ? string.Empty : ".rdt");
+ }
+
+ public bool Exists => File.Exists(_fullPath);
+
+ public string Read()
+ {
+ return Exists ? File.ReadAllText(_fullPath) : null;
+ }
+
+ public void Write(string content)
+ {
+ File.WriteAllText(_fullPath, content);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Rubberduck.Core/Templates/TemplateProvider.cs b/Rubberduck.Core/Templates/TemplateProvider.cs
new file mode 100644
index 0000000000..a1c931ab9a
--- /dev/null
+++ b/Rubberduck.Core/Templates/TemplateProvider.cs
@@ -0,0 +1,62 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+
+namespace Rubberduck.Templates
+{
+ public interface ITemplateProvider
+ {
+ ITemplate Load(string templateName);
+ IEnumerable GetTemplates();
+ }
+
+ public class TemplateProvider : ITemplateProvider
+ {
+ private readonly ITemplateFileHandlerProvider _provider;
+
+ public TemplateProvider(ITemplateFileHandlerProvider provider)
+ {
+ _provider = provider;
+ }
+
+ public ITemplate Load(string templateName)
+ {
+ var handler = _provider.CreateTemplateFileHandler(templateName);
+ return new Template(templateName, handler);
+ }
+
+ private Lazy> LazyList => new Lazy>(() =>
+ {
+ var list = new List();
+ var set = Rubberduck.Resources.Templates.ResourceManager.GetResourceSet(CultureInfo.CurrentCulture, true, true);
+
+ foreach (DictionaryEntry entry in set)
+ {
+ var key = (string)entry.Key;
+ var value = (string) entry.Value;
+ if (key.EndsWith("_Name"))
+ {
+ var handler = _provider.CreateTemplateFileHandler(value);
+ list.Add(new Template(value, handler));
+ }
+ }
+
+ foreach (var templateName in _provider.GetTemplateNames())
+ {
+ if (list.Any(e => e.Name == templateName))
+ {
+ continue;
+ }
+
+ var handler = _provider.CreateTemplateFileHandler(templateName);
+ list.Add(new Template(templateName, handler));
+ }
+
+ return list;
+ });
+
+ public IEnumerable GetTemplates() => LazyList.Value;
+ }
+}
diff --git a/Rubberduck.Core/UI/AddRemoveReferences/AddRemoveReferencesDialog.Designer.cs b/Rubberduck.Core/UI/AddRemoveReferences/AddRemoveReferencesDialog.Designer.cs
new file mode 100644
index 0000000000..1bebbe5c2b
--- /dev/null
+++ b/Rubberduck.Core/UI/AddRemoveReferences/AddRemoveReferencesDialog.Designer.cs
@@ -0,0 +1,67 @@
+namespace Rubberduck.UI.AddRemoveReferences
+{
+ partial class AddRemoveReferencesDialog
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(AddRemoveReferencesDialog));
+ this.elementHost1 = new System.Windows.Forms.Integration.ElementHost();
+ this.addRemoveReferencesWindow1 = new Rubberduck.UI.AddRemoveReferences.AddRemoveReferencesWindow();
+ this.SuspendLayout();
+ //
+ // elementHost1
+ //
+ this.elementHost1.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.elementHost1.Location = new System.Drawing.Point(0, 0);
+ this.elementHost1.Name = "elementHost1";
+ this.elementHost1.Size = new System.Drawing.Size(800, 550);
+ this.elementHost1.TabIndex = 0;
+ this.elementHost1.Text = "elementHost1";
+ this.elementHost1.Child = this.addRemoveReferencesWindow1;
+ //
+ // AddRemoveReferencesDialog
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(800, 550);
+ this.Controls.Add(this.elementHost1);
+ this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
+ this.Name = "AddRemoveReferencesDialog";
+ this.ShowInTaskbar = false;
+ this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Show;
+ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
+ this.Text = "Rubberduck - Add/Remove References";
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.Integration.ElementHost elementHost1;
+ private AddRemoveReferencesWindow addRemoveReferencesWindow1;
+ }
+}
\ No newline at end of file
diff --git a/Rubberduck.Core/UI/AddRemoveReferences/AddRemoveReferencesDialog.cs b/Rubberduck.Core/UI/AddRemoveReferences/AddRemoveReferencesDialog.cs
new file mode 100644
index 0000000000..1cf105c7ab
--- /dev/null
+++ b/Rubberduck.Core/UI/AddRemoveReferences/AddRemoveReferencesDialog.cs
@@ -0,0 +1,21 @@
+using System.Windows.Forms;
+using Rubberduck.UI.Refactorings;
+
+namespace Rubberduck.UI.AddRemoveReferences
+{
+ public partial class AddRemoveReferencesDialog : Form, IRefactoringDialog
+ {
+ public AddRemoveReferencesViewModel ViewModel { get; }
+
+ public AddRemoveReferencesDialog()
+ {
+ InitializeComponent();
+ }
+
+ public AddRemoveReferencesDialog(AddRemoveReferencesViewModel viewModel) : this()
+ {
+ ViewModel = viewModel;
+ addRemoveReferencesWindow1.DataContext = viewModel;
+ }
+ }
+}
diff --git a/Rubberduck.Core/UI/AddRemoveReferences/AddRemoveReferencesDialog.resx b/Rubberduck.Core/UI/AddRemoveReferences/AddRemoveReferencesDialog.resx
new file mode 100644
index 0000000000..0a0a5023e2
--- /dev/null
+++ b/Rubberduck.Core/UI/AddRemoveReferences/AddRemoveReferencesDialog.resx
@@ -0,0 +1,145 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+
+ AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAEAAAAAAAAABwAAAAgAAAAGAAAABwAAAAgAAAADAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAABAwNEAAAA/zpkZP8uXmP/LGBl/y9hY/80ZGP/OWlo/0t2dP8AAAD/AAAAxwEC
+ AogAAAAAAAAAAAAAAAAAAQGTCicr/zTk+f9U3v//RtX//0rV//9P3f//Ve3//y/p//855/v/Qv3//1T/
+ //8MLS/+AAAA0QAAAAAAAQGRBBMW/zXn/f846v//SNz9/0jL/v8xvv//OMv//0DU//8/3v3/L+j//zTo
+ //8r4/z/Svr//0mAf/4BAgKIISko/ynQ5v8x6P//Men//y7Z7v9ApLn/LrH4/y+y9f84vfT/K57K/y/l
+ +v8x5///L+j//0v2//+M////AAAAihIsLf8w7f//MOr//zXq//8x6f//JK6+/yeIp/8gZoL/LYyq/ynF
+ 2f8w5/7/MOj//zTq//9K9P//g////wAAAJUAAADSROX//1LK//9E5f//OO7//zLr//8w4ff/L+X7/y/j
+ +v8x6P//N+z//zHo//8s5v//gv3//7j///4AAACcAAAAlD+Cof87c4z/RGNj/zRdY/8xoK7+LfX//xrm
+ //8c2v//IeD//zDx//9G+P//Vvr//9z///8kKir/AAAAAAAAAFYAAABtAAAAGgAAAAgAAAALAAAA2yTb
+ +v8Y0f//HNr//xzf//8z+///Yv///2y8vP4qMjL/BAUFMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3Cac
+ r/8t0/7/Icn//x7Y//8o+P7/Sv///wAAAP8AAACgAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAEwwh
+ I/873P//JMj//xS8//8Wyv//Gpi2/wIADv8LCgC5AAAABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ ACUqYWT/Mtb//xy6//8Gt/7/Htr//xlhbP8fRvX/O1Hs/jlCctsAAAADAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAaSGpo/0vZ//4Zs///n+z//wAAAP9FVFz/E0Gh/z1G//9AS2j/AAAAAwAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAABAAAAKiS8/7MOr3//pLc/v/v////Pmxs/zBBPcIAAAD/ISEv/wAAAAQAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAABNAAAAtqXk8shM0v//Ppac/wAAAP8AAAA2AAAAAAAAAAcAAAABAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0AAACfAAAAlAAAAIAAAAA3AAAAAQAAAAAAAAAAAAAAAAAA
+ AAAAAAAA//8AAOADAACAAQAAAAAAAAAAAAAAAAAAAAAAAAABAAD4AwAA8AcAAPAPAADwBwAA8AcAAPAH
+ AAD4PwAA/H8AAA==
+
+
+
\ No newline at end of file
diff --git a/Rubberduck.Core/UI/AddRemoveReferences/AddRemoveReferencesModel.cs b/Rubberduck.Core/UI/AddRemoveReferences/AddRemoveReferencesModel.cs
new file mode 100644
index 0000000000..c798e69ea3
--- /dev/null
+++ b/Rubberduck.Core/UI/AddRemoveReferences/AddRemoveReferencesModel.cs
@@ -0,0 +1,34 @@
+using System.Collections.Generic;
+using System.Linq;
+using Rubberduck.AddRemoveReferences;
+using Rubberduck.Parsing.Symbols;
+using Rubberduck.Settings;
+
+namespace Rubberduck.UI.AddRemoveReferences
+{
+ public interface IAddRemoveReferencesModel
+ {
+ IGeneralSettings Settings { get; set; }
+ ProjectDeclaration Project { get; set; }
+ List References { get; set; }
+ IReadOnlyList NewReferences { get; set; }
+ }
+
+ public class AddRemoveReferencesModel : IAddRemoveReferencesModel
+ {
+ public AddRemoveReferencesModel(ProjectDeclaration project, IEnumerable references, IGeneralSettings settings)
+ {
+ Settings = settings;
+ Project = project;
+ References = references.ToList();
+ }
+
+ public IGeneralSettings Settings { get; set; }
+
+ public ProjectDeclaration Project { get; set; }
+
+ public List References { get; set; }
+
+ public IReadOnlyList NewReferences { get; set; }
+ }
+}
diff --git a/Rubberduck.Core/UI/AddRemoveReferences/AddRemoveReferencesPresenter.cs b/Rubberduck.Core/UI/AddRemoveReferences/AddRemoveReferencesPresenter.cs
new file mode 100644
index 0000000000..73ee20e69d
--- /dev/null
+++ b/Rubberduck.Core/UI/AddRemoveReferences/AddRemoveReferencesPresenter.cs
@@ -0,0 +1,50 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows.Forms;
+using Rubberduck.AddRemoveReferences;
+using Rubberduck.Parsing.Symbols;
+using Rubberduck.UI.Refactorings;
+
+namespace Rubberduck.UI.AddRemoveReferences
+{
+ public interface IAddRemoveReferencesPresenter
+ {
+ IAddRemoveReferencesModel Show();
+ IAddRemoveReferencesModel Show(ProjectDeclaration project);
+ IAddRemoveReferencesModel Model { get; }
+ }
+
+ public class AddRemoveReferencesPresenter : IAddRemoveReferencesPresenter
+ {
+ private readonly IRefactoringDialog _view;
+
+ public AddRemoveReferencesPresenter(IRefactoringDialog view)
+ {
+ _view = view;
+ Model = _view.ViewModel.Model;
+ }
+
+ public IAddRemoveReferencesModel Show()
+ {
+ return Model.Project == null ? null : Show(Model.Project);
+ }
+
+ public IAddRemoveReferencesModel Show(ProjectDeclaration project)
+ {
+ if (project is null)
+ {
+ return null;
+ }
+
+ Model.Project = project;
+ _view.ViewModel.Model = Model;
+
+ _view.ShowDialog();
+
+ Model.NewReferences = _view.ViewModel.ProjectReferences.SourceCollection.OfType().ToList();
+ return Model;
+ }
+
+ public IAddRemoveReferencesModel Model { get; }
+ }
+}
diff --git a/Rubberduck.Core/UI/AddRemoveReferences/AddRemoveReferencesPresenterFactory.cs b/Rubberduck.Core/UI/AddRemoveReferences/AddRemoveReferencesPresenterFactory.cs
new file mode 100644
index 0000000000..5fe334ad0a
--- /dev/null
+++ b/Rubberduck.Core/UI/AddRemoveReferences/AddRemoveReferencesPresenterFactory.cs
@@ -0,0 +1,110 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using NLog;
+using Rubberduck.AddRemoveReferences;
+using Rubberduck.Interaction;
+using Rubberduck.Parsing.Symbols;
+using Rubberduck.Parsing.VBA;
+using Rubberduck.Refactorings;
+using Rubberduck.Settings;
+using Rubberduck.SettingsProvider;
+using Rubberduck.VBEditor.SafeComWrappers.Abstract;
+
+namespace Rubberduck.UI.AddRemoveReferences
+{
+ public interface IAddRemoveReferencesPresenterFactory : IRefactoringPresenterFactory
+ {
+ AddRemoveReferencesPresenter Create(ProjectDeclaration project);
+ }
+
+ public class AddRemoveReferencesPresenterFactory : IAddRemoveReferencesPresenterFactory
+ {
+ private readonly Logger _logger = LogManager.GetCurrentClassLogger();
+ private readonly bool _use64BitPaths = Environment.Is64BitProcess;
+
+ private readonly IVBE _vbe;
+ private readonly RubberduckParserState _state;
+ private readonly IConfigProvider _settings;
+ private readonly IRegisteredLibraryFinderService _finder;
+ private readonly IMessageBox _messageBox;
+
+ public AddRemoveReferencesPresenterFactory(IVBE vbe,
+ RubberduckParserState state,
+ IConfigProvider generalSettingsProvider,
+ IRegisteredLibraryFinderService finder,
+ IMessageBox messageBox)
+ {
+ _vbe = vbe;
+ _state = state;
+ _settings = generalSettingsProvider;
+ _finder = finder;
+ _messageBox = messageBox;
+ }
+
+ public AddRemoveReferencesPresenter Create(ProjectDeclaration project)
+ {
+ if (project is null)
+ {
+ return null;
+ }
+
+ var refs = new Dictionary();
+ // Iterating the returned libraries here instead of just .ToDictionary() using because we can't trust that the registry doesn't contain errors.
+ foreach (var reference in _finder.FindRegisteredLibraries())
+ {
+ if (refs.ContainsKey(reference.UniqueId))
+ {
+ _logger.Warn($"Duplicate registry definition for {reference.Guid} version {reference.Version}.");
+ continue;
+ }
+ refs.Add(reference.UniqueId, reference);
+ }
+
+ var models = new Dictionary();
+ using (var references = project.Project?.References)
+ {
+ if (references is null)
+ {
+ return null;
+ }
+ var priority = 1;
+ foreach (var reference in references)
+ {
+ var libraryId = new RegisteredLibraryKey(new Guid(reference.Guid), reference.Major, reference.Minor);
+ if (refs.ContainsKey(libraryId))
+ {
+ // TODO: If for some reason the VBA reference is broken, we could technically use this to repair it. Just a thought...
+ models.Add(libraryId, new ReferenceModel(refs[libraryId], reference, priority++));
+ }
+ else // These should all be either VBA projects or irreparably broken.
+ {
+ models.Add(libraryId, new ReferenceModel(reference, priority++));
+ }
+ reference.Dispose();
+ }
+ }
+
+ foreach (var reference in refs.Where(library =>
+ (_use64BitPaths || library.Value.Has32BitVersion) &&
+ !models.ContainsKey(library.Key)))
+ {
+ models.Add(reference.Key, new ReferenceModel(reference.Value));
+ }
+
+ var settings = _settings.Create();
+ var model = new AddRemoveReferencesModel(project, models.Values, settings);
+
+ return new AddRemoveReferencesPresenter(new AddRemoveReferencesDialog(new AddRemoveReferencesViewModel(model, _messageBox)));
+ }
+
+ public AddRemoveReferencesPresenter Create()
+ {
+ using (var pane = _vbe.ActiveCodePane)
+ {
+ var selected = (ProjectDeclaration)Declaration.GetProjectParent(_state.DeclarationFinder.FindSelectedDeclaration(pane));
+ return selected is null ? null : Create(selected);
+ }
+ }
+ }
+}
diff --git a/Rubberduck.Core/UI/AddRemoveReferences/AddRemoveReferencesViewModel.cs b/Rubberduck.Core/UI/AddRemoveReferences/AddRemoveReferencesViewModel.cs
new file mode 100644
index 0000000000..b2960d2278
--- /dev/null
+++ b/Rubberduck.Core/UI/AddRemoveReferences/AddRemoveReferencesViewModel.cs
@@ -0,0 +1,346 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Windows.Input;
+using System.ComponentModel;
+using System.Runtime.InteropServices;
+using System.Windows.Data;
+using NLog;
+using Rubberduck.AddRemoveReferences;
+using Rubberduck.Interaction;
+using Rubberduck.Resources;
+using Rubberduck.UI.Command;
+using Rubberduck.VBEditor.SafeComWrappers;
+
+namespace Rubberduck.UI.AddRemoveReferences
+{
+ public enum ReferenceFilter
+ {
+ Recent,
+ Pinned,
+ ComTypes,
+ Projects
+ }
+
+ public class AddRemoveReferencesViewModel : ViewModelBase
+ {
+ private readonly IMessageBox _messageBox;
+
+ private readonly ObservableCollection _available;
+ private readonly ObservableCollection _project;
+
+ public AddRemoveReferencesViewModel(IAddRemoveReferencesModel model, IMessageBox messageBox)
+ {
+ Model = model;
+ _messageBox = messageBox;
+
+ foreach (var reference in model.References
+ .Where(item => Model.Settings.PinnedReferences
+ .Contains(item.Type == ReferenceKind.TypeLibrary ? item.Guid.ToString() : item.FullPath)))
+ {
+ reference.IsPinned = true;
+ }
+
+ foreach (var reference in model.References
+ .Where(item => Model.Settings.RecentReferences
+ .Contains(item.Type == ReferenceKind.TypeLibrary ? item.Guid.ToString() : item.FullPath)))
+ {
+ reference.IsRecent = true;
+ }
+
+ _available = new ObservableCollection(model.References
+ .Where(reference => !reference.IsReferenced).OrderBy(reference => reference.Description));
+ _project = new ObservableCollection(model.References
+ .Where(reference => reference.IsReferenced).OrderBy(reference => reference.Priority));
+
+ AddCommand = new DelegateCommand(LogManager.GetCurrentClassLogger(), ExecuteAddCommand);
+ RemoveCommand = new DelegateCommand(LogManager.GetCurrentClassLogger(), ExecuteRemoveCommand);
+ BrowseCommand = new DelegateCommand(LogManager.GetCurrentClassLogger(), ExecuteBrowseCommand);
+ MoveUpCommand = new DelegateCommand(LogManager.GetCurrentClassLogger(), ExecuteMoveUpCommand);
+ MoveDownCommand = new DelegateCommand(LogManager.GetCurrentClassLogger(), ExecuteMoveDownCommand);
+ PinLibraryCommand = new DelegateCommand(LogManager.GetCurrentClassLogger(), ExecutePinLibraryCommand);
+ PinReferenceCommand = new DelegateCommand(LogManager.GetCurrentClassLogger(), ExecutePinReferenceCommand);
+ }
+
+ public IAddRemoveReferencesModel Model { get; set; }
+
+ public ICommand AddCommand { get; }
+
+ public ICommand RemoveCommand { get; }
+ ///
+ /// Prompts user for a .tlb, .dll, or .ocx file, and attempts to append it to .
+ ///
+ public ICommand BrowseCommand { get; }
+
+ ///
+ /// Applies all changes to project references.
+ ///
+ public ICommand ApplyCommand { get; }
+
+ ///
+ /// Moves the up on the 'Priority' tab.
+ ///
+ public ICommand MoveUpCommand { get; }
+
+ ///
+ /// Moves the down on the 'Priority' tab.
+ ///
+ public ICommand MoveDownCommand { get; }
+
+ public ICommand PinLibraryCommand { get; }
+
+ public ICommand PinReferenceCommand { get; }
+
+ private void ExecuteAddCommand(object parameter)
+ {
+ if (SelectedLibrary == null)
+ {
+ return;
+ }
+
+ SelectedLibrary.Priority = _project.Count + 1;
+ _project.Add(SelectedLibrary);
+ ProjectReferences.Refresh();
+ _available.Remove(SelectedLibrary);
+ }
+
+ private void ExecuteRemoveCommand(object parameter)
+ {
+ if (SelectedReference == null)
+ {
+ return;
+ }
+
+ var priority = SelectedReference.Priority;
+ SelectedReference.Priority = null;
+ _available.Add(SelectedReference);
+ _project.Remove(SelectedReference);
+
+ foreach (var reference in _project.Where(lib => lib.Priority > priority).ToList())
+ {
+ reference.Priority--;
+ }
+ ProjectReferences.Refresh();
+ }
+
+ private static readonly List FileFilters = new List
+ {
+ RubberduckUI.References_BrowseFilterExecutable,
+ RubberduckUI.References_BrowseFilterExcel,
+ RubberduckUI.References_BrowseFilterTypes,
+ RubberduckUI.References_BrowseFilterActiveX,
+ RubberduckUI.References_BrowseFilterAllFiles,
+ };
+
+ private void ExecuteBrowseCommand(object parameter)
+ {
+ using (var dialog = new OpenFileDialog
+ {
+ Filter = string.Join("|", FileFilters),
+ Title = RubberduckUI.References_BrowseCaption
+ })
+ {
+ dialog.ShowDialog();
+ if (string.IsNullOrEmpty(dialog.FileName))
+ {
+ return;
+ }
+
+ var existing = _available.FirstOrDefault(library =>
+ library.FullPath.Equals(dialog.FileName, StringComparison.OrdinalIgnoreCase));
+
+ var project = Model.Project.Project;
+ using (var references = project.References)
+ {
+ try
+ {
+ using (var reference = references.AddFromFile(dialog.FileName))
+ {
+ if (reference is null)
+ {
+ return;
+ }
+
+ _project.Add(existing ?? new ReferenceModel(reference, _project.Count + 1));
+ ProjectReferences.Refresh();
+ if (existing is null)
+ {
+ return;
+ }
+
+ existing.Priority = _project.Count + 1;
+ _available.Remove(existing);
+ AvailableReferences.Refresh();
+ }
+ }
+ catch (COMException ex)
+ {
+ _messageBox.NotifyWarn(ex.Message, RubberduckUI.References_AddFailedCaption);
+ }
+ }
+ }
+ }
+
+ private void ExecuteMoveUpCommand(object parameter)
+ {
+ if (SelectedReference == null || SelectedReference.IsBuiltIn || SelectedReference.Priority == 1)
+ {
+ return;
+ }
+
+ var swap = _project.SingleOrDefault(reference => reference.Priority == SelectedReference.Priority - 1);
+
+ if (swap is null || swap.IsBuiltIn)
+ {
+ return;
+ }
+
+ swap.Priority = SelectedReference.Priority;
+ SelectedReference.Priority--;
+ ProjectReferences.Refresh();
+ }
+
+ private void ExecuteMoveDownCommand(object parameter)
+ {
+ if (SelectedReference == null || SelectedReference.IsBuiltIn || SelectedReference.Priority == _project.Count)
+ {
+ return;
+ }
+
+ var swap = _project.SingleOrDefault(reference => reference.Priority == SelectedReference.Priority + 1);
+
+ if (swap is null || swap.IsBuiltIn)
+ {
+ return;
+ }
+
+ swap.Priority = SelectedReference.Priority;
+ SelectedReference.Priority++;
+ ProjectReferences.Refresh();
+ }
+
+ private void ExecutePinLibraryCommand(object parameter)
+ {
+ if (SelectedLibrary == null)
+ {
+ return;
+ }
+ SelectedLibrary.IsPinned = !SelectedLibrary.IsPinned;
+ AvailableReferences.Refresh();
+ }
+
+ private void ExecutePinReferenceCommand(object parameter)
+ {
+ if (SelectedReference == null || SelectedReference.IsBuiltIn)
+ {
+ return;
+ }
+ SelectedReference.IsPinned = !SelectedReference.IsPinned;
+ ProjectReferences.Refresh();
+ }
+
+ public ICollectionView ProjectReferences
+ {
+ get
+ {
+ var projects = CollectionViewSource.GetDefaultView(_project);
+ projects.SortDescriptions.Add(new SortDescription("Priority", ListSortDirection.Ascending));
+ return projects;
+ }
+ }
+
+ public ICollectionView AvailableReferences
+ {
+ get
+ {
+ var available = CollectionViewSource.GetDefaultView(_available);
+ available.Filter = reference => Filter((ReferenceModel)reference);
+ return available;
+ }
+ }
+
+ private string _filter;
+ public string SelectedFilter
+ {
+ get => _filter;
+ set
+ {
+ _filter = value;
+ AvailableReferences.Refresh();
+ }
+ }
+
+ private bool Filter(ReferenceModel reference)
+ {
+ var filtered = false;
+ Enum.TryParse(SelectedFilter, out var filter);
+ switch (filter)
+ {
+ case ReferenceFilter.Recent:
+ filtered = reference.IsRecent;
+ break;
+ case ReferenceFilter.Pinned:
+ filtered = reference.IsPinned;
+ break;
+ case ReferenceFilter.ComTypes:
+ filtered = reference.Type == ReferenceKind.TypeLibrary;
+ break;
+ case ReferenceFilter.Projects:
+ filtered = reference.Type == ReferenceKind.Project;
+ break;
+ }
+
+ var searched = string.IsNullOrEmpty(Search)
+ || reference.Name.IndexOf(Search, StringComparison.OrdinalIgnoreCase) >= 0
+ || reference.Description.IndexOf(Search, StringComparison.OrdinalIgnoreCase) >= 0
+ || reference.FullPath.IndexOf(Search, StringComparison.OrdinalIgnoreCase) >= 0;
+
+ return filtered && searched;
+ }
+
+ private string _search = string.Empty;
+ public string Search
+ {
+ get => _search;
+ set
+ {
+ _search = value;
+ AvailableReferences.Refresh();
+ }
+ }
+
+ private ReferenceModel _selection;
+ public ReferenceModel CurrentSelection
+ {
+ get => _selection;
+ set
+ {
+ _selection = value;
+ OnPropertyChanged();
+ }
+ }
+
+ private ReferenceModel _reference;
+ public ReferenceModel SelectedReference
+ {
+ get => _reference;
+ set
+ {
+ _reference = value;
+ CurrentSelection = _reference;
+ }
+ }
+
+ private ReferenceModel _library;
+ public ReferenceModel SelectedLibrary
+ {
+ get => _library;
+ set
+ {
+ _library = value;
+ CurrentSelection = _library;
+ }
+ }
+ }
+}
diff --git a/Rubberduck.Core/UI/AddRemoveReferences/AddRemoveReferencesWindow.xaml b/Rubberduck.Core/UI/AddRemoveReferences/AddRemoveReferencesWindow.xaml
new file mode 100644
index 0000000000..00bbb46441
--- /dev/null
+++ b/Rubberduck.Core/UI/AddRemoveReferences/AddRemoveReferencesWindow.xaml
@@ -0,0 +1,369 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Rubberduck.Core/UI/AddRemoveReferences/AddRemoveReferencesWindow.xaml.cs b/Rubberduck.Core/UI/AddRemoveReferences/AddRemoveReferencesWindow.xaml.cs
new file mode 100644
index 0000000000..4ef1f4af84
--- /dev/null
+++ b/Rubberduck.Core/UI/AddRemoveReferences/AddRemoveReferencesWindow.xaml.cs
@@ -0,0 +1,17 @@
+using System.Windows.Controls;
+
+namespace Rubberduck.UI.AddRemoveReferences
+{
+ ///
+ /// Interaction logic for AddRemoveReferencesWindow.xaml
+ ///
+ public partial class AddRemoveReferencesWindow : UserControl
+ {
+ public AddRemoveReferencesWindow()
+ {
+ InitializeComponent();
+ }
+
+ private AddRemoveReferencesViewModel ViewModel => DataContext as AddRemoveReferencesViewModel;
+ }
+}
diff --git a/Rubberduck.Core/UI/AddRemoveReferences/ReferenceStatusImageSourceConverter.cs b/Rubberduck.Core/UI/AddRemoveReferences/ReferenceStatusImageSourceConverter.cs
new file mode 100644
index 0000000000..63a4bf61ee
--- /dev/null
+++ b/Rubberduck.Core/UI/AddRemoveReferences/ReferenceStatusImageSourceConverter.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Windows.Media;
+using Rubberduck.AddRemoveReferences;
+using ImageSourceConverter = Rubberduck.UI.Converters.ImageSourceConverter;
+
+namespace Rubberduck.UI.AddRemoveReferences
+{
+ public class ReferenceStatusImageSourceConverter : ImageSourceConverter
+ {
+ private readonly IDictionary _icons =
+ new Dictionary
+ {
+ { ReferenceStatus.None, null },
+ { ReferenceStatus.Pinned , ToImageSource(Resources.RubberduckUI.pinned) },
+ { ReferenceStatus.Recent, ToImageSource(Resources.RubberduckUI.clock_select) },
+ { ReferenceStatus.Recent | ReferenceStatus.Pinned, ToImageSource(Resources.RubberduckUI.clock_select_pinned) },
+ { ReferenceStatus.BuiltIn, ToImageSource(Resources.RubberduckUI.padlock) },
+ { ReferenceStatus.Broken, ToImageSource(Resources.RubberduckUI.exclamation) },
+ { ReferenceStatus.Loaded, ToImageSource(Resources.RubberduckUI.tick_circle) },
+ { ReferenceStatus.Added, ToImageSource(Resources.RubberduckUI.plus_circle) },
+ { ReferenceStatus.BuiltIn | ReferenceStatus.Pinned, ToImageSource(Resources.RubberduckUI.lock_pinned) },
+ { ReferenceStatus.Broken | ReferenceStatus.Pinned, ToImageSource(Resources.RubberduckUI.exclamation_pinned) },
+ { ReferenceStatus.Loaded | ReferenceStatus.Pinned, ToImageSource(Resources.RubberduckUI.tick_circle_pinned) },
+ { ReferenceStatus.Added | ReferenceStatus.Pinned, ToImageSource(Resources.RubberduckUI.plus_circle_pinned) }
+ };
+
+ public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value == null || value.GetType() != typeof(ReferenceStatus)) { return null; }
+ return _icons[(ReferenceStatus)value];
+ }
+ }
+}
\ No newline at end of file
diff --git a/Rubberduck.Core/UI/CodeExplorer/CodeExplorerControl.xaml b/Rubberduck.Core/UI/CodeExplorer/CodeExplorerControl.xaml
index 70764848f8..371e340605 100644
--- a/Rubberduck.Core/UI/CodeExplorer/CodeExplorerControl.xaml
+++ b/Rubberduck.Core/UI/CodeExplorer/CodeExplorerControl.xaml
@@ -17,20 +17,23 @@
+
+
+
-
-
+
+
-
+
@@ -39,12 +42,13 @@
-
+
+
-
+
@@ -57,7 +61,7 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DataType="codeExplorer:CodeExplorerProjectViewModel"
+ ItemsSource="{Binding Items}">
+
+
-
-
+
+
diff --git a/Rubberduck.Core/UI/Refactorings/ExtractInterface/InterfaceMemberViewModel.cs b/Rubberduck.Core/UI/Refactorings/ExtractInterface/InterfaceMemberViewModel.cs
index f67315e644..9aa5f4e007 100644
--- a/Rubberduck.Core/UI/Refactorings/ExtractInterface/InterfaceMemberViewModel.cs
+++ b/Rubberduck.Core/UI/Refactorings/ExtractInterface/InterfaceMemberViewModel.cs
@@ -4,18 +4,14 @@ namespace Rubberduck.UI.Refactorings.ExtractInterface
{
internal class InterfaceMemberViewModel : ViewModelBase
{
- private readonly InterfaceMember _wrapped;
- internal InterfaceMember Wrapped { get => _wrapped; }
-
-
- private bool _isSelected;
- private InterfaceMember model;
-
public InterfaceMemberViewModel(InterfaceMember model)
{
- this.model = model;
+ Wrapped = model;
}
+ internal InterfaceMember Wrapped { get; }
+
+ private bool _isSelected;
public bool IsSelected
{
get => _isSelected;
@@ -26,7 +22,7 @@ public bool IsSelected
}
}
- public string FullMemberSignature { get => _wrapped.FullMemberSignature; }
+ public string FullMemberSignature => Wrapped?.FullMemberSignature;
}
internal static class ConversionExtensions
diff --git a/Rubberduck.Core/UI/ISaveFileDialog.cs b/Rubberduck.Core/UI/SaveFileDialog.cs
similarity index 95%
rename from Rubberduck.Core/UI/ISaveFileDialog.cs
rename to Rubberduck.Core/UI/SaveFileDialog.cs
index 8c32d7fb14..9485ab8e60 100644
--- a/Rubberduck.Core/UI/ISaveFileDialog.cs
+++ b/Rubberduck.Core/UI/SaveFileDialog.cs
@@ -109,7 +109,20 @@ public bool DereferenceLinks
public void Dispose()
{
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ private bool _isDisposed;
+ protected virtual void Dispose(bool disposing)
+ {
+ if (_isDisposed || !disposing)
+ {
+ return;
+ }
+
_saveFileDialog.Dispose();
+ _isDisposed = true;
}
public event EventHandler Disposed;
diff --git a/Rubberduck.Core/UI/SelectionChangeService.cs b/Rubberduck.Core/UI/SelectionChangeService.cs
index 6919563c19..01e2822b98 100644
--- a/Rubberduck.Core/UI/SelectionChangeService.cs
+++ b/Rubberduck.Core/UI/SelectionChangeService.cs
@@ -186,8 +186,21 @@ private bool DeclarationChanged(Declaration current)
public void Dispose()
{
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ private bool _isDisposed;
+ protected virtual void Dispose(bool disposing)
+ {
+ if (_isDisposed || !disposing)
+ {
+ return;
+ }
+
VbeNativeServices.SelectionChanged -= OnVbeSelectionChanged;
VbeNativeServices.WindowFocusChange -= OnVbeFocusChanged;
+ _isDisposed = true;
}
}
diff --git a/Rubberduck.Core/UI/Settings/AutoCompleteSettings.xaml b/Rubberduck.Core/UI/Settings/AutoCompleteSettings.xaml
index a54d24fc96..dcf70c8995 100644
--- a/Rubberduck.Core/UI/Settings/AutoCompleteSettings.xaml
+++ b/Rubberduck.Core/UI/Settings/AutoCompleteSettings.xaml
@@ -111,12 +111,25 @@
IsChecked="{Binding ConcatVbNewLine}"
Content="{Resx ResxName=Rubberduck.Resources.Settings.AutoCompletesPage, Key=ConcatVbNewLine}" />
-
-
+
+
+
+
+
+
+
+
+
diff --git a/Rubberduck.Core/UI/Settings/AutoCompleteSettingsViewModel.cs b/Rubberduck.Core/UI/Settings/AutoCompleteSettingsViewModel.cs
index e61b8897ef..07797043e6 100644
--- a/Rubberduck.Core/UI/Settings/AutoCompleteSettingsViewModel.cs
+++ b/Rubberduck.Core/UI/Settings/AutoCompleteSettingsViewModel.cs
@@ -1,4 +1,5 @@
-using NLog;
+using System.Windows.Input;
+using NLog;
using Rubberduck.Resources;
using Rubberduck.Resources.Settings;
using Rubberduck.Settings;
@@ -14,8 +15,21 @@ public AutoCompleteSettingsViewModel(Configuration config)
TransferSettingsToView(config.UserSettings.AutoCompleteSettings);
ExportButtonCommand = new DelegateCommand(LogManager.GetCurrentClassLogger(), _ => ExportSettings());
ImportButtonCommand = new DelegateCommand(LogManager.GetCurrentClassLogger(), _ => ImportSettings());
+
+ IncrementMaxConcatLinesCommand = new DelegateCommand(null, ExecuteIncrementMaxConcatLines, CanExecuteIncrementMaxConcatLines);
+ DecrementMaxConcatLinesCommand = new DelegateCommand(null, ExecuteDecrementMaxConcatLines, CanExecuteDecrementMaxConcatLines);
}
+ public ICommand IncrementMaxConcatLinesCommand { get; }
+
+ private bool CanExecuteIncrementMaxConcatLines(object parameter) => ConcatMaxLines < ConcatMaxLinesMaxValue;
+ private void ExecuteIncrementMaxConcatLines(object parameter) => ConcatMaxLines++;
+
+ public ICommand DecrementMaxConcatLinesCommand { get; }
+
+ private bool CanExecuteDecrementMaxConcatLines(object parameter) => ConcatMaxLines > ConcatMaxLinesMinValue;
+ private void ExecuteDecrementMaxConcatLines(object parameter) => ConcatMaxLines--;
+
public void SetToDefaults(Configuration config)
{
TransferSettingsToView(config.UserSettings.AutoCompleteSettings);
diff --git a/Rubberduck.Core/UI/Settings/Settings.cs b/Rubberduck.Core/UI/Settings/Settings.cs
index 19ee8829b6..33b66e5899 100644
--- a/Rubberduck.Core/UI/Settings/Settings.cs
+++ b/Rubberduck.Core/UI/Settings/Settings.cs
@@ -61,10 +61,23 @@ private static void LoadLanguage()
public void Dispose()
{
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ private bool _isDisposed;
+ protected virtual void Dispose(bool disposing)
+ {
+ if (_isDisposed || !disposing)
+ {
+ return;
+ }
+
if (_configService != null)
{
_configService.SettingsChanged -= SettingsChanged;
}
+ _isDisposed = true;
}
}
}
\ No newline at end of file
diff --git a/Rubberduck.Core/UI/SimpleListControl.cs b/Rubberduck.Core/UI/SimpleListControl.cs
index dc34878209..7f4ce21452 100644
--- a/Rubberduck.Core/UI/SimpleListControl.cs
+++ b/Rubberduck.Core/UI/SimpleListControl.cs
@@ -6,7 +6,7 @@
namespace Rubberduck.UI
{
- public partial class SimpleListControl : UserControl, IDockableUserControl
+ public sealed partial class SimpleListControl : UserControl, IDockableUserControl
{
public SimpleListControl(Declaration target)
: this(string.Format(RubberduckUI.AllReferences_Caption, target.IdentifierName))
diff --git a/Rubberduck.Core/UI/Splash.Designer.cs b/Rubberduck.Core/UI/Splash.Designer.cs
index 884a3807de..3399bbb9fc 100644
--- a/Rubberduck.Core/UI/Splash.Designer.cs
+++ b/Rubberduck.Core/UI/Splash.Designer.cs
@@ -76,7 +76,8 @@ private void InitializeComponent()
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "Splash";
- this.ShowIcon = false;
+ this.ShowIcon = true;
+ this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.ShowInTaskbar = false;
this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
diff --git a/Rubberduck.Core/UI/Splash.cs b/Rubberduck.Core/UI/Splash.cs
index a5859dfd0e..5f8db3af5d 100644
--- a/Rubberduck.Core/UI/Splash.cs
+++ b/Rubberduck.Core/UI/Splash.cs
@@ -11,8 +11,8 @@ public Splash()
public string Version
{
- get { return VersionLabel.Text; }
- set { VersionLabel.Text = value; }
+ get => VersionLabel.Text;
+ set => VersionLabel.Text = value;
}
}
}
diff --git a/Rubberduck.Core/UI/Splash.resx b/Rubberduck.Core/UI/Splash.resx
index a4fe0f3d92..60b3478e08 100644
--- a/Rubberduck.Core/UI/Splash.resx
+++ b/Rubberduck.Core/UI/Splash.resx
@@ -1081,4 +1081,8 @@
AAAASUVORK5CYII=
+
+
+ ..\Ducky.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
\ No newline at end of file
diff --git a/Rubberduck.Core/UI/ToDoItems/ToDoExplorerWindow.cs b/Rubberduck.Core/UI/ToDoItems/ToDoExplorerWindow.cs
index 7c63a7e974..d252a7813b 100644
--- a/Rubberduck.Core/UI/ToDoItems/ToDoExplorerWindow.cs
+++ b/Rubberduck.Core/UI/ToDoItems/ToDoExplorerWindow.cs
@@ -6,7 +6,7 @@
namespace Rubberduck.UI.ToDoItems
{
[ExcludeFromCodeCoverage]
- public partial class ToDoExplorerWindow : UserControl, IDockableUserControl
+ public sealed partial class ToDoExplorerWindow : UserControl, IDockableUserControl
{
private const string ClassId = "8B071EDA-2C9C-4009-9A22-A1958BF98B28"; // todo get from Resources.Registration?
string IDockableUserControl.ClassId { get { return ClassId; } }
diff --git a/Rubberduck.Core/UI/UnitTesting/Commands/AddTestModuleCommand.cs b/Rubberduck.Core/UI/UnitTesting/Commands/AddTestModuleCommand.cs
index 67d2bb3ff8..548dccf614 100644
--- a/Rubberduck.Core/UI/UnitTesting/Commands/AddTestModuleCommand.cs
+++ b/Rubberduck.Core/UI/UnitTesting/Commands/AddTestModuleCommand.cs
@@ -125,12 +125,12 @@ private string DeclarationFormatFor(string declarationFormat, string type, IUnit
private IVBProject GetProject()
{
+ //No using because the wrapper gets returned potentially.
var activeProject = _vbe.ActiveVBProject;
if (!activeProject.IsWrappingNullReference)
{
return activeProject;
}
-
activeProject.Dispose();
using (var projects = _vbe.VBProjects)
@@ -143,10 +143,13 @@ private IVBProject GetProject()
protected override bool EvaluateCanExecute(object parameter)
{
+ bool canExecute;
using (var project = GetProject())
{
- return project != null && !project.IsWrappingNullReference && CanExecuteCode(project);
+ canExecute = project != null && !project.IsWrappingNullReference && CanExecuteCode(project);
}
+
+ return canExecute;
}
private bool CanExecuteCode(IVBProject project)
@@ -158,100 +161,115 @@ protected override void OnExecute(object parameter)
{
var parameterIsModuleDeclaration = parameter is ProceduralModuleDeclaration || parameter is ClassModuleDeclaration;
- using (var project = parameter as IVBProject ??
- (parameterIsModuleDeclaration ? ((Declaration) parameter).Project : GetProject()))
+ switch(parameter)
{
- if (project == null || project.IsWrappingNullReference)
- {
- return;
- }
+ case IVBProject project:
+ ExecuteInternal(project, null);
+ break;
+ case Declaration declaration when parameterIsModuleDeclaration:
+ ExecuteInternal(declaration.Project, declaration);
+ break;
+ default:
+ using (var project = GetProject())
+ {
+ ExecuteInternal(project, null);
+ }
+ break;
+ }
+ }
- var settings = _configLoader.LoadConfiguration().UserSettings.UnitTestSettings;
+ private void ExecuteInternal(IVBProject project, Declaration projectDeclaration)
+ {
+ if (project == null || project.IsWrappingNullReference)
+ {
+ return;
+ }
- if (settings.BindingMode == BindingMode.EarlyBinding)
- {
- // FIXME: Push the actual adding of TestModules into UnitTesting, which sidesteps VBEInteraction being inaccessble here
- _interaction.EnsureProjectReferencesUnitTesting(project);
- }
+ var settings = _configLoader.LoadConfiguration().UserSettings.UnitTestSettings;
- try
+ if (settings.BindingMode == BindingMode.EarlyBinding)
+ {
+ // FIXME: Push the actual adding of TestModules into UnitTesting, which sidesteps VBEInteraction being inaccessble here
+ _interaction.EnsureProjectReferencesUnitTesting(project);
+ }
+
+ try
+ {
+ using (var components = project.VBComponents)
{
- using (var components = project.VBComponents)
+ using (var component = components.Add(ComponentType.StandardModule))
{
- using (var component = components.Add(ComponentType.StandardModule))
+ using (var module = component.CodeModule)
{
- using (var module = component.CodeModule)
+ component.Name = GetNextTestModuleName(project);
+
+ var hasOptionExplicit = false;
+ if (module.CountOfLines > 0 && module.CountOfDeclarationLines > 0)
{
- component.Name = GetNextTestModuleName(project);
+ hasOptionExplicit = module.GetLines(1, module.CountOfDeclarationLines)
+ .Contains("Option Explicit");
+ }
- var hasOptionExplicit = false;
- if (module.CountOfLines > 0 && module.CountOfDeclarationLines > 0)
- {
- hasOptionExplicit = module.GetLines(1, module.CountOfDeclarationLines)
- .Contains("Option Explicit");
- }
+ var options = string.Concat(hasOptionExplicit ? string.Empty : "Option Explicit\r\n",
+ "Option Private Module\r\n\r\n");
- var options = string.Concat(hasOptionExplicit ? string.Empty : "Option Explicit\r\n",
- "Option Private Module\r\n\r\n");
+ if (projectDeclaration != null)
+ {
+ var moduleCodeBuilder = new StringBuilder();
+ var declarationsToStub = GetDeclarationsToStub(projectDeclaration);
- if (parameterIsModuleDeclaration)
+ foreach (var declaration in declarationsToStub)
{
- var moduleCodeBuilder = new StringBuilder();
- var declarationsToStub = GetDeclarationsToStub((Declaration) parameter);
+ var name = string.Empty;
- foreach (var declaration in declarationsToStub)
+ switch (declaration.DeclarationType)
{
- var name = string.Empty;
-
- switch (declaration.DeclarationType)
- {
- case DeclarationType.Procedure:
- case DeclarationType.Function:
- name = declaration.IdentifierName;
- break;
- case DeclarationType.PropertyGet:
- name = $"Get{declaration.IdentifierName}";
- break;
- case DeclarationType.PropertyLet:
- name = $"Let{declaration.IdentifierName}";
- break;
- case DeclarationType.PropertySet:
- name = $"Set{declaration.IdentifierName}";
- break;
- }
-
- var stub = AddTestMethodCommand.TestMethodTemplate.Replace(
- AddTestMethodCommand.NamePlaceholder, $"{name}_Test");
- moduleCodeBuilder.AppendLine(stub);
+ case DeclarationType.Procedure:
+ case DeclarationType.Function:
+ name = declaration.IdentifierName;
+ break;
+ case DeclarationType.PropertyGet:
+ name = $"Get{declaration.IdentifierName}";
+ break;
+ case DeclarationType.PropertyLet:
+ name = $"Let{declaration.IdentifierName}";
+ break;
+ case DeclarationType.PropertySet:
+ name = $"Set{declaration.IdentifierName}";
+ break;
}
- module.AddFromString(options + GetTestModule(settings) + moduleCodeBuilder);
+ var stub = AddTestMethodCommand.TestMethodTemplate.Replace(
+ AddTestMethodCommand.NamePlaceholder, $"{name}_Test");
+ moduleCodeBuilder.AppendLine(stub);
}
- else
- {
- var defaultTestMethod = settings.DefaultTestStubInNewModule
- ? AddTestMethodCommand.TestMethodTemplate.Replace(
- AddTestMethodCommand.NamePlaceholder,
- "TestMethod1")
- : string.Empty;
- module.AddFromString(options + GetTestModule(settings) + defaultTestMethod);
- }
+ module.AddFromString(options + GetTestModule(settings) + moduleCodeBuilder);
}
+ else
+ {
+ var defaultTestMethod = settings.DefaultTestStubInNewModule
+ ? AddTestMethodCommand.TestMethodTemplate.Replace(
+ AddTestMethodCommand.NamePlaceholder,
+ "TestMethod1")
+ : string.Empty;
- component.Activate();
+ module.AddFromString(options + GetTestModule(settings) + defaultTestMethod);
+ }
}
+
+ component.Activate();
}
}
- catch (Exception ex)
- {
- _messageBox.Message(TestExplorer.Command_AddTestModule_Error);
- Logger.Warn("Unable to add test module. An exception was thrown.");
- Logger.Warn(ex);
- }
-
- _state.OnParseRequested(this);
}
+ catch (Exception ex)
+ {
+ _messageBox.Message(TestExplorer.Command_AddTestModule_Error);
+ Logger.Warn("Unable to add test module. An exception was thrown.");
+ Logger.Warn(ex);
+ }
+
+ _state.OnParseRequested(this);
}
// FIXME push this into Rubberduck.UnitTesting assembly
diff --git a/Rubberduck.Core/UI/UnitTesting/TestExplorerControl.xaml.cs b/Rubberduck.Core/UI/UnitTesting/TestExplorerControl.xaml.cs
index 47cfb6df12..f3b4342e6c 100644
--- a/Rubberduck.Core/UI/UnitTesting/TestExplorerControl.xaml.cs
+++ b/Rubberduck.Core/UI/UnitTesting/TestExplorerControl.xaml.cs
@@ -50,14 +50,22 @@ private void OnTestCompleted(object sender, EventArgs eventArgs)
});
}
- private bool _isDisposed;
public void Dispose()
{
- if (_isDisposed || DataContext == null) { return; }
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ private bool _isDisposed;
+ protected virtual void Dispose(bool disposing)
+ {
+ if (_isDisposed || !disposing || DataContext == null)
+ {
+ return;
+ }
((TestExplorerViewModel)DataContext).TestCompleted -= OnTestCompleted;
DataContextChanged -= TestExplorerControl_DataContextChanged;
-
_isDisposed = true;
}
}
diff --git a/Rubberduck.Core/UI/UnitTesting/TestExplorerViewModel.cs b/Rubberduck.Core/UI/UnitTesting/TestExplorerViewModel.cs
index 7dc2276436..8d85542a48 100644
--- a/Rubberduck.Core/UI/UnitTesting/TestExplorerViewModel.cs
+++ b/Rubberduck.Core/UI/UnitTesting/TestExplorerViewModel.cs
@@ -236,15 +236,17 @@ private void ExecuteCopyResultsCommand(object parameter)
var htmlResults = ExportFormatter.HtmlClipboardFragment(aResults, title, columnInfos);
var rtfResults = ExportFormatter.RTF(aResults, title);
- var strm1 = ExportFormatter.XmlSpreadsheetNew(aResults, title, columnInfos);
- //Add the formats from richest formatting to least formatting
- _clipboard.AppendStream(DataFormats.GetDataFormat(XML_SPREADSHEET_DATA_FORMAT).Name, strm1);
- _clipboard.AppendString(DataFormats.Rtf, rtfResults);
- _clipboard.AppendString(DataFormats.Html, htmlResults);
- _clipboard.AppendString(DataFormats.CommaSeparatedValue, csvResults);
- //_clipboard.AppendString(DataFormats.UnicodeText, textResults);
-
- _clipboard.Flush();
+ using (var strm1 = ExportFormatter.XmlSpreadsheetNew(aResults, title, columnInfos))
+ {
+ //Add the formats from richest formatting to least formatting
+ _clipboard.AppendStream(DataFormats.GetDataFormat(XML_SPREADSHEET_DATA_FORMAT).Name, strm1);
+ _clipboard.AppendString(DataFormats.Rtf, rtfResults);
+ _clipboard.AppendString(DataFormats.Html, htmlResults);
+ _clipboard.AppendString(DataFormats.CommaSeparatedValue, csvResults);
+ //_clipboard.AppendString(DataFormats.UnicodeText, textResults);
+
+ _clipboard.Flush();
+ }
}
private void ExecuteRunSelectedCategoryTestsCommand(object obj)
diff --git a/Rubberduck.Core/app.config b/Rubberduck.Core/app.config
index 131b118527..f0be996d6a 100644
--- a/Rubberduck.Core/app.config
+++ b/Rubberduck.Core/app.config
@@ -1,9 +1,6 @@
-
-
-
@@ -13,401 +10,5 @@
-
-
-
-
-
- R
- true
- true
- false
- true
- CodePaneRefactorRenameCommand
-
-
-
-
-
-
- F
- true
- true
- false
- true
- RefactorEncapsulateFieldCommand
-
-
-
-
-
-
- M
- true
- true
- false
- true
- RefactorExtractMethodCommand
-
-
-
-
-
-
- C
- true
- true
- false
- true
- RefactorMoveCloserToUsageCommand
-
-
-
-
-
-
- R
- true
- false
- false
- true
- CodeExplorerCommand
-
-
-
-
-
-
- E
- true
- true
- false
- true
- ExportAllCommand
-
-
-
-
-
-
- T
- true
- false
- false
- true
- FindSymbolCommand
-
-
-
-
-
-
- M
- true
- false
- false
- true
- IndentCurrentModuleCommand
-
-
-
-
-
-
- P
- true
- false
- false
- true
- IndentCurrentProcedureCommand
-
-
-
-
-
-
- I
- true
- true
- false
- true
- InspectionResultsCommand
-
-
-
-
-
-
- `
- true
- false
- false
- true
- ReparseCommand
-
-
-
-
-
-
- T
- true
- true
- false
- true
- TestExplorerCommand
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- false
- false
- false
- false
- true
- false
- false
-
-
-
-
-
-
- LateBinding
- StrictAssert
- true
- true
- false
-
-
-
-
-
-
-
- true
- true
- true
- false
- false
- 10
- false
- 0
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- true
-
-
-
-
-
-
-
- None
- 25
-
-
-
-
-
-
-
-
diff --git a/Rubberduck.Deployment/BuildRegistryScript.ps1 b/Rubberduck.Deployment/BuildRegistryScript.ps1
index 059c12af18..ea17f679e3 100644
--- a/Rubberduck.Deployment/BuildRegistryScript.ps1
+++ b/Rubberduck.Deployment/BuildRegistryScript.ps1
@@ -113,7 +113,31 @@ try
if(!$result)
{
Write-Warning "VSSetup not installed; extracting...";
- Expand-Archive "$projectDir\OleWoo\VSSetup.zip" "$([Environment]::GetFolderPath("MyDocuments"))\WindowsPowerShell\Modules\VSSetup" -Force
+ $moduleDirPath = "$([Environment]::GetFolderPath("MyDocuments"))\WindowsPowerShell";
+ if(!(Test-Path -Path $moduleDirPath -PathType Container))
+ {
+ Write-Warning "WindowsPowerShell directory not found in user's documents. Creating.";
+ New-Item -Path $moduleDirPath -ItemType Directory;
+ }
+ $moduleDirPath += "\Modules";
+ if(!(Test-Path -Path $moduleDirPath -PathType Container))
+ {
+ Write-Warning "WindowsPowerShell\Modules directory not found in user's documents. Creating.";
+ New-Item -Path $moduleDirPath -ItemType Directory;
+ }
+ $moduleDirPath += "\VSSetup";
+ if(!(Test-Path -Path $moduleDirPath -PathType Container))
+ {
+ Write-Warning "WindowsPowerShell\Modules\VSSetup directory not found in user's documents. Creating.";
+ New-Item -Path $moduleDirPath -ItemType Directory;
+ }
+ # Sanity check
+ if(!(Test-Path -Path $moduleDirPath -PathType Container))
+ {
+ Write-Error "WindowsPowerShell\Modules\VSSetup directory still not found in user's documents after attempt to create it. Cannot continue";
+ throw [System.IO.DirectoryNotFoundException] "Cannot create or locate the directory at path '$moduleDirPath'";
+ }
+ Expand-Archive "$projectDir\OleWoo\VSSetup.zip" $moduleDirPath -Force;
}
try {
diff --git a/Rubberduck.Deployment/IdlGeneration/IdlGenerator.cs b/Rubberduck.Deployment/IdlGeneration/IdlGenerator.cs
index dc7ec79dfc..2b5873294f 100644
--- a/Rubberduck.Deployment/IdlGeneration/IdlGenerator.cs
+++ b/Rubberduck.Deployment/IdlGeneration/IdlGenerator.cs
@@ -39,6 +39,9 @@ public string GenerateIdl(Assembly assembly)
var owLib = new OWTypeLib(lib);
owLib.Listeners.Add(new IdlListener());
+ formatter.AddString("midl_pragma warning( disable: 2400 2401 ) ");
+ formatter.NewLine();
+
owLib.BuildIDLInto(formatter);
return formatter.ToString();
}
diff --git a/Rubberduck.Deployment/Rubberduck.Deployment.csproj b/Rubberduck.Deployment/Rubberduck.Deployment.csproj
index eebff5573e..442301af8a 100644
--- a/Rubberduck.Deployment/Rubberduck.Deployment.csproj
+++ b/Rubberduck.Deployment/Rubberduck.Deployment.csproj
@@ -1,5 +1,5 @@
-
+
Rubberduck.Deployment
Copyright © 2018
@@ -17,7 +17,10 @@
+
+
+
@@ -29,6 +32,11 @@
+
+
+ content/net40/*
+
+
OleWoo\olewoo.dll
diff --git a/Rubberduck.Interaction/Rubberduck.Interaction.csproj b/Rubberduck.Interaction/Rubberduck.Interaction.csproj
index 2b09548001..ca639854aa 100644
--- a/Rubberduck.Interaction/Rubberduck.Interaction.csproj
+++ b/Rubberduck.Interaction/Rubberduck.Interaction.csproj
@@ -1,5 +1,5 @@
-
+
Copyright © 2018
Rubberduck.Interaction
diff --git a/Rubberduck.Main/ComClientLibrary/UI/DockableWindowHost.cs b/Rubberduck.Main/ComClientLibrary/UI/DockableWindowHost.cs
index 3eea3c88aa..e38ecd3d15 100644
--- a/Rubberduck.Main/ComClientLibrary/UI/DockableWindowHost.cs
+++ b/Rubberduck.Main/ComClientLibrary/UI/DockableWindowHost.cs
@@ -1,6 +1,7 @@
using System;
using System.ComponentModel;
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
@@ -81,6 +82,8 @@ public interface IDockableWindowHost
//Nothing breaks because we declare a ProgId
// ReSharper disable once InconsistentNaming
//Underscores make classes invisible to VB6 object explorer
+ [SuppressMessage("Microsoft.Design", "CA1049")]
+ [SuppressMessage("Microsoft.Design", "CA1001")] //This should *never* have Dispose called on it. See comment block above.
public class _DockableWindowHost : COM_IOleObject, COM_IOleInPlaceObject, COM_IOleWindow, IDockableWindowHost
{
public static string RegisteredProgId => RubberduckProgId.DockableWindowHostProgId;
diff --git a/Rubberduck.Main/Root/RubberduckIoCInstaller.cs b/Rubberduck.Main/Root/RubberduckIoCInstaller.cs
index 5960e986e2..0d49406c46 100644
--- a/Rubberduck.Main/Root/RubberduckIoCInstaller.cs
+++ b/Rubberduck.Main/Root/RubberduckIoCInstaller.cs
@@ -9,6 +9,7 @@
using Castle.MicroKernel.Resolvers.SpecializedResolvers;
using Castle.MicroKernel.SubSystems.Configuration;
using Castle.Windsor;
+using Rubberduck.AddRemoveReferences;
using Rubberduck.ComClientLibrary.UnitTesting;
using Rubberduck.Common;
using Rubberduck.Common.Hotkeys;
@@ -52,6 +53,7 @@
using Rubberduck.VBEditor.SourceCodeHandling;
using Rubberduck.Parsing.VBA.DeclarationCaching;
using Rubberduck.Parsing.VBA.Parsing.ParsingExceptions;
+using Rubberduck.UI.AddRemoveReferences;
namespace Rubberduck.Root
{
@@ -89,11 +91,10 @@ public void Install(IWindsorContainer container, IConfigurationStore store)
container.Register(Component.For()
.UsingFactoryMethod(() => Assembly.GetExecutingAssembly().GetName().Version)
.LifestyleSingleton());
-
container.Register(Component.For()
.ImplementedBy()
.LifestyleSingleton());
- container.Register(Component.For()
+ container.Register(Component.For()
.ImplementedBy()
.LifestyleSingleton());
container.Register(Component.For()
@@ -113,12 +114,23 @@ public void Install(IWindsorContainer container, IConfigurationStore store)
RegisterParsingEngine(container);
RegisterTypeLibApi(container);
+ container.Register(Component.For()
+ .ImplementedBy()
+ .LifestyleSingleton());
+
container.Register(Component.For()
.LifestyleSingleton());
container.Register(Component.For()
.ImplementedBy()
.LifestyleSingleton());
+ container.Register(Component.For()
+ .ImplementedBy()
+ .LifestyleSingleton());
+ container.Register(Component.For()
+ .ImplementedBy()
+ .LifestyleSingleton());
+
RegisterRefactoringDialogs(container);
container.Register(Component.For()
@@ -282,6 +294,9 @@ private void RegisterSpecialFactories(IWindsorContainer container)
container.Register(Component.For()
.ImplementedBy()
.LifestyleSingleton());
+ container.Register(Component.For()
+ .ImplementedBy()
+ .LifestyleSingleton());
}
private void RegisterQuickFixes(IWindsorContainer container, Assembly[] assembliesToRegister)
diff --git a/Rubberduck.Main/Rubberduck.Main.csproj b/Rubberduck.Main/Rubberduck.Main.csproj
index 0d8eb19ecf..80d1356f85 100644
--- a/Rubberduck.Main/Rubberduck.Main.csproj
+++ b/Rubberduck.Main/Rubberduck.Main.csproj
@@ -1,5 +1,5 @@
-
+
Rubberduck
Rubberduck
diff --git a/Rubberduck.Main/VbeProvider.cs b/Rubberduck.Main/VbeProvider.cs
index c5b8f996e5..e85e5e1ae0 100644
--- a/Rubberduck.Main/VbeProvider.cs
+++ b/Rubberduck.Main/VbeProvider.cs
@@ -24,7 +24,6 @@ internal static void Initialize(IVBE vbe)
internal static void Terminate()
{
- Vbe.Dispose();
Vbe = null;
VbeRuntime = null;
}
diff --git a/Rubberduck.Parsing/Annotations/AnnotationService.cs b/Rubberduck.Parsing/Annotations/IdentifierAnnotationService.cs
similarity index 64%
rename from Rubberduck.Parsing/Annotations/AnnotationService.cs
rename to Rubberduck.Parsing/Annotations/IdentifierAnnotationService.cs
index df2ebd1df7..770a04fbe9 100644
--- a/Rubberduck.Parsing/Annotations/AnnotationService.cs
+++ b/Rubberduck.Parsing/Annotations/IdentifierAnnotationService.cs
@@ -1,16 +1,15 @@
-using Rubberduck.Parsing.Symbols;
-using Rubberduck.VBEditor;
+using Rubberduck.VBEditor;
using System.Collections.Generic;
using System.Linq;
using Rubberduck.Parsing.VBA.DeclarationCaching;
namespace Rubberduck.Parsing.Annotations
{
- public sealed class AnnotationService
+ public sealed class IdentifierAnnotationService
{
private readonly DeclarationFinder _declarationFinder;
- public AnnotationService(DeclarationFinder declarationFinder)
+ public IdentifierAnnotationService(DeclarationFinder declarationFinder)
{
_declarationFinder = declarationFinder;
}
@@ -22,13 +21,15 @@ public IEnumerable FindAnnotations(QualifiedModuleName module, int
// VBE 1-based indexing
for (var currentLine = line - 1; currentLine >= 1; currentLine--)
{
+ //Identifier annotation sections end at the first line above without an identifier annotation.
if (!moduleAnnotations.Any(annotation => annotation.QualifiedSelection.Selection.StartLine <= currentLine
- && annotation.QualifiedSelection.Selection.EndLine >= currentLine))
+ && annotation.QualifiedSelection.Selection.EndLine >= currentLine
+ && annotation.AnnotationType.HasFlag(AnnotationType.IdentifierAnnotation)))
{
break;
}
- var annotationsStartingOnCurrentLine = moduleAnnotations.Where(a => a.QualifiedSelection.Selection.StartLine == currentLine);
+ var annotationsStartingOnCurrentLine = moduleAnnotations.Where(a => a.QualifiedSelection.Selection.StartLine == currentLine && a.AnnotationType.HasFlag(AnnotationType.IdentifierAnnotation));
annotations.AddRange(annotationsStartingOnCurrentLine);
}
diff --git a/Rubberduck.Parsing/ComReflection/ComInterface.cs b/Rubberduck.Parsing/ComReflection/ComInterface.cs
index 723ef2a09f..c9d60917bd 100644
--- a/Rubberduck.Parsing/ComReflection/ComInterface.cs
+++ b/Rubberduck.Parsing/ComReflection/ComInterface.cs
@@ -80,7 +80,7 @@ private void GetImplementedInterfaces(ITypeInfo info, TYPEATTR typeAttr)
info.GetRefTypeInfo(href, out ITypeInfo implemented);
implemented.GetTypeAttr(out IntPtr attribPtr);
- using (DisposalActionContainer.Create(attribPtr, info.ReleaseTypeAttr))
+ using (DisposalActionContainer.Create(attribPtr, implemented.ReleaseTypeAttr))
{
var attribs = Marshal.PtrToStructure(attribPtr);
diff --git a/Rubberduck.Parsing/ComReflection/XmlComProjectSerializer.cs b/Rubberduck.Parsing/ComReflection/XmlComProjectSerializer.cs
index f5f6bf0c4c..5b1e6a9986 100644
--- a/Rubberduck.Parsing/ComReflection/XmlComProjectSerializer.cs
+++ b/Rubberduck.Parsing/ComReflection/XmlComProjectSerializer.cs
@@ -1,15 +1,21 @@
using System;
+using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Runtime.Serialization;
using System.Xml;
+using Rubberduck.SettingsProvider;
using Rubberduck.VBEditor;
namespace Rubberduck.Parsing.ComReflection
{
public class XmlComProjectSerializer : IComProjectSerializationProvider
{
- public static readonly string DefaultSerializationPath =
- Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Rubberduck", "Declarations");
+ public readonly string DefaultSerializationPath;
+
+ public XmlComProjectSerializer(IPersistancePathProvider pathProvider)
+ {
+ DefaultSerializationPath = pathProvider.DataFolderPath("Declarations");
+ }
private static readonly XmlReaderSettings ReaderSettings = new XmlReaderSettings
{
@@ -46,6 +52,7 @@ public bool SerializedVersionExists(ReferenceInfo reference)
return File.Exists(testFile);
}
+ [SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times")] //This is fine. XmlWriter disposes the FileStream, but calling twice is a NOP.
public void SerializeProject(ComProject project)
{
var filepath = Path.Combine(Target, FileName(project));
diff --git a/Rubberduck.Parsing/Grammar/VBAParser.g4 b/Rubberduck.Parsing/Grammar/VBAParser.g4
index a9b91491c2..d727f4cd14 100644
--- a/Rubberduck.Parsing/Grammar/VBAParser.g4
+++ b/Rubberduck.Parsing/Grammar/VBAParser.g4
@@ -549,9 +549,9 @@ upperBound : constantExpression;
constantExpression : expression;
-variableStmt : (DIM | STATIC | visibility) whiteSpace (WITHEVENTS whiteSpace)? variableListStmt;
+variableStmt : (DIM | STATIC | visibility) whiteSpace variableListStmt;
variableListStmt : variableSubStmt (whiteSpace? COMMA whiteSpace? variableSubStmt)*;
-variableSubStmt : identifier (whiteSpace? LPAREN whiteSpace? (subscripts whiteSpace?)? RPAREN)? (whiteSpace asTypeClause)?;
+variableSubStmt : (WITHEVENTS whiteSpace)? identifier (whiteSpace? LPAREN whiteSpace? (subscripts whiteSpace?)? RPAREN)? (whiteSpace asTypeClause)?;
whileWendStmt :
WHILE whiteSpace expression endOfStatement
@@ -898,7 +898,7 @@ statementKeyword :
;
endOfLine :
- whiteSpace? NEWLINE whiteSpace?
+ whiteSpace? NEWLINE
| whiteSpace? commentOrAnnotation
;
diff --git a/Rubberduck.Parsing/Inspections/Abstract/IQuickFix.cs b/Rubberduck.Parsing/Inspections/Abstract/IQuickFix.cs
index 89f3a91a0e..f84cc8c814 100644
--- a/Rubberduck.Parsing/Inspections/Abstract/IQuickFix.cs
+++ b/Rubberduck.Parsing/Inspections/Abstract/IQuickFix.cs
@@ -1,11 +1,13 @@
using System;
using System.Collections.Generic;
+using Rubberduck.Parsing.Rewriter;
+using Rubberduck.Parsing.VBA.Parsing;
namespace Rubberduck.Parsing.Inspections.Abstract
{
public interface IQuickFix
{
- void Fix(IInspectionResult result);
+ void Fix(IInspectionResult result, IRewriteSession rewriteSession);
string Description(IInspectionResult result);
bool CanFixInProcedure { get; }
@@ -13,6 +15,7 @@ public interface IQuickFix
bool CanFixInProject { get; }
IReadOnlyCollection SupportedInspections { get; }
+ CodeKind TargetCodeKind { get; }
void RegisterInspections(params Type[] inspections);
void RemoveInspections(params Type[] inspections);
diff --git a/Rubberduck.Parsing/Rewriter/AttributesRewriteSession.cs b/Rubberduck.Parsing/Rewriter/AttributesRewriteSession.cs
new file mode 100644
index 0000000000..9fd0d46774
--- /dev/null
+++ b/Rubberduck.Parsing/Rewriter/AttributesRewriteSession.cs
@@ -0,0 +1,47 @@
+using System;
+using Rubberduck.Parsing.VBA;
+using Rubberduck.Parsing.VBA.Parsing;
+using Rubberduck.VBEditor;
+
+namespace Rubberduck.Parsing.Rewriter
+{
+ public class AttributesRewriteSession : RewriteSessionBase
+ {
+ private readonly IParseManager _parseManager;
+
+ public AttributesRewriteSession(IParseManager parseManager, IRewriterProvider rewriterProvider,
+ Func rewritingAllowed)
+ : base(rewriterProvider, rewritingAllowed)
+ {
+ _parseManager = parseManager;
+ }
+
+ public override CodeKind TargetCodeKind => CodeKind.AttributesCode;
+
+ protected override IExecutableModuleRewriter ModuleRewriter(QualifiedModuleName module)
+ {
+ return RewriterProvider.AttributesModuleRewriter(module);
+ }
+
+ protected override bool TryRewriteInternal()
+ {
+ //The suspension ensures that only one parse gets executed instead of two for each rewritten module.
+ var result = _parseManager.OnSuspendParser(this, new[] {ParserState.Ready}, ExecuteAllRewriters);
+ if(result != SuspensionResult.Completed)
+ {
+ Logger.Warn($"Rewriting attribute modules did not succeed. suspension result = {result}");
+ return false;
+ }
+
+ return true;
+ }
+
+ private void ExecuteAllRewriters()
+ {
+ foreach (var rewriter in CheckedOutModuleRewriters.Values)
+ {
+ rewriter.Rewrite();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Rubberduck.Parsing/Rewriter/CodePaneRewriteSession.cs b/Rubberduck.Parsing/Rewriter/CodePaneRewriteSession.cs
new file mode 100644
index 0000000000..db2edab349
--- /dev/null
+++ b/Rubberduck.Parsing/Rewriter/CodePaneRewriteSession.cs
@@ -0,0 +1,37 @@
+using System;
+using Rubberduck.Parsing.VBA;
+using Rubberduck.Parsing.VBA.Parsing;
+using Rubberduck.VBEditor;
+
+namespace Rubberduck.Parsing.Rewriter
+{
+ public class CodePaneRewriteSession : RewriteSessionBase
+ {
+ private readonly IParseManager _parseManager;
+
+ public CodePaneRewriteSession(IParseManager parseManager, IRewriterProvider rewriterProvider,
+ Func rewritingAllowed)
+ : base(rewriterProvider, rewritingAllowed)
+ {
+ _parseManager = parseManager;
+ }
+
+ public override CodeKind TargetCodeKind => CodeKind.CodePaneCode;
+
+ protected override IExecutableModuleRewriter ModuleRewriter(QualifiedModuleName module)
+ {
+ return RewriterProvider.CodePaneModuleRewriter(module);
+ }
+
+ protected override bool TryRewriteInternal()
+ {
+ foreach (var rewriter in CheckedOutModuleRewriters.Values)
+ {
+ rewriter.Rewrite();
+ }
+ _parseManager.OnParseRequested(this);
+
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Rubberduck.Parsing/Rewriter/IExecutableModuleRewriter.cs b/Rubberduck.Parsing/Rewriter/IExecutableModuleRewriter.cs
new file mode 100644
index 0000000000..c7afaf2dc4
--- /dev/null
+++ b/Rubberduck.Parsing/Rewriter/IExecutableModuleRewriter.cs
@@ -0,0 +1,10 @@
+namespace Rubberduck.Parsing.Rewriter
+{
+ public interface IExecutableModuleRewriter : IModuleRewriter
+ {
+ ///
+ /// Rewrites the entire module / applies all changes.
+ ///
+ void Rewrite();
+ }
+}
\ No newline at end of file
diff --git a/Rubberduck.Parsing/Rewriter/IModuleRewriter.cs b/Rubberduck.Parsing/Rewriter/IModuleRewriter.cs
index 393a53bc98..4f2a1857e8 100644
--- a/Rubberduck.Parsing/Rewriter/IModuleRewriter.cs
+++ b/Rubberduck.Parsing/Rewriter/IModuleRewriter.cs
@@ -1,4 +1,4 @@
-using Antlr4.Runtime;
+using Antlr4.Runtime;
using Antlr4.Runtime.Misc;
using Antlr4.Runtime.Tree;
using Rubberduck.Parsing.Symbols;
@@ -10,84 +10,91 @@ public interface IModuleRewriter
bool IsDirty { get; }
///
- /// Rewrites the entire module / applies all changes.
- ///
- void Rewrite();
-
- ///
- /// Removes all tokens for specified . Use method to apply changes.
+ /// Removes all tokens for specified . Use to get the changed module code.
///
/// The to remove.
/// Removes a line that would be left empty by the removal of the declaration.
void Remove(Declaration target);
///
- /// Removes all tokens in specified context. Use method to apply changes.
+ /// Removes all tokens in specified context. Use to get the changed module code.
///
/// The to remove.
/// Removes a line that would be left empty by the removal of the identifier reference token.
void Remove(ParserRuleContext target);
///
- /// Removes all tokens for specified . Use method to apply changes.
+ /// Removes all tokens for specified . Use to get the changed module code.
///
/// The to remove.
/// Removes a line that would be left empty by the removal of the identifier reference token.
void Remove(IToken target);
///
- /// Removes all tokens for specified . Use method to apply changes.
+ /// Removes all tokens for specified . Use to get the changed module code.
///
/// The to remove.
/// Removes a line that would be left empty by the removal of the identifier reference token.
void Remove(ITerminalNode target);
+ ///
+ /// Removes all tokens for specified . Use to get the changed module code.
+ ///
+ /// The to remove.
+ /// Removes a line that would be left empty by the removal of the identifier reference token.
+ void Remove(IParseTree target);
///
- /// Removes all tokens from the start of the first node to the end of the second node.
+ /// Removes all tokens from the start of the first node to the end of the second node. Use to get the changed module code.
///
/// The start index to remove.
/// The end index to remove.
void RemoveRange(int start, int stop);
///
- /// Replaces all tokens for specified with specified content. Use method to apply changes.
+ /// Replaces all tokens for specified with specified content. Use to get the changed module code.
///
/// The to replace.
/// The literal replacement for the declaration.
/// Useful for adding/removing e.g. access modifiers.
void Replace(Declaration target, string content);
///
- /// Replaces all tokens for specified with specified content. Use method to apply changes.
+ /// Replaces all tokens for specified with specified content. Use to get the changed module code.
///
/// The to replace.
/// The literal replacement for the expression.
void Replace(ParserRuleContext target, string content);
///
- /// Replaces specified token with specified content. Use method to apply changes.
+ /// Replaces specified token with specified content. Use to get the changed module code.
///
/// The to replace.
/// The literal replacement for the expression.
void Replace(IToken token, string content);
///
- /// Replaces specified token with specified content. Use method to apply changes.
+ /// Replaces specified token with specified content. Use to get the changed module code.
///
/// The to replace.
/// The literal replacement for the expression.
void Replace(ITerminalNode target, string content);
+ ///
+ /// Replaces specified token with specified content. Use to get the changed module code.
+ ///
+ /// The to replace.
+ /// The literal replacement for the expression.
+ void Replace(IParseTree target, string content);
///
- /// Replaces specified interval with specified content. Use method to apply changes.
+ /// Replaces specified interval with specified content. Use to get the changed module code.
///
/// The to replace.
/// The literal replacement for the expression.
void Replace(Interval tokenInterval, string content);
///
- /// Inserts specified content before the specified token index in the module. Use method to apply changes.
+ /// Inserts specified content before the specified token index in the module. Use to get the changed module code.
///
/// The index of the insertion point in the module's lexer token stream.
/// The literal content to insert.
void InsertBefore(int tokenIndex, string content);
///
- /// Inserts specified content after the specified token index in the module. Use method to apply changes.
+ /// Inserts specified content after the specified token index in the module. Use to get the changed module code.
///
/// The index of the insertion point in the module's lexer token stream.
/// The literal content to insert.
diff --git a/Rubberduck.Parsing/Rewriter/IModuleRewriterFactory.cs b/Rubberduck.Parsing/Rewriter/IModuleRewriterFactory.cs
index 3db5967268..5721cc3f25 100644
--- a/Rubberduck.Parsing/Rewriter/IModuleRewriterFactory.cs
+++ b/Rubberduck.Parsing/Rewriter/IModuleRewriterFactory.cs
@@ -5,7 +5,7 @@ namespace Rubberduck.Parsing.Rewriter
{
public interface IModuleRewriterFactory
{
- IModuleRewriter CodePaneRewriter(QualifiedModuleName module, ITokenStream tokenStream);
- IModuleRewriter AttributesRewriter(QualifiedModuleName module, ITokenStream tokenStream);
+ IExecutableModuleRewriter CodePaneRewriter(QualifiedModuleName module, ITokenStream tokenStream);
+ IExecutableModuleRewriter AttributesRewriter(QualifiedModuleName module, ITokenStream tokenStream);
}
}
diff --git a/Rubberduck.Parsing/Rewriter/IRewriteSession.cs b/Rubberduck.Parsing/Rewriter/IRewriteSession.cs
new file mode 100644
index 0000000000..808b08993a
--- /dev/null
+++ b/Rubberduck.Parsing/Rewriter/IRewriteSession.cs
@@ -0,0 +1,14 @@
+using Rubberduck.Parsing.VBA.Parsing;
+using Rubberduck.VBEditor;
+
+namespace Rubberduck.Parsing.Rewriter
+{
+ public interface IRewriteSession
+ {
+ IModuleRewriter CheckOutModuleRewriter(QualifiedModuleName module);
+ bool TryRewrite();
+ bool IsInvalidated { get; }
+ void Invalidate();
+ CodeKind TargetCodeKind { get; }
+ }
+}
\ No newline at end of file
diff --git a/Rubberduck.Parsing/Rewriter/IRewriteSessionFactory.cs b/Rubberduck.Parsing/Rewriter/IRewriteSessionFactory.cs
new file mode 100644
index 0000000000..d4047a36d2
--- /dev/null
+++ b/Rubberduck.Parsing/Rewriter/IRewriteSessionFactory.cs
@@ -0,0 +1,10 @@
+using System;
+
+namespace Rubberduck.Parsing.Rewriter
+{
+ public interface IRewriteSessionFactory
+ {
+ IRewriteSession CodePaneSession(Func rewritingAllowed);
+ IRewriteSession AttributesSession(Func rewritingAllowed);
+ }
+}
\ No newline at end of file
diff --git a/Rubberduck.Parsing/Rewriter/IRewriterProvider.cs b/Rubberduck.Parsing/Rewriter/IRewriterProvider.cs
new file mode 100644
index 0000000000..2766c114e5
--- /dev/null
+++ b/Rubberduck.Parsing/Rewriter/IRewriterProvider.cs
@@ -0,0 +1,10 @@
+using Rubberduck.VBEditor;
+
+namespace Rubberduck.Parsing.Rewriter
+{
+ public interface IRewriterProvider
+ {
+ IExecutableModuleRewriter CodePaneModuleRewriter(QualifiedModuleName module);
+ IExecutableModuleRewriter AttributesModuleRewriter(QualifiedModuleName module);
+ }
+}
\ No newline at end of file
diff --git a/Rubberduck.Parsing/Rewriter/IRewritingManager.cs b/Rubberduck.Parsing/Rewriter/IRewritingManager.cs
new file mode 100644
index 0000000000..150b8adefe
--- /dev/null
+++ b/Rubberduck.Parsing/Rewriter/IRewritingManager.cs
@@ -0,0 +1,9 @@
+namespace Rubberduck.Parsing.Rewriter
+{
+ public interface IRewritingManager
+ {
+ IRewriteSession CheckOutCodePaneSession();
+ IRewriteSession CheckOutAttributesSession();
+ void InvalidateAllSessions();
+ }
+}
\ No newline at end of file
diff --git a/Rubberduck.Parsing/Rewriter/ITokenStreamCache.cs b/Rubberduck.Parsing/Rewriter/ITokenStreamCache.cs
new file mode 100644
index 0000000000..eedd6b8549
--- /dev/null
+++ b/Rubberduck.Parsing/Rewriter/ITokenStreamCache.cs
@@ -0,0 +1,11 @@
+using Antlr4.Runtime;
+using Rubberduck.VBEditor;
+
+namespace Rubberduck.Parsing.Rewriter
+{
+ public interface ITokenStreamCache
+ {
+ ITokenStream CodePaneTokenStream(QualifiedModuleName module);
+ ITokenStream AttributesTokenStream(QualifiedModuleName module);
+ }
+}
\ No newline at end of file
diff --git a/Rubberduck.Parsing/Rewriter/ModuleRewriter.cs b/Rubberduck.Parsing/Rewriter/ModuleRewriter.cs
index e54b52b567..4b2314a0c8 100644
--- a/Rubberduck.Parsing/Rewriter/ModuleRewriter.cs
+++ b/Rubberduck.Parsing/Rewriter/ModuleRewriter.cs
@@ -11,7 +11,7 @@
namespace Rubberduck.Parsing.Rewriter
{
- public class ModuleRewriter : IModuleRewriter
+ public class ModuleRewriter : IExecutableModuleRewriter
{
private readonly QualifiedModuleName _module;
private readonly ISourceCodeHandler _sourceCodeHandler;
@@ -73,6 +73,22 @@ public void Remove(IToken target)
_rewriter.Delete(target);
}
+ public void Remove(IParseTree target)
+ {
+ switch (target)
+ {
+ case ITerminalNode terminalNode:
+ Remove(terminalNode);
+ break;
+ case ParserRuleContext context:
+ Remove(context);
+ break;
+ default:
+ //It should be impossible to end up here.
+ throw new NotSupportedException();
+ }
+ }
+
public void RemoveRange(int start, int stop)
{
_rewriter.Delete(start, stop);
@@ -98,6 +114,22 @@ public void Replace(ITerminalNode target, string content)
_rewriter.Replace(target.Symbol.TokenIndex, content);
}
+ public void Replace(IParseTree target, string content)
+ {
+ switch (target)
+ {
+ case ITerminalNode terminalNode:
+ Replace(terminalNode, content);
+ break;
+ case ParserRuleContext context:
+ Replace(context, content);
+ break;
+ default:
+ //It should be impossible to end up here.
+ throw new NotSupportedException();
+ }
+ }
+
public void Replace(Interval tokenInterval, string content)
{
_rewriter.Replace(tokenInterval.a, tokenInterval.b, content);
diff --git a/Rubberduck.Parsing/Rewriter/ModuleRewriterFactory.cs b/Rubberduck.Parsing/Rewriter/ModuleRewriterFactory.cs
index 7968186d3b..4fb978fffa 100644
--- a/Rubberduck.Parsing/Rewriter/ModuleRewriterFactory.cs
+++ b/Rubberduck.Parsing/Rewriter/ModuleRewriterFactory.cs
@@ -20,12 +20,12 @@ public ModuleRewriterFactory(ISourceCodeHandler codePaneSourceCodeHandler, ISour
_attributesSourceCodeHandler = attributesSourceCodeHandler;
}
- public IModuleRewriter CodePaneRewriter(QualifiedModuleName module, ITokenStream tokenStream)
+ public IExecutableModuleRewriter CodePaneRewriter(QualifiedModuleName module, ITokenStream tokenStream)
{
return new ModuleRewriter(module, tokenStream, _codePaneSourceCodeHandlerr);
}
- public IModuleRewriter AttributesRewriter(QualifiedModuleName module, ITokenStream tokenStream)
+ public IExecutableModuleRewriter AttributesRewriter(QualifiedModuleName module, ITokenStream tokenStream)
{
return new ModuleRewriter(module, tokenStream, _attributesSourceCodeHandler);
}
diff --git a/Rubberduck.Parsing/Rewriter/RewriteSessionBase.cs b/Rubberduck.Parsing/Rewriter/RewriteSessionBase.cs
new file mode 100644
index 0000000000..21046c3731
--- /dev/null
+++ b/Rubberduck.Parsing/Rewriter/RewriteSessionBase.cs
@@ -0,0 +1,88 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using NLog;
+using Rubberduck.Parsing.VBA.Parsing;
+using Rubberduck.VBEditor;
+
+namespace Rubberduck.Parsing.Rewriter
+{
+ public abstract class RewriteSessionBase : IRewriteSession
+ {
+ protected readonly IDictionary CheckedOutModuleRewriters = new Dictionary();
+ protected readonly IRewriterProvider RewriterProvider;
+
+ private readonly Func _rewritingAllowed;
+
+ protected readonly Logger Logger = LogManager.GetCurrentClassLogger();
+ private readonly object _invalidationLockObject = new object();
+
+ public abstract CodeKind TargetCodeKind { get; }
+
+ protected RewriteSessionBase(IRewriterProvider rewriterProvider, Func rewritingAllowed)
+ {
+ RewriterProvider = rewriterProvider;
+ _rewritingAllowed = rewritingAllowed;
+ }
+
+
+ public IModuleRewriter CheckOutModuleRewriter(QualifiedModuleName module)
+ {
+ if (CheckedOutModuleRewriters.TryGetValue(module, out var rewriter))
+ {
+ return rewriter;
+ }
+
+ rewriter = ModuleRewriter(module);
+ CheckedOutModuleRewriters.Add(module, rewriter);
+ return rewriter;
+ }
+
+ protected abstract IExecutableModuleRewriter ModuleRewriter(QualifiedModuleName module);
+
+ public bool TryRewrite()
+ {
+ if (!CheckedOutModuleRewriters.Any())
+ {
+ return false;
+ }
+
+ //This is thread-safe because, once invalidated, there is no way back.
+ if (IsInvalidated)
+ {
+ Logger.Warn("Tried to execute Rewrite on a RewriteSession that was already invalidated.");
+ return false;
+ }
+
+ if (!_rewritingAllowed(this))
+ {
+ Logger.Debug("Tried to execute Rewrite on a RewriteSession when rewriting was no longer allowed.");
+ return false;
+ }
+
+ return TryRewriteInternal();
+ }
+
+ protected abstract bool TryRewriteInternal();
+
+ private bool _isInvalidated = false;
+ public bool IsInvalidated
+ {
+ get
+ {
+ lock (_invalidationLockObject)
+ {
+ return _isInvalidated;
+ }
+ }
+ }
+
+ public void Invalidate()
+ {
+ lock(_invalidationLockObject)
+ {
+ _isInvalidated = true;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Rubberduck.Parsing/Rewriter/RewriteSessionFactory.cs b/Rubberduck.Parsing/Rewriter/RewriteSessionFactory.cs
new file mode 100644
index 0000000000..d0f81ddbd1
--- /dev/null
+++ b/Rubberduck.Parsing/Rewriter/RewriteSessionFactory.cs
@@ -0,0 +1,27 @@
+using System;
+using Rubberduck.Parsing.VBA;
+
+namespace Rubberduck.Parsing.Rewriter
+{
+ public class RewriteSessionFactory : IRewriteSessionFactory
+ {
+ private readonly RubberduckParserState _state;
+ private readonly IRewriterProvider _rewriterProvider;
+
+ public RewriteSessionFactory(RubberduckParserState state, IRewriterProvider rewriterProvider)
+ {
+ _state = state;
+ _rewriterProvider = rewriterProvider;
+ }
+
+ public IRewriteSession CodePaneSession(Func rewritingAllowed)
+ {
+ return new CodePaneRewriteSession(_state, _rewriterProvider, rewritingAllowed);
+ }
+
+ public IRewriteSession AttributesSession(Func rewritingAllowed)
+ {
+ return new AttributesRewriteSession(_state, _rewriterProvider, rewritingAllowed);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Rubberduck.Parsing/Rewriter/RewriterProvider.cs b/Rubberduck.Parsing/Rewriter/RewriterProvider.cs
new file mode 100644
index 0000000000..23df1fb818
--- /dev/null
+++ b/Rubberduck.Parsing/Rewriter/RewriterProvider.cs
@@ -0,0 +1,29 @@
+using Rubberduck.VBEditor;
+
+namespace Rubberduck.Parsing.Rewriter
+{
+ public class RewriterProvider : IRewriterProvider
+ {
+ private ITokenStreamCache _tokenStreamCache;
+ private IModuleRewriterFactory _rewriterFactory;
+
+ public RewriterProvider(ITokenStreamCache tokenStreamCache, IModuleRewriterFactory rewriterFactory)
+ {
+ _tokenStreamCache = tokenStreamCache;
+ _rewriterFactory = rewriterFactory;
+ }
+
+
+ public IExecutableModuleRewriter CodePaneModuleRewriter(QualifiedModuleName module)
+ {
+ var tokenStream = _tokenStreamCache.CodePaneTokenStream(module);
+ return _rewriterFactory.CodePaneRewriter(module, tokenStream);
+ }
+
+ public IExecutableModuleRewriter AttributesModuleRewriter(QualifiedModuleName module)
+ {
+ var tokenStream = _tokenStreamCache.AttributesTokenStream(module);
+ return _rewriterFactory.AttributesRewriter(module, tokenStream);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Rubberduck.Parsing/Rewriter/RewritingManager.cs b/Rubberduck.Parsing/Rewriter/RewritingManager.cs
new file mode 100644
index 0000000000..60af3da64e
--- /dev/null
+++ b/Rubberduck.Parsing/Rewriter/RewritingManager.cs
@@ -0,0 +1,94 @@
+using System;
+using System.Collections.Generic;
+using Rubberduck.Parsing.VBA.Parsing;
+
+namespace Rubberduck.Parsing.Rewriter
+{
+ public class RewritingManager : IRewritingManager
+ {
+ private readonly HashSet _activeCodePaneSessions = new HashSet();
+ private readonly HashSet _activeAttributesSessions = new HashSet();
+
+ private readonly IRewriteSessionFactory _sessionFactory;
+
+ private readonly object _invalidationLockObject = new object();
+
+ public RewritingManager(IRewriteSessionFactory sessionFactory)
+ {
+ _sessionFactory = sessionFactory;
+ }
+
+
+ public IRewriteSession CheckOutCodePaneSession()
+ {
+ var newSession = _sessionFactory.CodePaneSession(TryAllowExclusiveRewrite);
+ lock (_invalidationLockObject)
+ {
+ _activeCodePaneSessions.Add(newSession);
+ }
+
+ return newSession;
+ }
+
+ public IRewriteSession CheckOutAttributesSession()
+ {
+ var newSession = _sessionFactory.AttributesSession(TryAllowExclusiveRewrite);
+ lock (_invalidationLockObject)
+ {
+ _activeAttributesSessions.Add(newSession);
+ }
+
+ return newSession;
+ }
+
+ private bool TryAllowExclusiveRewrite(IRewriteSession rewriteSession)
+ {
+ lock (_invalidationLockObject)
+ {
+ if (!IsCurrentlyActive(rewriteSession))
+ {
+ return false;
+ }
+
+ InvalidateAllSessionsInternal();
+ return true;
+ }
+ }
+
+ private bool IsCurrentlyActive(IRewriteSession rewriteSession)
+ {
+ switch (rewriteSession.TargetCodeKind)
+ {
+ case CodeKind.CodePaneCode:
+ return _activeCodePaneSessions.Contains(rewriteSession);
+ case CodeKind.AttributesCode:
+ return _activeAttributesSessions.Contains(rewriteSession);
+ default:
+ throw new NotSupportedException(nameof(rewriteSession));
+ }
+ }
+
+ public void InvalidateAllSessions()
+ {
+ lock (_invalidationLockObject)
+ {
+ InvalidateAllSessionsInternal();
+ }
+ }
+
+ private void InvalidateAllSessionsInternal()
+ {
+ foreach (var rewriteSession in _activeCodePaneSessions)
+ {
+ rewriteSession.Invalidate();
+ }
+ _activeCodePaneSessions.Clear();
+
+ foreach (var rewriteSession in _activeAttributesSessions)
+ {
+ rewriteSession.Invalidate();
+ }
+ _activeAttributesSessions.Clear();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Rubberduck.Parsing/Rewriter/StateTokenStreamCache.cs b/Rubberduck.Parsing/Rewriter/StateTokenStreamCache.cs
new file mode 100644
index 0000000000..80e1b939a8
--- /dev/null
+++ b/Rubberduck.Parsing/Rewriter/StateTokenStreamCache.cs
@@ -0,0 +1,27 @@
+using Antlr4.Runtime;
+using Rubberduck.Parsing.VBA;
+using Rubberduck.VBEditor;
+
+namespace Rubberduck.Parsing.Rewriter
+{
+ public class StateTokenStreamCache : ITokenStreamCache
+ {
+ private readonly RubberduckParserState _state;
+
+ public StateTokenStreamCache(RubberduckParserState state)
+ {
+ _state = state;
+ }
+
+
+ public ITokenStream CodePaneTokenStream(QualifiedModuleName module)
+ {
+ return _state.GetCodePaneTokenStream(module);
+ }
+
+ public ITokenStream AttributesTokenStream(QualifiedModuleName module)
+ {
+ return _state.GetAttributesTokenStream(module);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Rubberduck.Parsing/Rubberduck.Parsing.csproj b/Rubberduck.Parsing/Rubberduck.Parsing.csproj
index 39112bf542..4a6cc8ea22 100644
--- a/Rubberduck.Parsing/Rubberduck.Parsing.csproj
+++ b/Rubberduck.Parsing/Rubberduck.Parsing.csproj
@@ -1,5 +1,5 @@
-
+
Rubberduck.Parsing
Rubberduck.Parsing
@@ -25,7 +25,7 @@
Rubberduck.Parsing.Grammar
-
+
Rubberduck.Parsing.PreProcessing
diff --git a/Rubberduck.Parsing/Symbols/IdentifierReferenceResolver.cs b/Rubberduck.Parsing/Symbols/IdentifierReferenceResolver.cs
index b5489bf426..95cc8cbfb8 100644
--- a/Rubberduck.Parsing/Symbols/IdentifierReferenceResolver.cs
+++ b/Rubberduck.Parsing/Symbols/IdentifierReferenceResolver.cs
@@ -23,7 +23,7 @@ public sealed class IdentifierReferenceResolver
private Declaration _currentParent;
private readonly BindingService _bindingService;
private readonly BoundExpressionVisitor _boundExpressionVisitor;
- private readonly AnnotationService _annotationService;
+ private readonly IdentifierAnnotationService _identifierAnnotationService;
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
public IdentifierReferenceResolver(QualifiedModuleName qualifiedModuleName, DeclarationFinder finder)
@@ -44,8 +44,8 @@ public IdentifierReferenceResolver(QualifiedModuleName qualifiedModuleName, Decl
new DefaultBindingContext(_declarationFinder, typeBindingContext, procedurePointerBindingContext),
typeBindingContext,
procedurePointerBindingContext);
- _annotationService = new AnnotationService(_declarationFinder);
- _boundExpressionVisitor = new BoundExpressionVisitor(_annotationService);
+ _identifierAnnotationService = new IdentifierAnnotationService(_declarationFinder);
+ _boundExpressionVisitor = new BoundExpressionVisitor(_identifierAnnotationService);
}
public void SetCurrentScope()
@@ -153,7 +153,7 @@ private void ResolveLabel(ParserRuleContext context, string label)
identifier,
callee,
callSiteContext.GetSelection(),
- _annotationService.FindAnnotations(_qualifiedModuleName, callSiteContext.GetSelection().StartLine));
+ _identifierAnnotationService.FindAnnotations(_qualifiedModuleName, callSiteContext.GetSelection().StartLine));
}
}
@@ -713,7 +713,7 @@ public void Resolve(VBAParser.RaiseEventStmtContext context)
identifier,
callee,
callSiteContext.GetSelection(),
- _annotationService.FindAnnotations(_qualifiedModuleName, callSiteContext.GetSelection().StartLine));
+ _identifierAnnotationService.FindAnnotations(_qualifiedModuleName, callSiteContext.GetSelection().StartLine));
}
if (context.eventArgumentList() == null)
{
@@ -819,7 +819,7 @@ public void Resolve(VBAParser.DebugPrintStmtContext context)
context.debugPrint().debugModule().GetText(),
debugModule,
context.debugPrint().debugModule().GetSelection(),
- _annotationService.FindAnnotations(_qualifiedModuleName, context.debugPrint().debugModule().GetSelection().StartLine));
+ _identifierAnnotationService.FindAnnotations(_qualifiedModuleName, context.debugPrint().debugModule().GetSelection().StartLine));
debugPrint.AddReference(
_qualifiedModuleName,
_currentScope,
@@ -828,7 +828,7 @@ public void Resolve(VBAParser.DebugPrintStmtContext context)
context.debugPrint().debugPrintSub().GetText(),
debugPrint,
context.debugPrint().debugPrintSub().GetSelection(),
- _annotationService.FindAnnotations(_qualifiedModuleName, context.debugPrint().debugPrintSub().GetSelection().StartLine));
+ _identifierAnnotationService.FindAnnotations(_qualifiedModuleName, context.debugPrint().debugPrintSub().GetSelection().StartLine));
var outputList = context.outputList();
if (outputList != null)
{
diff --git a/Rubberduck.Parsing/VBA/AccessibilityCheck.cs b/Rubberduck.Parsing/VBA/AccessibilityCheck.cs
index 1527be7eb1..9cb0681b85 100644
--- a/Rubberduck.Parsing/VBA/AccessibilityCheck.cs
+++ b/Rubberduck.Parsing/VBA/AccessibilityCheck.cs
@@ -5,14 +5,20 @@ namespace Rubberduck.Parsing.VBA
{
public static class AccessibilityCheck
{
- public static bool IsAccessible(Declaration callingProject, Declaration callingModule, Declaration callingParent, Declaration callee)
+ public static bool IsAccessible(Declaration callingParent, Declaration callee)
{
- return callee != null
- && (callee.DeclarationType.HasFlag(DeclarationType.Project)
- || (callee.DeclarationType.HasFlag(DeclarationType.Module) && IsModuleAccessible(callingProject, callingModule, callee))
- || (!callee.DeclarationType.HasFlag(DeclarationType.Module) && IsMemberAccessible(callingProject, callingModule, callingParent, callee)));
+ var callingModule = callingParent.ParentScopeDeclaration;
+ var callingProject = callingModule.ParentDeclaration;
+ return IsAccessible(callingProject, callingModule, callingParent, callee);
}
+ public static bool IsAccessible(Declaration callingProject, Declaration callingModule, Declaration callingParent, Declaration callee)
+ {
+ return callee != null
+ && (callee.DeclarationType.HasFlag(DeclarationType.Project)
+ || (callee.DeclarationType.HasFlag(DeclarationType.Module) && IsModuleAccessible(callingProject, callingModule, callee))
+ || (!callee.DeclarationType.HasFlag(DeclarationType.Module) && IsMemberAccessible(callingProject, callingModule, callingParent, callee)));
+ }
public static bool IsModuleAccessible(Declaration callingProject, Declaration callingModule, Declaration calleeModule)
{
diff --git a/Rubberduck.Parsing/VBA/DeclarationCaching/DeclarationFinder.cs b/Rubberduck.Parsing/VBA/DeclarationCaching/DeclarationFinder.cs
index 8c91bc9ec3..13bcc959f8 100644
--- a/Rubberduck.Parsing/VBA/DeclarationCaching/DeclarationFinder.cs
+++ b/Rubberduck.Parsing/VBA/DeclarationCaching/DeclarationFinder.cs
@@ -22,7 +22,7 @@ public class DeclarationFinder
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private readonly IHostApplication _hostApp;
- private readonly AnnotationService _annotationService;
+ private readonly IdentifierAnnotationService _identifierAnnotationService;
private IDictionary> _declarationsByName;
private IDictionary> _declarations;
private readonly ConcurrentDictionary> _newUndeclared;
@@ -68,7 +68,7 @@ public DeclarationFinder(IReadOnlyList declarations, IEnumerable>(new Dictionary>());
_newUnresolved = new ConcurrentBag(new List());
- _annotationService = new AnnotationService(this);
+ _identifierAnnotationService = new IdentifierAnnotationService(this);
var collectionConstructionActions = CollectionConstructionActions(declarations, annotations, unresolvedMemberDeclarations);
ExecuteCollectionConstructionActions(collectionConstructionActions);
@@ -503,7 +503,10 @@ public bool IsMatch(string declarationName, string potentialMatchName)
private IEnumerable FindEvents(Declaration module)
{
- Debug.Assert(module != null);
+ if (module is null)
+ {
+ return Enumerable.Empty();
+ }
var members = Members(module.QualifiedName.QualifiedModuleName);
return members == null
@@ -531,6 +534,71 @@ public IEnumerable MatchName(string name)
: Enumerable.Empty();
}
+ public ParameterDeclaration FindParameterFromArgument(VBAParser.ArgumentExpressionContext argExpression, Declaration enclosingProcedure)
+ {
+ if (argExpression == null ||
+ argExpression.GetDescendent() != null ||
+ argExpression.BYVAL() != null)
+ {
+ // not an argument, or argument is parenthesized and thus passed ByVal
+ return null;
+ }
+
+ var callStmt = argExpression.GetAncestor();
+
+ var identifier = callStmt?
+ .GetDescendent()
+ .GetDescendents()
+ .LastOrDefault();
+
+ if (identifier == null)
+ {
+ // if we don't know what we're calling, we can't dig any further
+ return null;
+ }
+
+ var selection = new QualifiedSelection(enclosingProcedure.QualifiedModuleName, identifier.GetSelection());
+ if (!_referencesBySelection.TryGetValue(selection, out var matches))
+ {
+ return null;
+ }
+
+ var procedure = matches.SingleOrDefault()?.Declaration;
+ if (procedure?.ParentScopeDeclaration is ClassModuleDeclaration)
+ {
+ // we can't know that the member is on the class' default interface
+ return null;
+ }
+
+ var parameters = Parameters(procedure);
+
+ ParameterDeclaration parameter;
+ var namedArg = argExpression.GetAncestor();
+ if (namedArg != null)
+ {
+ // argument is named: we're lucky
+ var parameterName = namedArg.unrestrictedIdentifier().GetText();
+ parameter = parameters.SingleOrDefault(p => p.IdentifierName == parameterName);
+ }
+ else
+ {
+ // argument is positional: work out its index
+ var argList = callStmt.GetDescendent();
+ var args = argList.GetDescendents().ToArray();
+
+ var parameterIndex = args
+ .Select((param, index) => param.GetDescendent() == argExpression ? (param, index) : (null, -1))
+ .SingleOrDefault(item => item.param != null).index;
+
+ parameter = parameters
+ .OrderBy(p => p.Selection)
+ .Select((param, index) => (param, index))
+ .SingleOrDefault(item => item.index == parameterIndex).param;
+ }
+
+ return parameter;
+ }
+
private string ToNormalizedName(string name)
{
var lower = name.ToLowerInvariant();
@@ -746,7 +814,7 @@ public Declaration FindMemberEnclosingProcedure(Declaration enclosingProcedure,
public Declaration OnUndeclaredVariable(Declaration enclosingProcedure, string identifierName, ParserRuleContext context)
{
- var annotations = _annotationService.FindAnnotations(enclosingProcedure.QualifiedName.QualifiedModuleName, context.Start.Line);
+ var annotations = _identifierAnnotationService.FindAnnotations(enclosingProcedure.QualifiedName.QualifiedModuleName, context.Start.Line);
var undeclaredLocal =
new Declaration(
new QualifiedMemberName(enclosingProcedure.QualifiedName.QualifiedModuleName, identifierName),
@@ -801,7 +869,7 @@ public void AddUnboundContext(Declaration parentDeclaration, VBAParser.LExpressi
}
var identifier = context.GetChild(0);
- var annotations = _annotationService.FindAnnotations(parentDeclaration.QualifiedName.QualifiedModuleName, context.Start.Line);
+ var annotations = _identifierAnnotationService.FindAnnotations(parentDeclaration.QualifiedName.QualifiedModuleName, context.Start.Line);
var declaration = new UnboundMemberDeclaration(parentDeclaration, identifier,
(context is VBAParser.MemberAccessExprContext) ? (ParserRuleContext)context.children[0] : withExpression.Context,
@@ -1034,12 +1102,8 @@ public IEnumerable FindNewDeclarationNameConflicts(string newName,
if (IsEnumOrUDTMemberDeclaration(renameTarget))
{
- var memberMatches = identifierMatches.Where(idm =>
+ return identifierMatches.Where(idm =>
IsEnumOrUDTMemberDeclaration(idm) && idm.ParentDeclaration == renameTarget.ParentDeclaration);
- if (memberMatches.Any())
- {
- return memberMatches;
- }
}
identifierMatches = identifierMatches.Where(nc => !IsEnumOrUDTMemberDeclaration(nc));
diff --git a/Rubberduck.Parsing/VBA/DeclarationResolving/DeclarationResolveRunnerBase.cs b/Rubberduck.Parsing/VBA/DeclarationResolving/DeclarationResolveRunnerBase.cs
index a254a8dfe8..ecef2de7c6 100644
--- a/Rubberduck.Parsing/VBA/DeclarationResolving/DeclarationResolveRunnerBase.cs
+++ b/Rubberduck.Parsing/VBA/DeclarationResolving/DeclarationResolveRunnerBase.cs
@@ -159,12 +159,6 @@ protected void ResolveDeclarations(QualifiedModuleName module, IParseTree tree,
{
_state.AddDeclaration(createdDeclaration);
}
-
- //This is a hack to deal with annotations on module level variables.
- var memberAnnotations = declarationsListener.CreatedDeclarations
- .SelectMany(declaration => declaration.Annotations)
- .ToHashSet();
- moduleDeclaration.RemoveAnnotations(memberAnnotations);
}
catch (Exception exception)
{
@@ -235,43 +229,25 @@ private static IEnumerable FindModuleAnnotations(IParseTree tree, I
return null;
}
- var lastDeclarationsSectionLine = LastDeclarationsSectionLine(tree, annotations);
+ var potentialModuleAnnotations = annotations.Where(annotation =>
+ annotation.AnnotationType.HasFlag(AnnotationType.ModuleAnnotation));
+
+ var lastPossibleDeclarationsSectionLine = LastPossibleDeclarationsSectionLine(tree);
//There is no module body.
- if (lastDeclarationsSectionLine == null)
+ if (lastPossibleDeclarationsSectionLine == null)
{
- return annotations;
+ return potentialModuleAnnotations;
}
- var lastPossibleModuleAnnotationLine = lastDeclarationsSectionLine.Value;
- var moduleAnnotations = annotations.Where(annotation => annotation.QualifiedSelection.Selection.EndLine <= lastPossibleModuleAnnotationLine);
- return moduleAnnotations.ToList();
+ var lastPossibleModuleAnnotationLine = lastPossibleDeclarationsSectionLine.Value;
+ var moduleAnnotations = potentialModuleAnnotations.Where(annotation => annotation.QualifiedSelection.Selection.EndLine <= lastPossibleModuleAnnotationLine);
+ return moduleAnnotations;
}
- private static int? LastDeclarationsSectionLine(IParseTree tree, ICollection annotations)
+ private static int? LastPossibleDeclarationsSectionLine(IParseTree tree)
{
- var firstModuleBodyElementLine = FirstModuleBodyElementLine(tree);
-
- if (firstModuleBodyElementLine == null)
- {
- return null;
- }
-
- //The VBE uses 1-based lines.
- for (var currentLine = firstModuleBodyElementLine.Value - 1; currentLine >= 1; currentLine--)
- {
- if (annotations.Any(annotation => annotation.QualifiedSelection.Selection.StartLine <= currentLine
- && annotation.QualifiedSelection.Selection.EndLine >=
- currentLine))
- {
- continue;
- }
-
- return currentLine;
- }
-
- //There is no declaration section.
- return 0;
+ return FirstModuleBodyElementLine(tree) - 1;
}
private static int? FirstModuleBodyElementLine(IParseTree tree)
diff --git a/Rubberduck.Parsing/VBA/DeclarationResolving/DeclarationSymbolsListener.cs b/Rubberduck.Parsing/VBA/DeclarationResolving/DeclarationSymbolsListener.cs
index f38ceb04a0..8521ec2067 100644
--- a/Rubberduck.Parsing/VBA/DeclarationResolving/DeclarationSymbolsListener.cs
+++ b/Rubberduck.Parsing/VBA/DeclarationResolving/DeclarationSymbolsListener.cs
@@ -43,6 +43,11 @@ public DeclarationSymbolsListener(
}
private IEnumerable FindMemberAnnotations(int firstMemberLine)
+ {
+ return FindAnnotations(firstMemberLine, AnnotationType.MemberAnnotation);
+ }
+
+ private IEnumerable FindAnnotations(int firstLine, AnnotationType annotationTypeFlag)
{
if (_annotations == null)
{
@@ -52,15 +57,20 @@ private IEnumerable FindMemberAnnotations(int firstMemberLine)
var annotations = new List();
// VBE 1-based indexing
- for (var currentLine = firstMemberLine - 1; currentLine >= 1; currentLine--)
+ for (var currentLine = firstLine - 1; currentLine >= 1; currentLine--)
{
+ //Annotation sections end at the first line without an annotation with the specified flag or identifier annotation.
+ //Identifier annotations are treated in a special way because identifier references can appear on the same line as other constructs that can be annotated.
if (!_annotations.Any(annotation => annotation.QualifiedSelection.Selection.StartLine <= currentLine
- && annotation.QualifiedSelection.Selection.EndLine >= currentLine))
+ && annotation.QualifiedSelection.Selection.EndLine >= currentLine
+ && (annotation.AnnotationType.HasFlag(annotationTypeFlag)
+ || annotation.AnnotationType.HasFlag(AnnotationType.IdentifierAnnotation))))
{
break;
}
- var annotationsStartingOnCurrentLine = _annotations.Where(a => a.QualifiedSelection.Selection.StartLine == currentLine);
+ var annotationsStartingOnCurrentLine = _annotations.Where(a => a.QualifiedSelection.Selection.StartLine == currentLine
+ && a.AnnotationType.HasFlag(annotationTypeFlag));
annotations.AddRange(annotationsStartingOnCurrentLine);
}
@@ -68,6 +78,16 @@ private IEnumerable FindMemberAnnotations(int firstMemberLine)
return annotations;
}
+ private IEnumerable FindVariableAnnotations(int firstVariableLine)
+ {
+ return FindAnnotations(firstVariableLine, AnnotationType.VariableAnnotation);
+ }
+
+ private IEnumerable FindGeneralAnnotations(int firstLine)
+ {
+ return FindAnnotations(firstLine, AnnotationType.GeneralAnnotation);
+ }
+
private Declaration CreateDeclaration(
string identifierName,
string asTypeName,
@@ -112,7 +132,6 @@ private Declaration CreateDeclaration(
_attributes.TryGetValue(key, out var attributes);
_membersAllowingAttributes.TryGetValue(key, out var attributesPassContext);
- var annotations = FindMemberAnnotations(selection.StartLine);
switch (declarationType)
{
case DeclarationType.Procedure:
@@ -125,8 +144,8 @@ private Declaration CreateDeclaration(
context,
attributesPassContext,
selection,
- true,
- annotations,
+ true,
+ FindMemberAnnotations(selection.StartLine),
attributes);
break;
case DeclarationType.Function:
@@ -143,7 +162,7 @@ private Declaration CreateDeclaration(
selection,
isArray,
true,
- annotations,
+ FindMemberAnnotations(selection.StartLine),
attributes);
break;
case DeclarationType.Event:
@@ -159,7 +178,7 @@ private Declaration CreateDeclaration(
selection,
isArray,
true,
- annotations,
+ FindGeneralAnnotations(selection.StartLine),
attributes);
break;
case DeclarationType.LibraryProcedure:
@@ -174,8 +193,8 @@ private Declaration CreateDeclaration(
accessibility,
context,
selection,
- true,
- annotations);
+ true,
+ FindMemberAnnotations(selection.StartLine));
break;
case DeclarationType.PropertyGet:
result = new PropertyGetDeclaration(
@@ -191,7 +210,7 @@ private Declaration CreateDeclaration(
selection,
isArray,
true,
- annotations,
+ FindMemberAnnotations(selection.StartLine),
attributes);
break;
case DeclarationType.PropertySet:
@@ -204,8 +223,8 @@ private Declaration CreateDeclaration(
context,
attributesPassContext,
selection,
- true,
- annotations,
+ true,
+ FindMemberAnnotations(selection.StartLine),
attributes);
break;
case DeclarationType.PropertyLet:
@@ -218,8 +237,8 @@ private Declaration CreateDeclaration(
context,
attributesPassContext,
selection,
- true,
- annotations,
+ true,
+ FindMemberAnnotations(selection.StartLine),
attributes);
break;
case DeclarationType.EnumerationMember:
@@ -229,8 +248,8 @@ private Declaration CreateDeclaration(
_currentScope,
asTypeName,
asTypeContext,
- typeHint,
- annotations,
+ typeHint,
+ FindVariableAnnotations(selection.StartLine),
accessibility,
declarationType,
(context as VBAParser.EnumerationStmt_ConstantContext)?.expression()?.GetText() ?? string.Empty,
@@ -252,7 +271,7 @@ private Declaration CreateDeclaration(
selection,
isArray,
asTypeContext,
- annotations,
+ FindVariableAnnotations(selection.StartLine),
attributes);
break;
default:
@@ -272,7 +291,7 @@ private Declaration CreateDeclaration(
isArray,
asTypeContext,
true,
- annotations,
+ FindGeneralAnnotations(selection.StartLine),
attributes);
break;
}
@@ -666,7 +685,7 @@ public override void EnterVariableSubStmt(VBAParser.VariableSubStmtContext conte
? Tokens.Variant
: asTypeClause.type().GetText()
: SymbolList.TypeHintToTypeName[typeHint];
- var withEvents = parent.WITHEVENTS() != null;
+ var withEvents = context.WITHEVENTS() != null;
var isAutoObject = asTypeClause != null && asTypeClause.NEW() != null;
bool isArray = context.LPAREN() != null;
AddDeclaration(
@@ -708,7 +727,7 @@ public override void EnterConstSubStmt(VBAParser.ConstSubStmtContext context)
asTypeName,
asTypeClause,
typeHint,
- FindMemberAnnotations(constStmt.Start.Line),
+ FindVariableAnnotations(constStmt.Start.Line),
accessibility,
DeclarationType.Constant,
value,
diff --git a/Rubberduck.Parsing/VBA/IParseManager.cs b/Rubberduck.Parsing/VBA/IParseManager.cs
new file mode 100644
index 0000000000..fc9bda0947
--- /dev/null
+++ b/Rubberduck.Parsing/VBA/IParseManager.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+
+namespace Rubberduck.Parsing.VBA
+{
+ public interface IParseManager
+ {
+ event EventHandler StateChanged;
+ event EventHandler ModuleStateChanged;
+
+ void OnParseRequested(object requestor);
+ SuspensionResult OnSuspendParser(object requestor, IEnumerable allowedRunStates, Action busyAction, int millisecondsTimeout = -1);
+ }
+}
\ No newline at end of file
diff --git a/Rubberduck.Parsing/VBA/ModuleState.cs b/Rubberduck.Parsing/VBA/ModuleState.cs
index c85b52e2cd..27eedbcfe1 100644
--- a/Rubberduck.Parsing/VBA/ModuleState.cs
+++ b/Rubberduck.Parsing/VBA/ModuleState.cs
@@ -4,11 +4,9 @@
using Antlr4.Runtime;
using Antlr4.Runtime.Tree;
using Rubberduck.Parsing.Annotations;
-using Rubberduck.Parsing.Rewriter;
using Rubberduck.Parsing.Symbols;
using Rubberduck.Parsing.VBA.Parsing;
using Rubberduck.Parsing.VBA.Parsing.ParsingExceptions;
-using Rubberduck.VBEditor;
namespace Rubberduck.Parsing.VBA
{
@@ -16,8 +14,8 @@ public class ModuleState
{
public ConcurrentDictionary Declarations { get; private set; }
public ConcurrentDictionary UnresolvedMemberDeclarations { get; private set; }
- public IModuleRewriter ModuleRewriter { get; private set; }
- public IModuleRewriter AttributesRewriter { get; private set; }
+ public ITokenStream CodePaneTokenStream { get; private set; }
+ public ITokenStream AttributesTokenStream { get; private set; }
public IParseTree ParseTree { get; private set; }
public IParseTree AttributesPassParseTree { get; private set; }
public ParserState State { get; private set; }
@@ -79,9 +77,9 @@ public ModuleState(SyntaxErrorException moduleException)
IsNew = true;
}
- public ModuleState SetCodePaneRewriter(QualifiedModuleName module, IModuleRewriter codePaneRewriter)
+ public ModuleState SetCodePaneTokenStream(ITokenStream codePaneTokenStream)
{
- ModuleRewriter = codePaneRewriter;
+ CodePaneTokenStream = codePaneTokenStream;
return this;
}
@@ -144,9 +142,9 @@ public ModuleState SetMembersAllowingAttributes(IDictionary<(string scopeIdentif
return this;
}
- public ModuleState SetAttributesRewriter(IModuleRewriter rewriter)
+ public ModuleState SetAttributesTokenStream(ITokenStream attributesTokenStream)
{
- AttributesRewriter = rewriter;
+ AttributesTokenStream = attributesTokenStream;
return this;
}
diff --git a/Rubberduck.Parsing/VBA/ParseCoordinator.cs b/Rubberduck.Parsing/VBA/ParseCoordinator.cs
index e8685888d3..0fc023f679 100644
--- a/Rubberduck.Parsing/VBA/ParseCoordinator.cs
+++ b/Rubberduck.Parsing/VBA/ParseCoordinator.cs
@@ -8,6 +8,7 @@
using System.Diagnostics;
using System.Linq;
using NLog;
+using Rubberduck.Parsing.Rewriter;
using Rubberduck.Parsing.VBA.Extensions;
using Rubberduck.VBEditor.SafeComWrappers.Abstract;
@@ -27,6 +28,7 @@ public class ParseCoordinator : IParseCoordinator
private readonly IProjectManager _projectManager;
private readonly IParsingCacheService _parsingCacheService;
private readonly IParserStateManager _parserStateManager;
+ private readonly IRewritingManager _rewritingManager;
private readonly ConcurrentStack