diff --git a/DPackRx.Tests/CodeModel/FileCodeModelTests.cs b/DPackRx.Tests/CodeModel/MemberCodeModelTests.cs similarity index 92% rename from DPackRx.Tests/CodeModel/FileCodeModelTests.cs rename to DPackRx.Tests/CodeModel/MemberCodeModelTests.cs index b8ef9a8..a2760d2 100644 --- a/DPackRx.Tests/CodeModel/FileCodeModelTests.cs +++ b/DPackRx.Tests/CodeModel/MemberCodeModelTests.cs @@ -5,19 +5,19 @@ namespace DPackRx.Tests.CodeModel { /// - /// FileCodeModel tests. + /// tests. /// [TestFixture] - public class FileCodeModelTests + public class MemberCodeModelTests { #region Private Methods /// /// Returns test model instance. /// - private FileCodeModel GetModel() + private MemberCodeModel GetModel() { - return new FileCodeModel(); + return new MemberCodeModel(); } #endregion diff --git a/DPackRx.Tests/DPackRx.Tests.csproj b/DPackRx.Tests/DPackRx.Tests.csproj index a3d2c28..8d4dc54 100644 --- a/DPackRx.Tests/DPackRx.Tests.csproj +++ b/DPackRx.Tests/DPackRx.Tests.csproj @@ -169,7 +169,7 @@ - + diff --git a/DPackRx.Tests/Features/CodeBrowserViewModelTests.cs b/DPackRx.Tests/Features/CodeBrowserViewModelTests.cs index 317f824..9c9935b 100644 --- a/DPackRx.Tests/Features/CodeBrowserViewModelTests.cs +++ b/DPackRx.Tests/Features/CodeBrowserViewModelTests.cs @@ -31,7 +31,7 @@ public class CodeBrowserViewModelTests private Mock _searchMatchServiceMock; private Mock _shellSelectionServiceMock; private Mock _shellImageServiceMock; - private List _members; + private List _members; #endregion @@ -49,17 +49,17 @@ public void Setup() _optionsServiceMock = new Mock(); - _members = new List + _members = new List { - new FileCodeModel { Name = "Test", FullName = "class1.Test", ElementKind = Kind.Method, Rank = 0, Matched = false }, - new FileCodeModel { Name = "Hello", FullName = "class1.Hello", ElementKind = Kind.Method, Rank = 0, Matched = false }, - new FileCodeModel { Name = "TestToo", FullName = "class1.TestToo", ElementKind = Kind.Property, Rank = 0, Matched = false }, - new FileCodeModel { Name = "_somethingElse", FullName = "class1._somethingElse", ElementKind = Kind.Variable, Rank = 0, Matched = false }, + new MemberCodeModel { Name = "Test", FullName = "class1.Test", ElementKind = Kind.Method, Rank = 0, Matched = false }, + new MemberCodeModel { Name = "Hello", FullName = "class1.Hello", ElementKind = Kind.Method, Rank = 0, Matched = false }, + new MemberCodeModel { Name = "TestToo", FullName = "class1.TestToo", ElementKind = Kind.Property, Rank = 0, Matched = false }, + new MemberCodeModel { Name = "_somethingElse", FullName = "class1._somethingElse", ElementKind = Kind.Variable, Rank = 0, Matched = false }, }; _fileProcessorMock = new Mock(); _fileProcessorMock .Setup(p => p.GetMembers(ProcessorFlags.IncludeFileCodeModel, It.IsAny())) - .Returns(_members) + .Returns(new FileCodeModel { FileName = "test", Members = _members }) .Verifiable(); _searchMatchServiceMock = new Mock(); @@ -125,6 +125,7 @@ public void OnInitialize(string search, CodeModelFilterFlags flags, int expected else if (flags == CodeModelFilterFlags.ClassesInterfaces) _members.Where(f => f.Matched && !((f.ElementKind == Kind.Class) || (f.ElementKind == Kind.Interface))).ForEach(f => f.Matched = false); + _optionsServiceMock.Setup(o => o.GetStringOption(viewModel.Feature, "File", string.Empty)).Returns("test").Verifiable(); _optionsServiceMock.Setup(o => o.GetStringOption(viewModel.Feature, "Search", string.Empty)).Returns(search).Verifiable(); _optionsServiceMock.Setup(o => o.GetIntOption(viewModel.Feature, "Filter", (int)flags)).Returns((int)flags).Verifiable(); _optionsServiceMock.Setup(o => o.GetBoolOption(viewModel.Feature, "XmlDoc", false)).Returns(false).Verifiable(); @@ -137,12 +138,27 @@ public void OnInitialize(string search, CodeModelFilterFlags flags, int expected Assert.That(viewModel.Search, Is.EqualTo(search)); Assert.That(viewModel.Filter, Is.EqualTo(flags)); _fileProcessorMock.Verify(p => p.GetMembers(ProcessorFlags.IncludeFileCodeModel, It.IsAny())); + _optionsServiceMock.Verify(o => o.GetStringOption(viewModel.Feature, "File", string.Empty)); _optionsServiceMock.Verify(o => o.GetStringOption(viewModel.Feature, "Search", string.Empty)); _optionsServiceMock.Verify(o => o.GetIntOption(viewModel.Feature, "Filter", (int)flags)); _optionsServiceMock.Verify(o => o.GetBoolOption(viewModel.Feature, "XmlDoc", false)); _searchMatchServiceMock.Verify(s => s.MatchItems(search, It.IsAny>()), Times.Once); } + [Test] + public void OnInitialize_ResetSearch() + { + var viewModel = GetViewModel(); + + _optionsServiceMock.Setup(o => o.GetStringOption(viewModel.Feature, "File", string.Empty)).Returns("something else").Verifiable(); + _optionsServiceMock.Setup(o => o.GetStringOption(viewModel.Feature, "Search", string.Empty)).Returns("hello").Verifiable(); + + viewModel.OnInitialize(CodeModelFilterFlags.All); + + Assert.That(viewModel.Search, Is.Null.Or.Empty, "Search should be reset on new file"); + Assert.That(viewModel.FileName, Is.EqualTo("test")); + } + [Test] public void OnInitialize_ErrorHandling() { @@ -157,6 +173,7 @@ public void OnClose(bool apply) { var viewModel = GetViewModel(); + _optionsServiceMock.Setup(o => o.SetStringOption(viewModel.Feature, "File", string.Empty)).Verifiable(); _optionsServiceMock.Setup(o => o.SetStringOption(viewModel.Feature, "Search", string.Empty)).Verifiable(); _optionsServiceMock.Setup(o => o.SetIntOption(viewModel.Feature, "Filter", 0)).Verifiable(); @@ -166,6 +183,7 @@ public void OnClose(bool apply) viewModel.OnClose(apply); + _optionsServiceMock.Verify(o => o.SetStringOption(viewModel.Feature, "File", string.Empty)); _optionsServiceMock.Verify(o => o.SetStringOption(viewModel.Feature, "Search", string.Empty)); _optionsServiceMock.Verify(o => o.SetIntOption(viewModel.Feature, "Filter", 0)); if (apply) @@ -238,7 +256,7 @@ public void SelectMemberCommand_Execute() { var viewModel = GetViewModel(); - var selection = new FileCodeModel(); + var selection = new MemberCodeModel(); viewModel.SelectMemberCommand.Execute(selection); Assert.That(viewModel.Selection, Is.EqualTo(selection)); diff --git a/DPackRx.Tests/Features/FileBrowserViewModelTests.cs b/DPackRx.Tests/Features/FileBrowserViewModelTests.cs index 7d225a6..4a5d1b2 100644 --- a/DPackRx.Tests/Features/FileBrowserViewModelTests.cs +++ b/DPackRx.Tests/Features/FileBrowserViewModelTests.cs @@ -64,7 +64,7 @@ public void Setup() _solutionProcessorMock = new Mock(); _solutionProcessorMock .Setup(p => p.GetProjects(ProcessorFlags.IncludeFiles | ProcessorFlags.GroupLinkedFiles, CodeModelFilterFlags.All)) - .Returns(new SolutionModel { Files = _files }) + .Returns(new SolutionModel { SolutionName = "test", Files = _files }) .Verifiable(); _fileTypeResolverMock = new Mock(); @@ -142,6 +142,7 @@ public void OnInitialize(string search, bool allFiles, string ignoreFiles, strin else _files.Where(f => f.FileName.IndexOf(search, StringComparison.OrdinalIgnoreCase) >= 0).ForEach(f => f.Matched = true); + _optionsServiceMock.Setup(o => o.GetStringOption(viewModel.Feature, "Solution", string.Empty)).Returns("test").Verifiable(); _optionsServiceMock.Setup(o => o.GetStringOption(viewModel.Feature, "Search", string.Empty)).Returns(search).Verifiable(); _optionsServiceMock.Setup(o => o.GetBoolOption(viewModel.Feature, "AllFiles", false)).Returns(allFiles).Verifiable(); _optionsServiceMock.Setup(o => o.GetStringOption(viewModel.Feature, "IgnoreFiles", null)).Returns(ignoreFiles).Verifiable(); @@ -159,7 +160,9 @@ public void OnInitialize(string search, bool allFiles, string ignoreFiles, strin Assert.That(viewModel.FilteredFiles.Count, Is.EqualTo(expectedCodeFileCount + expectedNoneCodeFileCount)); Assert.That(viewModel.Search, Is.EqualTo(search)); Assert.That(viewModel.AllFiles, Is.EqualTo(allFiles)); + Assert.That(viewModel.SolutionName, Is.EqualTo("test")); _solutionProcessorMock.Verify(p => p.GetProjects(ProcessorFlags.IncludeFiles | ProcessorFlags.GroupLinkedFiles, CodeModelFilterFlags.All)); + _optionsServiceMock.Verify(o => o.GetStringOption(viewModel.Feature, "Solution", string.Empty), Times.Once); _optionsServiceMock.Verify(o => o.GetStringOption(viewModel.Feature, "Search", string.Empty), Times.Once); _optionsServiceMock.Verify(o => o.GetBoolOption(viewModel.Feature, "AllFiles", false), Times.Once); _optionsServiceMock.Verify(o => o.GetStringOption(viewModel.Feature, "IgnoreFiles", null), Times.Once); @@ -180,6 +183,20 @@ public void OnInitialize(string search, bool allFiles, string ignoreFiles, strin _searchMatchServiceMock.Verify(s => s.MatchItems(search, It.IsAny>()), Times.Once); } + [Test] + public void OnInitialize_ResetSearch() + { + var viewModel = GetViewModel(); + + _optionsServiceMock.Setup(o => o.GetStringOption(viewModel.Feature, "Solution", string.Empty)).Returns("something else").Verifiable(); + _optionsServiceMock.Setup(o => o.GetStringOption(viewModel.Feature, "Search", string.Empty)).Returns("hello").Verifiable(); + + viewModel.OnInitialize(null); + + Assert.That(viewModel.Search, Is.Null.Or.Empty, "Search should be reset on new solution"); + Assert.That(viewModel.SolutionName, Is.EqualTo("test")); + } + [TestCase(true, true)] [TestCase(true, false)] [TestCase(false, true)] @@ -187,6 +204,7 @@ public void OnClose(bool apply, bool selectCode) { var viewModel = GetViewModel(); + _optionsServiceMock.Setup(o => o.SetStringOption(viewModel.Feature, "Solution", string.Empty)).Verifiable(); _optionsServiceMock.Setup(o => o.SetStringOption(viewModel.Feature, "Search", string.Empty)).Verifiable(); _optionsServiceMock.Setup(o => o.SetBoolOption(viewModel.Feature, "AllFiles", false)).Verifiable(); @@ -198,6 +216,7 @@ public void OnClose(bool apply, bool selectCode) viewModel.OnClose(apply); + _optionsServiceMock.Verify(o => o.SetStringOption(viewModel.Feature, "Solution", string.Empty)); _optionsServiceMock.Verify(o => o.SetStringOption(viewModel.Feature, "Search", string.Empty)); _optionsServiceMock.Verify(o => o.SetBoolOption(viewModel.Feature, "AllFiles", false)); if (apply) diff --git a/DPackRx/CodeModel/FileCodeModel.cs b/DPackRx/CodeModel/FileCodeModel.cs index ff5ab8c..af63bff 100644 --- a/DPackRx/CodeModel/FileCodeModel.cs +++ b/DPackRx/CodeModel/FileCodeModel.cs @@ -1,239 +1,22 @@ -using System; -using System.ComponentModel; +using System.Collections.Generic; using System.Diagnostics; -using DPackRx.Helpers; - namespace DPackRx.CodeModel { - /// - /// File code model. - /// - [DebuggerDisplay("{Name}, {ElementModifier}, {ElementKind}, Matched = {Matched}")] - public class FileCodeModel : INotifyPropertyChanged, IMatchItem, IExtensibilityItem + [DebuggerDisplay("Member Count = {Members.Count}")] + public class FileCodeModel { - #region Fields - - private string _shortName; - private int _dataEndingIndex = NOT_SET; - private string _pascalCasedData; - - private const int NOT_SET = -10; - - #endregion - #region Properties /// - /// Item full name. - /// - public string FullName { get; set; } - - /// - /// Item name with immediate parent name. Qualified parents are classes, interfaces and Structs. - /// - public string ParentFullName { get; set; } - - /// - /// vsCMElement enum converted to int. - /// - public int CodeModelElementKind { get; set; } - - /// - /// Element kind. - /// - public Kind ElementKind { get; set; } - - /// - /// Element modifier. - /// - public Modifier ElementModifier { get; set; } - - /// - /// Whether item is a constant. - /// - public bool IsConstant { get; set; } - - /// - /// Whether item is static. - /// - public bool IsStatic { get; set; } - - /// - /// Item line number. - /// - public int Line { get; set; } - - /// - /// Item code snippet. - /// - public string Code { get; set; } - - /// - /// Item return type name. - /// - public string ReturnTypeName { get; set; } - - /// - /// Item Xml documentation. - /// - public string XmlDoc { get; set; } - - /// - /// Whether item's language supports generics. - /// - public bool SupportsGenerics { get; set; } - - /// - /// Item language generics suffix. - /// - public string GenericsSuffix { get; set; } - - /// - /// Last part of item name separated by '.'. - /// - public string ShortName - { - get - { - if (_shortName == null) - { - var index = this.Name.LastIndexOf(".", StringComparison.OrdinalIgnoreCase); - if (index >= 0) - _shortName = index == this.Name.Length ? string.Empty : this.Name.Substring(index + 1); - else - _shortName = this.Name; - } - - return _shortName; - } - } - - #endregion - - #region INotifyPropertyChanged Members - - public event PropertyChangedEventHandler PropertyChanged - { - add { } - remove { } - } - - #endregion - - #region IComparable Members - used for sorting - - public int CompareTo(object obj) - { - return CompareTo(obj as IMatchItem); - } - - #endregion - - #region IComparable Members - - public int CompareTo(IMatchItem obj) - { - var item = obj as FileCodeModel; - if (item == null) - return -1; - - if (((item.Rank == 0) && (this.Rank == 0)) || (item.Rank == this.Rank)) - { - return this.Line.CompareTo(item.Line); - } - else - { - // Higher ranked item must goes first - return item.Rank.CompareTo(this.Rank); - } - } - - #endregion - - #region IMatchItem Members - - /// - /// Data used for matching. - /// - public string Data - { - get { return this.ShortName; } - } - - /// - /// Data optional ending index. - /// Matching uses that to treat partial match up to index as an exact one. - /// It must be greater than 0. - /// - public int DataEndingIndex - { - get - { - if (_dataEndingIndex == NOT_SET) - { - if (this.SupportsGenerics && !string.IsNullOrEmpty(this.GenericsSuffix)) - { - var genericsStart = this.GenericsSuffix[0]; - _dataEndingIndex = this.ShortName.LastIndexOf(genericsStart); // looking for the start of generics definition here - } - else - { - _dataEndingIndex = this.ShortName.Length; // use the entire name - } - } - - return _dataEndingIndex; - } - } - - /// - /// Optional pascal cased version of Data used for matching. - /// - public string PascalCasedData - { - get - { - if (_pascalCasedData == null) - _pascalCasedData = SearchHelper.GetPascalCasedString(this.ShortName); - - return _pascalCasedData; - } - } - - /// - /// Item's match result. - /// - public bool Matched { get; set; } = true; - - /// - /// Match rank 0 and up, the higher the better matched item it is. - /// - public int Rank { get; set; } - - #endregion - - #region IExtensibilityItem Members - - /// - /// Item name. - /// - public string Name { get; set; } - - /// - /// Untyped extensibility link (name matches the actual type). - /// - public object ProjectItem { get; set; } - - /// - /// Item code type, whenever's applicable. + /// Full file name. /// - public FileSubType ItemSubType { get; } = FileSubType.None; + public string FileName { get; internal set; } = string.Empty; /// - /// Item parent code type, whenever's applicable. + /// Projects. /// - public FileSubType ParentSubType { get; set; } = FileSubType.Code; + public ICollection Members { get; internal set; } = new List(10); #endregion } diff --git a/DPackRx/CodeModel/FileModel.cs b/DPackRx/CodeModel/FileModel.cs index 10e7744..6cca1cc 100644 --- a/DPackRx/CodeModel/FileModel.cs +++ b/DPackRx/CodeModel/FileModel.cs @@ -58,7 +58,7 @@ public class FileModel : INotifyPropertyChanged, IMatchItem, IExtensibilityItem /// /// Optional file code members. /// - public ICollection Members { get; private set; } = new List(); + public ICollection Members { get; private set; } = new List(); #endregion diff --git a/DPackRx/CodeModel/FileProcessor.cs b/DPackRx/CodeModel/FileProcessor.cs index d2489fb..66f8e37 100644 --- a/DPackRx/CodeModel/FileProcessor.cs +++ b/DPackRx/CodeModel/FileProcessor.cs @@ -6,7 +6,6 @@ using DPackRx.Language; using DPackRx.Services; -using DPackFileCodeModel = DPackRx.CodeModel.FileCodeModel; using EnvDTE; using EnvDTE80; @@ -187,13 +186,13 @@ public bool IsDocumentValid(object document, out object projectItem) /// Processor flags. /// Code model filter. /// Code members. - public ICollection GetMembers(ProcessorFlags flags, CodeModelFilterFlags filter = CodeModelFilterFlags.All) + public FileCodeModel GetMembers(ProcessorFlags flags, CodeModelFilterFlags filter = CodeModelFilterFlags.All) { ThreadHelper.ThrowIfNotOnUIThread(); var item = _shellSelectionService.GetActiveItem(); if (item == null) - return new List(); + return new FileCodeModel(); return GetMembers(item, flags, filter); } @@ -205,13 +204,13 @@ public ICollection GetMembers(ProcessorFlags flags, CodeMode /// Processor flags. /// Code model filter. /// Code members. - public ICollection GetMembers(object projectItem, ProcessorFlags flags, CodeModelFilterFlags filter = CodeModelFilterFlags.All) + public FileCodeModel GetMembers(object projectItem, ProcessorFlags flags, CodeModelFilterFlags filter = CodeModelFilterFlags.All) { ThreadHelper.ThrowIfNotOnUIThread(); - var model = new List(10); + var model = new List(10); GetMembersInternal(projectItem, model, flags, filter); - return model; + return new FileCodeModel { FileName = _shellProjectService.GetItemFileName(projectItem), Members = model }; } #endregion @@ -221,7 +220,7 @@ public ICollection GetMembers(object projectItem, ProcessorF /// /// Returns project item code members. /// - private void GetMembersInternal(object projectItem, List model, ProcessorFlags flags, CodeModelFilterFlags filter = CodeModelFilterFlags.All) + private void GetMembersInternal(object projectItem, List model, ProcessorFlags flags, CodeModelFilterFlags filter = CodeModelFilterFlags.All) { ThreadHelper.ThrowIfNotOnUIThread(); @@ -271,7 +270,7 @@ private void GetMembersInternal(object projectItem, List mod /// /// Recursively processes qualified code elements. /// - private void ProcessCodeElement(ProjectItem item, CodeElement element, List model, ProcessorFlags flags, + private void ProcessCodeElement(ProjectItem item, CodeElement element, List model, ProcessorFlags flags, LanguageSettings languageSet, ICollection dteFilter, CodeModelFilterFlags filter, bool parented) { ThreadHelper.ThrowIfNotOnUIThread(); @@ -345,7 +344,7 @@ private void ProcessCodeElement(ProjectItem item, CodeElement element, List /// Creates code model element. /// - private void AddCodeElement(ProjectItem item, CodeElement parentElement, CodeElement element, List model, ProcessorFlags flags, + private void AddCodeElement(ProjectItem item, CodeElement parentElement, CodeElement element, List model, ProcessorFlags flags, LanguageSettings languageSet, ICollection dteFilter, CodeModelFilterFlags filter) { ThreadHelper.ThrowIfNotOnUIThread(); @@ -507,7 +506,7 @@ private void AddCodeElement(ProjectItem item, CodeElement parentElement, CodeEle } } - var modelItem = new DPackFileCodeModel + var member = new MemberCodeModel { ProjectItem = element.ProjectItem, Name = name, @@ -526,7 +525,7 @@ private void AddCodeElement(ProjectItem item, CodeElement parentElement, CodeEle XmlDoc = xmlDoc }; - model.Add(modelItem); + model.Add(member); } catch (COMException ex) { diff --git a/DPackRx/CodeModel/IFileProcessor.cs b/DPackRx/CodeModel/IFileProcessor.cs index 3b2ddf8..aafb7e0 100644 --- a/DPackRx/CodeModel/IFileProcessor.cs +++ b/DPackRx/CodeModel/IFileProcessor.cs @@ -22,7 +22,7 @@ public interface IFileProcessor /// Processor flags. /// Code model filter. /// Code members. - ICollection GetMembers(ProcessorFlags flags, CodeModelFilterFlags filter = CodeModelFilterFlags.All); + FileCodeModel GetMembers(ProcessorFlags flags, CodeModelFilterFlags filter = CodeModelFilterFlags.All); /// /// Returns item code members. @@ -31,7 +31,7 @@ public interface IFileProcessor /// Processor flags. /// Code model filter. /// Code members. - ICollection GetMembers(object projectItem, ProcessorFlags flags, CodeModelFilterFlags filter = CodeModelFilterFlags.All); + FileCodeModel GetMembers(object projectItem, ProcessorFlags flags, CodeModelFilterFlags filter = CodeModelFilterFlags.All); } #region CodeModelFilterFlags enum diff --git a/DPackRx/CodeModel/MemberCodeModel.cs b/DPackRx/CodeModel/MemberCodeModel.cs new file mode 100644 index 0000000..eb8c24c --- /dev/null +++ b/DPackRx/CodeModel/MemberCodeModel.cs @@ -0,0 +1,240 @@ +using System; +using System.ComponentModel; +using System.Diagnostics; + +using DPackRx.Helpers; + +namespace DPackRx.CodeModel +{ + /// + /// Member code model. + /// + [DebuggerDisplay("{Name}, {ElementModifier}, {ElementKind}, Matched = {Matched}")] + public class MemberCodeModel : INotifyPropertyChanged, IMatchItem, IExtensibilityItem + { + #region Fields + + private string _shortName; + private int _dataEndingIndex = NOT_SET; + private string _pascalCasedData; + + private const int NOT_SET = -10; + + #endregion + + #region Properties + + /// + /// Item full name. + /// + public string FullName { get; set; } + + /// + /// Item name with immediate parent name. Qualified parents are classes, interfaces and Structs. + /// + public string ParentFullName { get; set; } + + /// + /// vsCMElement enum converted to int. + /// + public int CodeModelElementKind { get; set; } + + /// + /// Element kind. + /// + public Kind ElementKind { get; set; } + + /// + /// Element modifier. + /// + public Modifier ElementModifier { get; set; } + + /// + /// Whether item is a constant. + /// + public bool IsConstant { get; set; } + + /// + /// Whether item is static. + /// + public bool IsStatic { get; set; } + + /// + /// Item line number. + /// + public int Line { get; set; } + + /// + /// Item code snippet. + /// + public string Code { get; set; } + + /// + /// Item return type name. + /// + public string ReturnTypeName { get; set; } + + /// + /// Item Xml documentation. + /// + public string XmlDoc { get; set; } + + /// + /// Whether item's language supports generics. + /// + public bool SupportsGenerics { get; set; } + + /// + /// Item language generics suffix. + /// + public string GenericsSuffix { get; set; } + + /// + /// Last part of item name separated by '.'. + /// + public string ShortName + { + get + { + if (_shortName == null) + { + var index = this.Name.LastIndexOf(".", StringComparison.OrdinalIgnoreCase); + if (index >= 0) + _shortName = index == this.Name.Length ? string.Empty : this.Name.Substring(index + 1); + else + _shortName = this.Name; + } + + return _shortName; + } + } + + #endregion + + #region INotifyPropertyChanged Members + + public event PropertyChangedEventHandler PropertyChanged + { + add { } + remove { } + } + + #endregion + + #region IComparable Members - used for sorting + + public int CompareTo(object obj) + { + return CompareTo(obj as IMatchItem); + } + + #endregion + + #region IComparable Members + + public int CompareTo(IMatchItem obj) + { + var member = obj as MemberCodeModel; + if (member == null) + return -1; + + if (((member.Rank == 0) && (this.Rank == 0)) || (member.Rank == this.Rank)) + { + return this.Line.CompareTo(member.Line); + } + else + { + // Higher ranked item must goes first + return member.Rank.CompareTo(this.Rank); + } + } + + #endregion + + #region IMatchItem Members + + /// + /// Data used for matching. + /// + public string Data + { + get { return this.ShortName; } + } + + /// + /// Data optional ending index. + /// Matching uses that to treat partial match up to index as an exact one. + /// It must be greater than 0. + /// + public int DataEndingIndex + { + get + { + if (_dataEndingIndex == NOT_SET) + { + if (this.SupportsGenerics && !string.IsNullOrEmpty(this.GenericsSuffix)) + { + var genericsStart = this.GenericsSuffix[0]; + _dataEndingIndex = this.ShortName.LastIndexOf(genericsStart); // looking for the start of generics definition here + } + else + { + _dataEndingIndex = this.ShortName.Length; // use the entire name + } + } + + return _dataEndingIndex; + } + } + + /// + /// Optional pascal cased version of Data used for matching. + /// + public string PascalCasedData + { + get + { + if (_pascalCasedData == null) + _pascalCasedData = SearchHelper.GetPascalCasedString(this.ShortName); + + return _pascalCasedData; + } + } + + /// + /// Item's match result. + /// + public bool Matched { get; set; } = true; + + /// + /// Match rank 0 and up, the higher the better matched item it is. + /// + public int Rank { get; set; } + + #endregion + + #region IExtensibilityItem Members + + /// + /// Item name. + /// + public string Name { get; set; } + + /// + /// Untyped extensibility link (name matches the actual type). + /// + public object ProjectItem { get; set; } + + /// + /// Item code type, whenever's applicable. + /// + public FileSubType ItemSubType { get; } = FileSubType.None; + + /// + /// Item parent code type, whenever's applicable. + /// + public FileSubType ParentSubType { get; set; } = FileSubType.Code; + + #endregion + } +} \ No newline at end of file diff --git a/DPackRx/CodeModel/ProjectProcessor.cs b/DPackRx/CodeModel/ProjectProcessor.cs index d503781..be8cf42 100644 --- a/DPackRx/CodeModel/ProjectProcessor.cs +++ b/DPackRx/CodeModel/ProjectProcessor.cs @@ -214,9 +214,9 @@ private void ProcessProjectItems(List model, ProcessorFlags flags, Co if (flags.HasFlag(ProcessorFlags.IncludeFileCodeModel)) { - var members = _fileProcessor.GetMembers(projectItem, flags, filter); - if (members != null) - itemModel.Members.AddRange(members); + var codeMembers = _fileProcessor.GetMembers(projectItem, flags, filter); + if ((codeMembers != null) && (codeMembers.Members != null)) + itemModel.Members.AddRange(codeMembers.Members); } model.Add(itemModel); diff --git a/DPackRx/CodeModel/SolutionModel.cs b/DPackRx/CodeModel/SolutionModel.cs index aa696d6..4f98285 100644 --- a/DPackRx/CodeModel/SolutionModel.cs +++ b/DPackRx/CodeModel/SolutionModel.cs @@ -18,6 +18,11 @@ public class SolutionModel #region Properties + /// + /// Solution file name without extension. + /// + public string SolutionName { get; internal set; } + /// /// Projects. /// diff --git a/DPackRx/CodeModel/SolutionProcessor.cs b/DPackRx/CodeModel/SolutionProcessor.cs index 6840766..2eceaa2 100644 --- a/DPackRx/CodeModel/SolutionProcessor.cs +++ b/DPackRx/CodeModel/SolutionProcessor.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using DPackRx.Extensions; @@ -64,6 +65,13 @@ private void GetProjectsInternal(SolutionModel model, ProcessorFlags flags, Code var dte = _shellProjectService.GetDTE() as DTE; + _log.LogMessage("Resolving solution name"); + + var solution = dte.Solution?.FileName; + if (!string.IsNullOrEmpty(solution)) + solution = Path.GetFileNameWithoutExtension(solution); + model.SolutionName = solution; + _log.LogMessage("Collecting solution projects"); var projects = dte.Solution.Projects; diff --git a/DPackRx/DPackRx.csproj b/DPackRx/DPackRx.csproj index a3bb67a..6725f9f 100644 --- a/DPackRx/DPackRx.csproj +++ b/DPackRx/DPackRx.csproj @@ -52,9 +52,10 @@ rules.ruleset + - + diff --git a/DPackRx/Features/CodeBrowser/CodeBrowserViewModel.cs b/DPackRx/Features/CodeBrowser/CodeBrowserViewModel.cs index c422a80..d302a59 100644 --- a/DPackRx/Features/CodeBrowser/CodeBrowserViewModel.cs +++ b/DPackRx/Features/CodeBrowser/CodeBrowserViewModel.cs @@ -33,7 +33,7 @@ public class CodeBrowserViewModel : FeatureViewModelBase private readonly ISearchMatchService _searchMatchService; private readonly IShellSelectionService _shellSelectionService; private readonly IShellImageService _shellImageService; - private readonly ObservableCollection _sourceMembers; + private readonly ObservableCollection _sourceMembers; private readonly CollectionViewSource _members; private string _search = string.Empty; @@ -58,7 +58,7 @@ public CodeBrowserViewModel(IServiceProvider serviceProvider, ILog log, IMessage _shellImageService = shellImageService; // Source members must be setup in constructor or view won't show any binding data - _sourceMembers = new ObservableCollection(); + _sourceMembers = new ObservableCollection(); _members = new CollectionViewSource { Source = _sourceMembers }; // must be ObservableCollection this.ShowAllMembersCommand = new RelayCommand(_messageService, OnShowAllMembers, OnCanShowAllMembers); @@ -78,6 +78,7 @@ public override void OnInitialize(object argument) if (!(argument is CodeModelFilterFlags)) throw new ArgumentException("Invalid initialization argument", nameof(argument)); + this.FileName = _optionsService.GetStringOption(this.Feature, "File", this.FileName); _search = _optionsService.GetStringOption(this.Feature, "Search", _search); this.Filter = (CodeModelFilterFlags)argument; var filter = (CodeModelFilterFlags)_optionsService.GetIntOption(this.Feature, "Filter", (int)this.Filter); @@ -102,6 +103,7 @@ public override void OnInitialize(object argument) /// public override void OnClose(bool apply) { + _optionsService.SetStringOption(this.Feature, "File", this.FileName); _optionsService.SetStringOption(this.Feature, "Search", _search); _optionsService.SetIntOption(this.Feature, "Filter", (int)this.Filter); @@ -111,11 +113,11 @@ public override void OnClose(bool apply) if (this.Selection == null) return; - var fileCodeModel = this.Selection as FileCodeModel; - if (fileCodeModel == null) + var member = this.Selection as MemberCodeModel; + if (member == null) return; - _shellSelectionService.SetActiveFilePosition(fileCodeModel.Line, 1); + _shellSelectionService.SetActiveFilePosition(member.Line, 1); } #endregion @@ -169,9 +171,9 @@ public ICollectionView Members /// Filtered members. /// /// Exposed for testing purposes only. - protected internal IList FilteredMembers + protected internal IList FilteredMembers { - get { return ((IEnumerable)_members?.View).Cast().ToList(); } + get { return ((IEnumerable)_members?.View).Cast().ToList(); } } /// @@ -203,6 +205,12 @@ public ImageSource ImageShowAllMembers /// Exposed for testing purposes only. protected internal IExtensibilityItem Selection { get; set; } + /// + /// Full file name. + /// + /// Exposed for testing purposes only. + protected internal string FileName { get; private set; } = string.Empty; + #endregion #region Private Methods @@ -216,7 +224,14 @@ private void ApplyMembers() if (_optionsService.GetBoolOption(this.Feature, "XmlDoc")) flags = flags | ProcessorFlags.IncludeMemberXmlDoc; - var members = _fileProcessor.GetMembers(flags, this.Filter); + var model = _fileProcessor.GetMembers(flags, this.Filter); + var members = model.Members; + var fileName = model.FileName; + + // Reset search on new file + if (!string.IsNullOrEmpty(this.FileName) && !string.IsNullOrEmpty(fileName) && !fileName.Equals(this.FileName, StringComparison.OrdinalIgnoreCase)) + _search = string.Empty; + this.FileName = fileName; _sourceMembers.Clear(); _sourceMembers.AddRange(members); // causes filter to be evaluated @@ -244,14 +259,14 @@ private void OnFilter(object sender, FilterEventArgs e) return; } - if (!(e.Item is FileCodeModel)) + if (!(e.Item is MemberCodeModel)) { e.Accepted = false; return; } - var fileCodeModel = (FileCodeModel)e.Item; - e.Accepted = fileCodeModel.Matched; + var member = (MemberCodeModel)e.Item; + e.Accepted = member.Matched; } /// @@ -293,7 +308,7 @@ private bool OnCanSelectMember(object argument) /// Optional Xaml defined parameter. private void OnSelectMember(object obj) { - this.Selection = obj as FileCodeModel; + this.Selection = obj as MemberCodeModel; this.CloseWindow = true; } diff --git a/DPackRx/Features/FileBrowser/FileBrowserViewModel.cs b/DPackRx/Features/FileBrowser/FileBrowserViewModel.cs index d76d42a..1524d76 100644 --- a/DPackRx/Features/FileBrowser/FileBrowserViewModel.cs +++ b/DPackRx/Features/FileBrowser/FileBrowserViewModel.cs @@ -89,12 +89,20 @@ public override void OnInitialize(object argument) { _log.LogMessage(this.Feature, "Initializing..."); - var files = _solutionProcessor.GetProjects(ProcessorFlags.IncludeFiles | ProcessorFlags.GroupLinkedFiles).Files; + var model = _solutionProcessor.GetProjects(ProcessorFlags.IncludeFiles | ProcessorFlags.GroupLinkedFiles); + var files = model.Files; + var solutionName = model.SolutionName; files = ApplyOptions(files); + this.SolutionName = _optionsService.GetStringOption(this.Feature, "Solution", this.SolutionName); _search = _optionsService.GetStringOption(this.Feature, "Search", _search); _allFiles = _optionsService.GetBoolOption(this.Feature, "AllFiles", _allFiles); + // Reset search on new solution + if (!string.IsNullOrEmpty(this.SolutionName) && !string.IsNullOrEmpty(solutionName) && !solutionName.Equals(this.SolutionName, StringComparison.OrdinalIgnoreCase)) + _search = string.Empty; + this.SolutionName = solutionName; + _sourceFiles.Clear(); _sourceFiles.AddRange(files); // causes filter to be evaluated @@ -114,6 +122,7 @@ public override void OnInitialize(object argument) /// public override void OnClose(bool apply) { + _optionsService.SetStringOption(this.Feature, "Solution", this.SolutionName); _optionsService.SetStringOption(this.Feature, "Search", _search); _optionsService.SetBoolOption(this.Feature, "AllFiles", _allFiles); @@ -290,6 +299,12 @@ public ImageSource ImageOpenCodeBrowserProperties /// Exposed for testing purposes only. protected internal int CodeBrowserCommandId { get; set; } + /// + /// Solution name without extension. + /// + /// Exposed for testing purposes only. + protected internal string SolutionName { get; private set; } = string.Empty; + #endregion #region Private Methods diff --git a/DPackRx/Package/Beta.cs b/DPackRx/Package/Beta.cs index 76c08b4..2af995d 100644 --- a/DPackRx/Package/Beta.cs +++ b/DPackRx/Package/Beta.cs @@ -8,7 +8,7 @@ namespace DPackRx.Package /// internal static class Beta { - public static readonly DateTime ExpirationDate = new DateTime(2020, 4, 6).AddHours(12); + public static readonly DateTime ExpirationDate = new DateTime(2020, 5, 25).AddHours(12); } #endif } \ No newline at end of file diff --git a/DPackRx/Services/AsyncTaskService.cs b/DPackRx/Services/AsyncTaskService.cs index 1c02ad7..ce66ea7 100644 --- a/DPackRx/Services/AsyncTaskService.cs +++ b/DPackRx/Services/AsyncTaskService.cs @@ -21,7 +21,7 @@ public class AsyncTaskService : IAsyncTaskService /// /// Minimum delay that is not perceived as long wait. /// - private const double DELAY_MSCES = 700; + private const double DELAY_MSCES = 750; #endregion diff --git a/DPackRx/Services/IShellProjectService.cs b/DPackRx/Services/IShellProjectService.cs index 05c7ab2..9044467 100644 --- a/DPackRx/Services/IShellProjectService.cs +++ b/DPackRx/Services/IShellProjectService.cs @@ -84,5 +84,10 @@ public interface IShellProjectService /// Returns untyped ProjectItem instance for untyped Document instance. /// object GetDocumentProjectItem(object document); + + /// + /// Returns untyped Project file name. + /// + string GetItemFileName(object projectItem); } } \ No newline at end of file diff --git a/DPackRx/Services/ShellService.cs b/DPackRx/Services/ShellService.cs index a80e98a..e0e5891 100644 --- a/DPackRx/Services/ShellService.cs +++ b/DPackRx/Services/ShellService.cs @@ -1289,6 +1289,23 @@ public object GetDocumentProjectItem(object document) return null; } + /// + /// Returns untyped Project file name. + /// + public string GetItemFileName(object projectItem) + { + ThreadHelper.ThrowIfNotOnUIThread(); + + var dteItem = GetProjectItemInternal(projectItem, false); + if (dteItem == null) + return null; + + if (dteItem.FileCount == 0) + return null; + + return dteItem.FileNames[1]; + } + #endregion #region IShellCodeModelService Members diff --git a/DPackRx/UI/Converters/FileCodeModelToImageConverter.cs b/DPackRx/UI/Converters/FileCodeModelToImageConverter.cs index e60050c..0e2bc97 100644 --- a/DPackRx/UI/Converters/FileCodeModelToImageConverter.cs +++ b/DPackRx/UI/Converters/FileCodeModelToImageConverter.cs @@ -13,9 +13,9 @@ namespace DPackRx.UI.Converters { /// - /// Converts to type. + /// Converts to type. /// - [ValueConversion(typeof(FileCodeModel), typeof(ImageSource))] + [ValueConversion(typeof(MemberCodeModel), typeof(ImageSource))] public class FileCodeModelToImageConverter : MarkupExtension, IValueConverter { #region Fields @@ -39,15 +39,15 @@ public override object ProvideValue(IServiceProvider serviceProvider) public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - if (!(value is FileCodeModel)) + if (!(value is MemberCodeModel)) return null; var imageService = GetImageService(); if (imageService == null) return null; - var fileCodeModel = (FileCodeModel)value; - return imageService.GetMemberImage(fileCodeModel.ElementModifier, fileCodeModel.ElementKind, fileCodeModel.IsStatic); + var member = (MemberCodeModel)value; + return imageService.GetMemberImage(member.ElementModifier, member.ElementKind, member.IsStatic); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) diff --git a/DPackRx/source.extension.vsixmanifest b/DPackRx/source.extension.vsixmanifest index e48b69c..57a800b 100644 --- a/DPackRx/source.extension.vsixmanifest +++ b/DPackRx/source.extension.vsixmanifest @@ -1,7 +1,7 @@  - + DPack Rx FREE tools collection designed to greatly increase developer's productivity, automate repetitive processes and expand upon some of Microsoft Visual Studio features. Visual Studio 2017 and 2019 are supported. https://github.com/usysware/dpack