From 64cc6b7af7f55099866a5064094c74b3f150b387 Mon Sep 17 00:00:00 2001 From: Davide Giacometti Date: Wed, 20 Oct 2021 15:27:15 +0200 Subject: [PATCH] [PT Run] [Folder Plugin] Environment Variables With Autocomplete (#13811) * search environment variables folders with autocomplete * refactoring and tests * fix --- .github/actions/spell-check/expect.txt | 1 + .../EnvironmentHelperTests.cs | 29 ++++++++ .../QueryEnvironmentVariableTests.cs | 67 +++++++++++++++++++ .../EnvironmentVariableProcessor.cs | 39 +++++++++++ .../Plugins/Microsoft.Plugin.Folder/Main.cs | 3 + .../Sources/EnvironmentHelper.cs | 27 ++++++++ .../Sources/IEnvironmentHelper.cs | 15 +++++ .../Sources/IQueryEnvironmentVariable.cs | 14 ++++ .../Sources/QueryEnvironmentVariable.cs | 56 ++++++++++++++++ .../Result/EnvironmentVariableResult.cs | 45 +++++++++++++ 10 files changed, 296 insertions(+) create mode 100644 src/modules/launcher/Plugins/Microsoft.Plugin.Folder.UnitTests/EnvironmentHelperTests.cs create mode 100644 src/modules/launcher/Plugins/Microsoft.Plugin.Folder.UnitTests/QueryEnvironmentVariableTests.cs create mode 100644 src/modules/launcher/Plugins/Microsoft.Plugin.Folder/EnvironmentVariableProcessor.cs create mode 100644 src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/EnvironmentHelper.cs create mode 100644 src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/IEnvironmentHelper.cs create mode 100644 src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/IQueryEnvironmentVariable.cs create mode 100644 src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/QueryEnvironmentVariable.cs create mode 100644 src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/EnvironmentVariableResult.cs diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 6d9f33a32f..d678404ba5 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -868,6 +868,7 @@ IEasing IEnum IEnumerable IEnumerator +IEnvironment IEquality IEquatable IEvent diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder.UnitTests/EnvironmentHelperTests.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder.UnitTests/EnvironmentHelperTests.cs new file mode 100644 index 0000000000..67a1686697 --- /dev/null +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder.UnitTests/EnvironmentHelperTests.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.Plugin.Folder.Sources; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Microsoft.Plugin.Folder.UnitTests +{ + [TestClass] + public class EnvironmentHelperTests + { + [DataTestMethod] + [DataRow(@"%", true)] + [DataRow(@"%P", true)] + [DataRow(@"%PROGRAMDATA%", true)] + [DataRow(@"", false)] + [DataRow(@"C:\ProgramData", false)] + + public void IsValidEnvironmentVariable(string search, bool expectedSuccess) + { + var helper = new EnvironmentHelper(); + + var result = helper.IsEnvironmentVariable(search); + + Assert.AreEqual(expectedSuccess, result); + } + } +} diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder.UnitTests/QueryEnvironmentVariableTests.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder.UnitTests/QueryEnvironmentVariableTests.cs new file mode 100644 index 0000000000..ff93b9ad98 --- /dev/null +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder.UnitTests/QueryEnvironmentVariableTests.cs @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.IO.Abstractions.TestingHelpers; +using System.Linq; +using Microsoft.Plugin.Folder.Sources; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; + +namespace Microsoft.Plugin.Folder.UnitTests +{ + [TestClass] + public class QueryEnvironmentVariableTests + { + private static IQueryEnvironmentVariable _queryEnvironmentVariable; + private static MockFileSystem _fileSystem; + + [TestInitialize] + public void SetupMock() + { + var environmentHelperMock = new Mock(); + + environmentHelperMock + .Setup(h => h.GetEnvironmentVariables()) + .Returns(() => new Dictionary + { + { "OS", "Windows_NT" }, + { "WINDIR", @"C:\Windows" }, + { "PROGRAMDATA", @"C:\ProgramData" }, + { "PROGRAMFILES", @"C:\Program Files" }, + }); + + _fileSystem = new MockFileSystem(new Dictionary() + { + { @"C:\Windows", new MockDirectoryData() }, + { @"C:\ProgramData", new MockDirectoryData() }, + { @"C:\Program Files", new MockDirectoryData() }, + }); + + _queryEnvironmentVariable = new QueryEnvironmentVariable(_fileSystem.Directory, environmentHelperMock.Object); + } + + [DataTestMethod] + [DataRow(@"%OS%")] // Not a directory + [DataRow(@"%CUSTOM%")] // Directory doesn't exist + [DataRow(@"WINDIR")] // Not an environment variable + public void QueryWithEmptyResults(string search) + { + var results = _queryEnvironmentVariable.Query(search); + Assert.AreEqual(results.Count(), 0); + } + + [DataTestMethod] + [DataRow(@"", 3)] + [DataRow(@"%", 3)] + [DataRow(@"%WIN", 1)] + [DataRow(@"%WINDIR%", 1)] + [DataRow(@"%PROGRAM", 2)] + public void QueryWithResults(string search, int numberOfResults) + { + var results = _queryEnvironmentVariable.Query(search); + Assert.AreEqual(results.Count(), numberOfResults); + } + } +} diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/EnvironmentVariableProcessor.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/EnvironmentVariableProcessor.cs new file mode 100644 index 0000000000..58a5be8c76 --- /dev/null +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/EnvironmentVariableProcessor.cs @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Plugin.Folder.Sources; +using Microsoft.Plugin.Folder.Sources.Result; + +namespace Microsoft.Plugin.Folder +{ + public class EnvironmentVariableProcessor : IFolderProcessor + { + private readonly IEnvironmentHelper _environmentHelper; + private readonly IQueryEnvironmentVariable _queryEnvironmentVariable; + + public EnvironmentVariableProcessor(IEnvironmentHelper environmentHelper, IQueryEnvironmentVariable queryEnvironmentVariable) + { + _environmentHelper = environmentHelper; + _queryEnvironmentVariable = queryEnvironmentVariable; + } + + public IEnumerable Results(string actionKeyword, string search) + { + if (search == null) + { + throw new ArgumentNullException(nameof(search)); + } + + if (!_environmentHelper.IsEnvironmentVariable(search)) + { + return Enumerable.Empty(); + } + + return _queryEnvironmentVariable.Query(search); + } + } +} diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Main.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Main.cs index b48195fa2e..12763596f6 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Main.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Main.cs @@ -26,11 +26,14 @@ namespace Microsoft.Plugin.Folder private static readonly FolderSettings _settings = _storage.Load(); private static readonly IQueryInternalDirectory _internalDirectory = new QueryInternalDirectory(_settings, new QueryFileSystemInfo(_fileSystem.DirectoryInfo), _fileSystem.Directory); private static readonly FolderHelper _folderHelper = new FolderHelper(new DriveInformation(), new FolderLinksSettings(_settings)); + private static readonly IEnvironmentHelper _environmentHelper = new EnvironmentHelper(); + private static readonly IQueryEnvironmentVariable _queryEnvironmentVariable = new QueryEnvironmentVariable(_fileSystem.Directory, _environmentHelper); private static readonly ICollection _processors = new IFolderProcessor[] { new UserFolderProcessor(_folderHelper), new InternalDirectoryProcessor(_folderHelper, _internalDirectory), + new EnvironmentVariableProcessor(_environmentHelper, _queryEnvironmentVariable), }; private static PluginInitContext _context; diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/EnvironmentHelper.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/EnvironmentHelper.cs new file mode 100644 index 0000000000..795c710cd2 --- /dev/null +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/EnvironmentHelper.cs @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections; + +namespace Microsoft.Plugin.Folder.Sources +{ + public class EnvironmentHelper : IEnvironmentHelper + { + public bool IsEnvironmentVariable(string search) + { + if (search == null) + { + throw new ArgumentNullException(paramName: nameof(search)); + } + + return search.StartsWith('%'); + } + + public IDictionary GetEnvironmentVariables() + { + return Environment.GetEnvironmentVariables(); + } + } +} diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/IEnvironmentHelper.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/IEnvironmentHelper.cs new file mode 100644 index 0000000000..21ce19d33e --- /dev/null +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/IEnvironmentHelper.cs @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; + +namespace Microsoft.Plugin.Folder.Sources +{ + public interface IEnvironmentHelper + { + bool IsEnvironmentVariable(string search); + + IDictionary GetEnvironmentVariables(); + } +} diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/IQueryEnvironmentVariable.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/IQueryEnvironmentVariable.cs new file mode 100644 index 0000000000..955221e590 --- /dev/null +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/IQueryEnvironmentVariable.cs @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using Microsoft.Plugin.Folder.Sources.Result; + +namespace Microsoft.Plugin.Folder.Sources +{ + public interface IQueryEnvironmentVariable + { + IEnumerable Query(string querySearch); + } +} diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/QueryEnvironmentVariable.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/QueryEnvironmentVariable.cs new file mode 100644 index 0000000000..49c0f1d29a --- /dev/null +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/QueryEnvironmentVariable.cs @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO.Abstractions; +using System.Linq; +using Microsoft.Plugin.Folder.Sources.Result; + +namespace Microsoft.Plugin.Folder.Sources +{ + public class QueryEnvironmentVariable : IQueryEnvironmentVariable + { + private readonly IDirectory _directory; + private readonly IEnvironmentHelper _environmentHelper; + + public QueryEnvironmentVariable(IDirectory directory, IEnvironmentHelper environmentHelper) + { + _directory = directory; + _environmentHelper = environmentHelper; + } + + public IEnumerable Query(string querySearch) + { + if (querySearch == null) + { + throw new ArgumentNullException(nameof(querySearch)); + } + + return GetEnvironmentVariables(querySearch) + .OrderBy(v => v.Title) + .Where(v => v.Title.StartsWith(querySearch, StringComparison.InvariantCultureIgnoreCase)); + } + + public IEnumerable GetEnvironmentVariables(string querySearch) + { + foreach (DictionaryEntry variable in _environmentHelper.GetEnvironmentVariables()) + { + if (variable.Value == null) + { + continue; + } + + var name = "%" + (string)variable.Key + "%"; + var path = (string)variable.Value; + + if (_directory.Exists(path)) + { + yield return new EnvironmentVariableResult(querySearch, name, path); + } + } + } + } +} diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/EnvironmentVariableResult.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/EnvironmentVariableResult.cs new file mode 100644 index 0000000000..f7faa06286 --- /dev/null +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/EnvironmentVariableResult.cs @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Globalization; +using Wox.Infrastructure; +using Wox.Plugin; + +namespace Microsoft.Plugin.Folder.Sources.Result +{ + public class EnvironmentVariableResult : IItemResult + { + private readonly IShellAction _shellAction = new ShellAction(); + + public string Search { get; set; } + + public string Title { get; private set; } + + public string Subtitle { get; private set; } + + public string Path { get; private set; } + + public EnvironmentVariableResult(string search, string title, string path) + { + Search = search; + Title = title; + Path = path; + } + + public Wox.Plugin.Result Create(IPublicAPI contextApi) + { + return new Wox.Plugin.Result(StringMatcher.FuzzySearch(Search, Title).MatchData) + { + Title = Title, + IcoPath = Path, + + // Using CurrentCulture since this is user facing + SubTitle = string.Format(CultureInfo.CurrentCulture, Properties.Resources.wox_plugin_folder_select_folder_result_subtitle, Path), + QueryTextDisplay = Path, + ContextData = new SearchResult { Type = ResultType.Folder, FullPath = Path }, + Action = c => _shellAction.Execute(Path, contextApi), + }; + } + } +}