From 92c85630a988a6648a9959f2d1d4b97018bd6707 Mon Sep 17 00:00:00 2001 From: Davide Giacometti Date: Tue, 20 Feb 2024 13:08:54 +0100 Subject: [PATCH] [PTRun][Calculator]Replace input with result with '=' key (#31391) * replace input with result * fix and add test * add options and use result property --- .../QueryTests.cs | 64 +++++++++++++------ .../Main.cs | 43 +++++++++++-- .../Properties/Resources.Designer.cs | 18 ++++++ .../Properties/Resources.resx | 6 ++ .../ResultHelper.cs | 11 ++-- .../launcher/PowerLauncher/MainWindow.xaml.cs | 3 - .../PowerLauncher/PublicAPIInstance.cs | 5 +- .../PowerLauncher/ViewModel/MainViewModel.cs | 8 ++- 8 files changed, 121 insertions(+), 37 deletions(-) diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/QueryTests.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/QueryTests.cs index 746206d8ed..0f8328ef10 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/QueryTests.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/QueryTests.cs @@ -4,6 +4,7 @@ using System.Globalization; using System.Linq; +using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using Wox.Plugin; @@ -13,6 +14,19 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator.UnitTests [TestClass] public class QueryTests { + private static Mock
_main; + private static Mock _api; + + [TestInitialize] + public void Initialize() + { + _api = new Mock(); + _api.Setup(api => api.ChangeQuery(It.IsAny(), It.IsAny())).Verifiable(); + + _main = new Mock
(); + _main.Object.Init(new PluginInitContext() { API = _api.Object }); + } + [DataTestMethod] [DataRow("=pi(9+)", "Expression wrong or incomplete (Did you forget some parentheses?)")] [DataRow("=pi,", "Expression wrong or incomplete (Did you forget some parentheses?)")] @@ -28,12 +42,10 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator.UnitTests [DataRow("10+(8*9)/0*7", "Expression contains division by zero")] public void ErrorResultOnInvalidKeywordQuery(string typedString, string expectedResult) { - // Setup - Mock
main = new(); Query expectedQuery = new(typedString, "="); // Act - var result = main.Object.Query(expectedQuery).FirstOrDefault().SubTitle; + var result = _main.Object.Query(expectedQuery).FirstOrDefault().SubTitle; // Assert Assert.AreEqual(expectedResult, result); @@ -55,11 +67,10 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator.UnitTests public void NoResultOnInvalidGlobalQuery(string typedString) { // Setup - Mock
main = new(); Query expectedQuery = new(typedString); // Act - var result = main.Object.Query(expectedQuery).Count; + var result = _main.Object.Query(expectedQuery).Count; // Assert Assert.AreEqual(result, 0); @@ -79,13 +90,12 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator.UnitTests public void NoResultIfQueryEndsWithBinaryOperator(string typedString) { // Setup - Mock
main = new(); Query expectedQuery = new(typedString); Query expectedQueryWithKeyword = new("=" + typedString, "="); // Act - var result = main.Object.Query(expectedQuery).Count; - var resultWithKeyword = main.Object.Query(expectedQueryWithKeyword).Count; + var result = _main.Object.Query(expectedQuery).Count; + var resultWithKeyword = _main.Object.Query(expectedQueryWithKeyword).Count; // Assert Assert.AreEqual(result, 0); @@ -100,13 +110,12 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator.UnitTests public void NoErrorForDivisionByNumberWithDecimalDigits(string typedString) { // Setup - Mock
main = new(); Query expectedQuery = new(typedString); Query expectedQueryWithKeyword = new("=" + typedString, "="); // Act - var result = main.Object.Query(expectedQuery).FirstOrDefault().SubTitle; - var resultWithKeyword = main.Object.Query(expectedQueryWithKeyword).FirstOrDefault().SubTitle; + var result = _main.Object.Query(expectedQuery).FirstOrDefault().SubTitle; + var resultWithKeyword = _main.Object.Query(expectedQueryWithKeyword).FirstOrDefault().SubTitle; // Assert Assert.AreEqual(result, "Copy this number to the clipboard"); @@ -190,13 +199,12 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator.UnitTests public void NoErrorForHumanMultiplicationExpressions(string typedString) { // Setup - Mock
main = new(); Query expectedQuery = new(typedString); Query expectedQueryWithKeyword = new("=" + typedString, "="); // Act - var result = main.Object.Query(expectedQuery).FirstOrDefault()?.SubTitle; - var resultWithKeyword = main.Object.Query(expectedQueryWithKeyword).FirstOrDefault()?.SubTitle; + var result = _main.Object.Query(expectedQuery).FirstOrDefault()?.SubTitle; + var resultWithKeyword = _main.Object.Query(expectedQueryWithKeyword).FirstOrDefault()?.SubTitle; // Assert Assert.AreEqual("Copy this number to the clipboard", result); @@ -221,13 +229,12 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator.UnitTests public void RightAnswerForHumanMultiplicationExpressions(string typedString, double answer) { // Setup - Mock
main = new(); Query expectedQuery = new(typedString); Query expectedQueryWithKeyword = new("=" + typedString, "="); // Act - var result = main.Object.Query(expectedQuery).FirstOrDefault()?.Title; - var resultWithKeyword = main.Object.Query(expectedQueryWithKeyword).FirstOrDefault()?.Title; + var result = _main.Object.Query(expectedQuery).FirstOrDefault()?.Title; + var resultWithKeyword = _main.Object.Query(expectedQueryWithKeyword).FirstOrDefault()?.Title; // Assert Assert.AreEqual(answer.ToString(CultureInfo.CurrentCulture), result); @@ -242,17 +249,34 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator.UnitTests public void RightAnswerForLargeHexadecimalNumbers(string typedString, double answer) { // Setup - Mock
main = new(); Query expectedQuery = new(typedString); Query expectedQueryWithKeyword = new("=" + typedString, "="); // Act - var result = main.Object.Query(expectedQuery).FirstOrDefault()?.Title; - var resultWithKeyword = main.Object.Query(expectedQueryWithKeyword).FirstOrDefault()?.Title; + var result = _main.Object.Query(expectedQuery).FirstOrDefault()?.Title; + var resultWithKeyword = _main.Object.Query(expectedQueryWithKeyword).FirstOrDefault()?.Title; // Assert Assert.AreEqual(answer.ToString(CultureInfo.CurrentCulture), result); Assert.AreEqual(answer.ToString(CultureInfo.CurrentCulture), resultWithKeyword); } + + [DataTestMethod] + [DataRow("#", "#1+1=", true)] + [DataRow("#", "#1+1", false)] + [DataRow("#", "#1/0=", false)] + [DataRow("", "1+1=", false)] + public void HandleInputReplace(string actionKeyword, string query, bool shouldChangeQuery) + { + // Setup + Query expectedQuery = new(query, actionKeyword); + _main.Object.UpdateSettings(new PowerLauncherPluginSettings()); // Default settings + + // Act + _main.Object.Query(expectedQuery); + + // Assert + _api.Verify(api => api.ChangeQuery(It.IsAny(), It.IsAny()), shouldChangeQuery ? Times.Once : Times.Never); + } } } diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/Main.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/Main.cs index 3a1d0fec78..fccb4ead7c 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/Main.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/Main.cs @@ -17,6 +17,10 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator { public class Main : IPlugin, IPluginI18n, IDisposable, ISettingProvider { + private const string InputUseEnglishFormat = nameof(InputUseEnglishFormat); + private const string OutputUseEnglishFormat = nameof(OutputUseEnglishFormat); + private const string ReplaceInput = nameof(ReplaceInput); + private static readonly CalculateEngine CalculateEngine = new CalculateEngine(); private PluginInitContext Context { get; set; } @@ -25,6 +29,7 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator private bool _inputUseEnglishFormat; private bool _outputUseEnglishFormat; + private bool _replaceInput; public string Name => Resources.wox_plugin_calculator_plugin_name; @@ -40,20 +45,27 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator public IEnumerable AdditionalOptions => new List() { // The number examples has to be created at runtime to prevent translation. - new PluginAdditionalOption() + new PluginAdditionalOption { - Key = "InputUseEnglishFormat", + Key = InputUseEnglishFormat, DisplayLabel = Resources.wox_plugin_calculator_in_en_format, DisplayDescription = string.Format(CultureInfo.CurrentCulture, WoxPluginCalculatorInEnFormatDescription, 1000.55.ToString("N2", new CultureInfo("en-us"))), Value = false, }, - new PluginAdditionalOption() + new PluginAdditionalOption { - Key = "OutputUseEnglishFormat", + Key = OutputUseEnglishFormat, DisplayLabel = Resources.wox_plugin_calculator_out_en_format, DisplayDescription = string.Format(CultureInfo.CurrentCulture, WoxPluginCalculatorOutEnFormatDescription, 1000.55.ToString("G", new CultureInfo("en-us"))), Value = false, }, + new PluginAdditionalOption + { + Key = ReplaceInput, + DisplayLabel = Resources.wox_plugin_calculator_replace_input, + DisplayDescription = Resources.wox_plugin_calculator_replace_input_description, + Value = true, + }, }; public List Query(Query query) @@ -61,6 +73,7 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator ArgumentNullException.ThrowIfNull(query); bool isGlobalQuery = string.IsNullOrEmpty(query.ActionKeyword); + bool replaceInput = _replaceInput && !isGlobalQuery && query.Search.EndsWith('='); CultureInfo inputCulture = _inputUseEnglishFormat ? new CultureInfo("en-us") : CultureInfo.CurrentCulture; CultureInfo outputCulture = _outputUseEnglishFormat ? new CultureInfo("en-us") : CultureInfo.CurrentCulture; @@ -73,6 +86,11 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator NumberTranslator translator = NumberTranslator.Create(inputCulture, new CultureInfo("en-US")); var input = translator.Translate(query.Search.Normalize(NormalizationForm.FormKC)); + if (replaceInput) + { + input = input[..^1]; + } + if (!CalculateHelper.InputValid(input)) { return new List(); @@ -89,10 +107,16 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator // If errorMessage is not default then do error handling return errorMessage == default ? new List() : ErrorHandler.OnError(IconPath, isGlobalQuery, query.RawQuery, errorMessage); } + else if (replaceInput) + { + var pluginResult = ResultHelper.CreateResult(result.RoundedResult, IconPath, inputCulture, outputCulture); + Context.API.ChangeQuery($"{query.ActionKeyword} {pluginResult.QueryTextDisplay}"); + return new List(); + } return new List { - ResultHelper.CreateResult(result.RoundedResult, IconPath, outputCulture), + ResultHelper.CreateResult(result.RoundedResult, IconPath, inputCulture, outputCulture), }; } catch (Mages.Core.ParseException) @@ -157,18 +181,23 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator { var inputUseEnglishFormat = false; var outputUseEnglishFormat = false; + var replaceInput = true; if (settings != null && settings.AdditionalOptions != null) { - var optionInputEn = settings.AdditionalOptions.FirstOrDefault(x => x.Key == "InputUseEnglishFormat"); + var optionInputEn = settings.AdditionalOptions.FirstOrDefault(x => x.Key == InputUseEnglishFormat); inputUseEnglishFormat = optionInputEn?.Value ?? inputUseEnglishFormat; - var optionOutputEn = settings.AdditionalOptions.FirstOrDefault(x => x.Key == "OutputUseEnglishFormat"); + var optionOutputEn = settings.AdditionalOptions.FirstOrDefault(x => x.Key == OutputUseEnglishFormat); outputUseEnglishFormat = optionOutputEn?.Value ?? outputUseEnglishFormat; + + var optionReplaceInput = settings.AdditionalOptions.FirstOrDefault(x => x.Key == ReplaceInput); + replaceInput = optionReplaceInput?.Value ?? replaceInput; } _inputUseEnglishFormat = inputUseEnglishFormat; _outputUseEnglishFormat = outputUseEnglishFormat; + _replaceInput = replaceInput; } public void Dispose() diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/Properties/Resources.Designer.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/Properties/Resources.Designer.cs index 3ac08d3ab9..d772c9c367 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/Properties/Resources.Designer.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/Properties/Resources.Designer.cs @@ -185,5 +185,23 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator.Properties { return ResourceManager.GetString("wox_plugin_calculator_plugin_name", resourceCulture); } } + + /// + /// Looks up a localized string similar to Replace input appending '='. + /// + public static string wox_plugin_calculator_replace_input { + get { + return ResourceManager.GetString("wox_plugin_calculator_replace_input", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to When using direct activation, appending '=' to the expression will replace the input with the calculated result (e.g. =5*3-2=).. + /// + public static string wox_plugin_calculator_replace_input_description { + get { + return ResourceManager.GetString("wox_plugin_calculator_replace_input_description", resourceCulture); + } + } } } diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/Properties/Resources.resx b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/Properties/Resources.resx index a51a01975e..538b135c94 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/Properties/Resources.resx +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/Properties/Resources.resx @@ -161,4 +161,10 @@ Expression contains division by zero + + Replace input if query ends with '=' + + + When using direct activation, appending '=' to the expression will replace the input with the calculated result (e.g. '=5*3-2=' will change the query to '=13'). + \ No newline at end of file diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/ResultHelper.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/ResultHelper.cs index 0cc9033747..3dd1a6b137 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/ResultHelper.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/ResultHelper.cs @@ -12,12 +12,12 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator { public static class ResultHelper { - public static Result CreateResult(CalculateResult result, string iconPath, CultureInfo culture) + public static Result CreateResult(CalculateResult result, string iconPath, CultureInfo inputCulture, CultureInfo outputCulture) { - return CreateResult(result.RoundedResult, iconPath, culture); + return CreateResult(result.RoundedResult, iconPath, inputCulture, outputCulture); } - public static Result CreateResult(decimal? roundedResult, string iconPath, CultureInfo culture) + public static Result CreateResult(decimal? roundedResult, string iconPath, CultureInfo inputCulture, CultureInfo outputCulture) { // Return null when the expression is not a valid calculator query. if (roundedResult == null) @@ -28,11 +28,12 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator return new Result { // Using CurrentCulture since this is user facing - Title = roundedResult?.ToString(culture), + Title = roundedResult?.ToString(outputCulture), IcoPath = iconPath, Score = 300, SubTitle = Properties.Resources.wox_plugin_calculator_copy_number_to_clipboard, - Action = c => Action(roundedResult, culture), + Action = c => Action(roundedResult, outputCulture), + QueryTextDisplay = roundedResult?.ToString(inputCulture), }; } diff --git a/src/modules/launcher/PowerLauncher/MainWindow.xaml.cs b/src/modules/launcher/PowerLauncher/MainWindow.xaml.cs index 4e62d9d407..2fa2699953 100644 --- a/src/modules/launcher/PowerLauncher/MainWindow.xaml.cs +++ b/src/modules/launcher/PowerLauncher/MainWindow.xaml.cs @@ -842,9 +842,6 @@ namespace PowerLauncher { if (_viewModel.Plugins.Count > 0 && _viewModel.SelectedPlugin != null) { - // Needed to update UI in case the user choose the same plugin multiple times - _viewModel.ChangeQueryText(string.Empty); - _viewModel.ChangeQueryText(_viewModel.SelectedPlugin.Metadata.ActionKeyword, true); SearchBox.QueryTextBox.Focus(); diff --git a/src/modules/launcher/PowerLauncher/PublicAPIInstance.cs b/src/modules/launcher/PowerLauncher/PublicAPIInstance.cs index 4357270a6b..d13f28cccf 100644 --- a/src/modules/launcher/PowerLauncher/PublicAPIInstance.cs +++ b/src/modules/launcher/PowerLauncher/PublicAPIInstance.cs @@ -49,7 +49,10 @@ namespace Wox public void ChangeQuery(string query, bool requery = false) { - _mainVM.ChangeQueryText(query, requery); + Application.Current.Dispatcher.Invoke(() => + { + _mainVM.ChangeQueryText(query, requery); + }); } public void CheckForNewUpdate() diff --git a/src/modules/launcher/PowerLauncher/ViewModel/MainViewModel.cs b/src/modules/launcher/PowerLauncher/ViewModel/MainViewModel.cs index 0e81bcaa75..3b65c8ed7b 100644 --- a/src/modules/launcher/PowerLauncher/ViewModel/MainViewModel.cs +++ b/src/modules/launcher/PowerLauncher/ViewModel/MainViewModel.cs @@ -9,7 +9,6 @@ using System.Globalization; using System.Linq; using System.Reflection; using System.Text; -using System.Text.Json; using System.Threading; using System.Threading.Tasks; using System.Windows; @@ -380,8 +379,15 @@ namespace PowerLauncher.ViewModel /// Optional Parameter that if true, will automatically execute a query against the updated text public void ChangeQueryText(string queryText, bool requery = false) { + var sameQueryText = SystemQueryText == queryText; + SystemQueryText = queryText; + if (sameQueryText) + { + OnPropertyChanged(nameof(SystemQueryText)); + } + if (requery) { QueryText = queryText;