From 465df35d2769345263a6a7495b5e3d3ff5ec2c92 Mon Sep 17 00:00:00 2001 From: Heiko <61519853+htcfreek@users.noreply.github.com> Date: Thu, 2 Jun 2022 11:44:12 +0200 Subject: [PATCH] [PT Run] Calculator plugin: Various improvements (#18159) * crash fixes and error result * small changes and test fixes * improve exceptions and message * double array crash fix * overflowexception * improve error handling * varous improvements * varous improvements * fix spelling * fix spelling * Revert #16980 * add description * error improvemenet * Update tests * spelling fixes * small changes * add settings * last changes * fix description * update dev docs * spell check --- .../modules/launcher/plugins/calculator.md | 36 +++++++ .../ExtendedCalculatorParserTests.cs | 16 ++-- ...Toys.Run.Plugin.Calculator.UnitTest.csproj | 5 +- .../NumberTranslatorTests.cs | 4 +- .../QueryTests.cs | 61 ++++++++++++ .../CalculateEngine.cs | 18 +++- .../CalculateHelper.cs | 6 -- .../ErrorHandler.cs | 57 +++++++++++ .../Main.cs | 96 ++++++++++++++++--- .../NumberTranslator.cs | 9 +- .../Properties/Resources.Designer.cs | 74 ++++++++++++-- .../Properties/Resources.resx | 23 ++++- .../ResultHelper.cs | 14 +-- 13 files changed, 363 insertions(+), 56 deletions(-) create mode 100644 src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/QueryTests.cs create mode 100644 src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/ErrorHandler.cs diff --git a/doc/devdocs/modules/launcher/plugins/calculator.md b/doc/devdocs/modules/launcher/plugins/calculator.md index 45089ef84b51..79ffe208a840 100644 --- a/doc/devdocs/modules/launcher/plugins/calculator.md +++ b/doc/devdocs/modules/launcher/plugins/calculator.md @@ -3,6 +3,22 @@ The Calculator plugin as the name suggests is used to perform calculations on th ![Image of Calculator plugin](/doc/images/launcher/plugins/calculator.png) +## Optional plugin settings + +* We have the following settings that the user can configure to change the behavior of the plugin: + + | Key | Default value | Name | Description | + |--------------|-----------|------------|------------| + | `InputUseEnglishFormat` | `false` | Use English (United States) number format for input | Ignores your system setting and expects numbers in the format '1,000.50' | + | `OutputUseEnglishFormat` | `false` | Use English (United States) number format for output | Ignores your system setting and returns numbers in the format '1000.50' | + +* The optional plugin settings are implemented via the [`ISettingProvider`](/src/modules/launcher/Wox.Plugin/ISettingProvider.cs) interface from `Wox.Plugin` project. All available settings for the plugin are defined in the [`Main`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/Main.cs) class of the plugin. + +## Technical details + +### [`BracketHelper`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/BracketHelper.cs) +- This helper validates the bracket usage in the input string. + ### [`CalculateHelper`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateHelper.cs) - The [`CalculateHelper.cs`](src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateHelper.cs) class checks to see if the user entered query is a valid input to the calculator and only if the input is valid does it perform the operation. - It does so by matching the user query to a valid regex. @@ -18,6 +34,26 @@ var result = CalculateEngine.Interpret(query.Search, CultureInfo.CurrentUICultur - The class which encapsulates the result of the computation. - It comprises of the `Result` and `RoundedResult` properties. +### [`ErrorHandler`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/ErrorHandler.cs) +- The class which encapsulates the code to log errors and format the user message. +- It returns an error result if the user searches with the activation command. This error result is shown to the user. + ### Score The score of each result from the calculator plugin is `300`. + +## [Unit Tests](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTests) +We have a [Unit Test project](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTests) that executes various test to ensure that the plugin works as expected. + +### [`BracketHelperTests`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTests/BracketHelperTests.cs) +- The [`BracketHelperTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTests/BracketHelperTests.cs) class contains tests to validate that brackets are handled correctly. + +### [`ExtendedCalculatorParserTests`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTests/ExtendedCalculatorParserTests.cs) +- The [`ExtendedCalculatorParserTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTests/ExtendedCalculatorParserTests.cs) class contains tests to validate that the input is parsed correctly and the result is correct. + +### [`NumberTranslatorTests`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTests/NumberTranslatorTests.cs) +- The [`NumberTranslatorTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTests/NumberTranslatorTests.cs) class contains tests to validate that each number is converted correctly based on the defined locals. + +### [`QueryTests`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTests/QueryTests.cs) +- The [`QueryTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTests/QueryTests.cs) class contains tests to validate that the user gets the correct results when searching. + diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/ExtendedCalculatorParserTests.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/ExtendedCalculatorParserTests.cs index 99ae3d709908..016718308bae 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/ExtendedCalculatorParserTests.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/ExtendedCalculatorParserTests.cs @@ -32,21 +32,21 @@ public void Interpret_ThrowError_WhenCalledNullOrEmpty(string input) var engine = new CalculateEngine(); // Act - Assert.ThrowsException(() => engine.Interpret(input, CultureInfo.CurrentCulture)); + Assert.ThrowsException(() => engine.Interpret(input, CultureInfo.CurrentCulture, out _)); } [DataTestMethod] - [DataRow("42")] [DataRow("test")] [DataRow("pi(2)")] // Incorrect input, constant is being treated as a function. [DataRow("e(2)")] + [DataRow("[10,10]")] // '[10,10]' is interpreted as array by mages engine public void Interpret_NoResult_WhenCalled(string input) { // Arrange var engine = new CalculateEngine(); // Act - var result = engine.Interpret(input, CultureInfo.CurrentCulture); + var result = engine.Interpret(input, CultureInfo.CurrentCulture, out _); // Assert Assert.AreEqual(default(CalculateResult), result); @@ -87,7 +87,7 @@ public void Interpret_NoErrors_WhenCalledWithRounding(string input, decimal expe // Act // Using InvariantCulture since this is internal - var result = engine.Interpret(input, CultureInfo.InvariantCulture); + var result = engine.Interpret(input, CultureInfo.InvariantCulture, out _); // Assert Assert.IsNotNull(result); @@ -111,7 +111,7 @@ public void Interpret_QuirkOutput_WhenCalled(string input, decimal expectedResul // Act // Using InvariantCulture since this is internal - var result = engine.Interpret(input, CultureInfo.InvariantCulture); + var result = engine.Interpret(input, CultureInfo.InvariantCulture, out _); // Assert Assert.IsNotNull(result); @@ -135,7 +135,7 @@ public void Interpret_DifferentCulture_WhenCalled(string input, decimal expected var engine = new CalculateEngine(); // Act - var result = engine.Interpret(input, cultureInfo); + var result = engine.Interpret(input, cultureInfo, out _); // Assert Assert.IsNotNull(result); @@ -184,7 +184,7 @@ public void Interpret_MustReturnResult_WhenResultIsZero(string input) // Act // Using InvariantCulture since this is internal - var result = engine.Interpret(input, CultureInfo.InvariantCulture); + var result = engine.Interpret(input, CultureInfo.InvariantCulture, out _); // Assert Assert.IsNotNull(result); @@ -210,7 +210,7 @@ public void Interpret_MustReturnExpectedResult_WhenCalled(string input, decimal // Act // Using InvariantCulture since this is internal - var result = engine.Interpret(input, CultureInfo.InvariantCulture); + var result = engine.Interpret(input, CultureInfo.InvariantCulture, out _); // Assert Assert.IsNotNull(result); diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest.csproj index f70db75bb1a3..39709456d836 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest.csproj +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest.csproj @@ -8,9 +8,10 @@ + - all - runtime; build; native; contentfiles; analyzers; buildtransitive + all + runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/NumberTranslatorTests.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/NumberTranslatorTests.cs index 71d3578c3c55..e3e57966f7de 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/NumberTranslatorTests.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/NumberTranslatorTests.cs @@ -132,12 +132,12 @@ public void Translate_RemoveNumberGroupSeparator_WhenCalled(string decimalSepara } [DataTestMethod] - [DataRow("12.0004", "12.0004")] + [DataRow("12,0004", "12.0004")] [DataRow("0xF000", "0xF000")] public void Translate_NoRemovalOfLeadingZeroesOnEdgeCases(string input, string expectedResult) { // Arrange - var translator = NumberTranslator.Create(new CultureInfo("pt-PT"), new CultureInfo("en-US")); + var translator = NumberTranslator.Create(new CultureInfo("de-de"), new CultureInfo("en-US")); // Act var result = translator.Translate(input); 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 new file mode 100644 index 000000000000..befc8400fb54 --- /dev/null +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/QueryTests.cs @@ -0,0 +1,61 @@ +// 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.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Wox.Plugin; + +namespace Microsoft.PowerToys.Run.Plugin.Calculator.UnitTests +{ + [TestClass] + public class QueryTests + { + [DataTestMethod] + [DataRow("=pi(9+)", "Expression wrong or incomplete (Did you forget some parentheses?)")] + [DataRow("=pi(9)", "Expression wrong or incomplete (Did you forget some parentheses?)")] + [DataRow("=pi,", "Expression wrong or incomplete (Did you forget some parentheses?)")] + [DataRow("=log()", "Expression wrong or incomplete (Did you forget some parentheses?)")] + [DataRow("=0xf0x6", "Expression wrong or incomplete (Did you forget some parentheses?)")] + [DataRow("=0xf,0x6", "Expression wrong or incomplete (Did you forget some parentheses?)")] + [DataRow("=2^96", "Result value was either too large or too small for a decimal number")] + [DataRow("=+()", "Calculation result is not a valid number (NaN)")] + [DataRow("=[10,10]", "Unsupported use of square brackets")] + public void ErrorResultOnInvalidKeywordQuery(string typedString, string expectedResult) + { + // Setup + Mock
main = new (); + Query expectedQuery = new (typedString, "="); + + // Act + var result = main.Object.Query(expectedQuery).FirstOrDefault().SubTitle; + + // Assert + Assert.AreEqual(expectedResult, result); + } + + [DataTestMethod] + [DataRow("pi(9+)")] + [DataRow("pi(9)")] + [DataRow("pi,")] + [DataRow("log()")] + [DataRow("0xf0x6")] + [DataRow("0xf,0x6")] + [DataRow("2^96")] + [DataRow("+()")] + [DataRow("[10,10]")] + public void NoResultOnInvalidGlobalQuery(string typedString) + { + // Setup + Mock
main = new (); + Query expectedQuery = new (typedString); + + // Act + var result = main.Object.Query(expectedQuery).Count; + + // Assert + Assert.AreEqual(result, 0); + } + } +} diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateEngine.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateEngine.cs index fc2d76f83630..2398c377238e 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateEngine.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateEngine.cs @@ -25,8 +25,10 @@ public class CalculateEngine /// Interpret /// /// Use CultureInfo.CurrentCulture if something is user facing - public CalculateResult Interpret(string input, CultureInfo cultureInfo) + public CalculateResult Interpret(string input, CultureInfo cultureInfo, out string error) { + error = default; + if (!CalculateHelper.InputValid(input)) { return default; @@ -43,10 +45,16 @@ public CalculateResult Interpret(string input, CultureInfo cultureInfo) // This could happen for some incorrect queries, like pi(2) if (result == null) { + error = Properties.Resources.wox_plugin_calculator_expression_not_complete; return default; } result = TransformResult(result); + if (result is string) + { + error = result as string; + return default; + } if (string.IsNullOrEmpty(result?.ToString())) { @@ -68,7 +76,7 @@ public static decimal Round(decimal value) return Math.Round(value, RoundingDigits, MidpointRounding.AwayFromZero); } - private static object TransformResult(object result) + private static dynamic TransformResult(object result) { if (result.ToString() == "NaN") { @@ -80,6 +88,12 @@ private static object TransformResult(object result) return Properties.Resources.wox_plugin_calculator_expression_not_complete; } + if (result is double[,]) + { + // '[10,10]' is interpreted as array by mages engine + return Properties.Resources.wox_plugin_calculator_double_array_returned; + } + return result; } } diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateHelper.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateHelper.cs index c9892059ac75..861e2e7e4112 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateHelper.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateHelper.cs @@ -28,12 +28,6 @@ public static bool InputValid(string input) throw new ArgumentNullException(paramName: nameof(input)); } - bool singleDigitFactorial = input.EndsWith("!", StringComparison.InvariantCulture); - if (input.Length <= 2 && !singleDigitFactorial) - { - return false; - } - if (!RegValidExpressChar.IsMatch(input)) { return false; diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/ErrorHandler.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/ErrorHandler.cs new file mode 100644 index 000000000000..b9dbfd3cbef5 --- /dev/null +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/ErrorHandler.cs @@ -0,0 +1,57 @@ +// 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 Wox.Plugin; +using Wox.Plugin.Logger; + +namespace Microsoft.PowerToys.Run.Plugin.Calculator +{ + internal static class ErrorHandler + { + /// + /// Method to handles errors while calculating + /// + /// Path to result icon. + /// Bool to indicate if it is a global query. + /// User input as string including the action keyword. + /// Error message if applicable. + /// Exception if applicable. + /// List of results to show. Either an error message or an empty list. + /// Thrown if and are both filled with their default values. + internal static List OnError(string icon, bool isGlobalQuery, string queryInput, string errorMessage, Exception exception = default) + { + string userMessage; + + if (errorMessage != default) + { + Log.Error($"Failed to calculate <{queryInput}>: {errorMessage}", typeof(Calculator.Main)); + userMessage = errorMessage; + } + else if (exception != default) + { + Log.Exception($"Exception when query for <{queryInput}>", exception, exception.GetType()); + userMessage = exception.Message; + } + else + { + throw new ArgumentException("The arguments error and exception have default values. One of them has to be filled with valid error data (error message/exception)!"); + } + + return isGlobalQuery ? new List() : new List { CreateErrorResult(userMessage, icon) }; + } + + private static Result CreateErrorResult(string errorMessage, string iconPath) + { + return new Result + { + Title = Properties.Resources.wox_plugin_calculator_calculation_failed, + SubTitle = errorMessage, + IcoPath = iconPath, + Score = 300, + }; + } + } +} 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 e546d2b66229..371d56aab056 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 @@ -5,14 +5,17 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.Linq; using System.Text; +using System.Windows.Controls; using ManagedCommon; +using Microsoft.PowerToys.Run.Plugin.Calculator.Properties; +using Microsoft.PowerToys.Settings.UI.Library; using Wox.Plugin; -using Wox.Plugin.Logger; namespace Microsoft.PowerToys.Run.Plugin.Calculator { - public class Main : IPlugin, IPluginI18n, IDisposable + public class Main : IPlugin, IPluginI18n, IDisposable, ISettingProvider { private static readonly CalculateEngine CalculateEngine = new CalculateEngine(); @@ -20,20 +23,51 @@ public class Main : IPlugin, IPluginI18n, IDisposable private string IconPath { get; set; } - public string Name => Properties.Resources.wox_plugin_calculator_plugin_name; + private bool _inputUseEnglishFormat; + private bool _outputUseEnglishFormat; - public string Description => Properties.Resources.wox_plugin_calculator_plugin_description; + public string Name => Resources.wox_plugin_calculator_plugin_name; + + public string Description => Resources.wox_plugin_calculator_plugin_description; private bool _disposed; + public IEnumerable AdditionalOptions => new List() + { + new PluginAdditionalOption() + { + Key = "InputUseEnglishFormat", + DisplayLabel = Resources.wox_plugin_calculator_in_en_format, + DisplayDescription = Resources.wox_plugin_calculator_in_en_format_description, + Value = false, + }, + new PluginAdditionalOption() + { + Key = "OutputUseEnglishFormat", + DisplayLabel = Resources.wox_plugin_calculator_out_en_format, + DisplayDescription = Resources.wox_plugin_calculator_out_en_format_description, + Value = false, + }, + }; + public List Query(Query query) { + bool isGlobalQuery = string.IsNullOrEmpty(query.ActionKeyword); + CultureInfo inputCulture = _inputUseEnglishFormat ? new CultureInfo("en-us") : CultureInfo.CurrentCulture; + CultureInfo outputCulture = _outputUseEnglishFormat ? new CultureInfo("en-us") : CultureInfo.CurrentCulture; + if (query == null) { throw new ArgumentNullException(paramName: nameof(query)); } - NumberTranslator translator = NumberTranslator.Create(CultureInfo.CurrentCulture, new CultureInfo("en-US")); + // Happens if the user has only typed the action key so far + if (string.IsNullOrEmpty(query.Search)) + { + return new List(); + } + + NumberTranslator translator = NumberTranslator.Create(inputCulture, new CultureInfo("en-US")); var input = translator.Translate(query.Search.Normalize(NormalizationForm.FormKC)); if (!CalculateHelper.InputValid(input)) @@ -44,27 +78,38 @@ public List Query(Query query) try { // Using CurrentUICulture since this is user facing - var result = CalculateEngine.Interpret(input, CultureInfo.CurrentUICulture); + var result = CalculateEngine.Interpret(input, outputCulture, out string errorMessage); // This could happen for some incorrect queries, like pi(2) if (result.Equals(default(CalculateResult))) { - return new List(); + // If errorMessage is not default then do error handling + return errorMessage == default ? new List() : ErrorHandler.OnError(IconPath, isGlobalQuery, query.RawQuery, errorMessage); } return new List { - ResultHelper.CreateResult(result.RoundedResult, IconPath), + ResultHelper.CreateResult(result.RoundedResult, IconPath, outputCulture), }; - } // We want to keep the process alive if any the mages library throws any exceptions. + } + catch (Mages.Core.ParseException) + { + // Invalid input + return ErrorHandler.OnError(IconPath, isGlobalQuery, query.RawQuery, Properties.Resources.wox_plugin_calculator_expression_not_complete); + } + catch (OverflowException) + { + // Result to big to convert to decimal + return ErrorHandler.OnError(IconPath, isGlobalQuery, query.RawQuery, Properties.Resources.wox_plugin_calculator_not_covert_to_decimal); + } #pragma warning disable CA1031 // Do not catch general exception types catch (Exception e) #pragma warning restore CA1031 // Do not catch general exception types { - Log.Exception("Exception when query for <{query}>", e, GetType()); + // Any other crash occurred + // We want to keep the process alive if any the mages library throws any exceptions. + return ErrorHandler.OnError(IconPath, isGlobalQuery, query.RawQuery, default, e); } - - return new List(); } public void Init(PluginInitContext context) @@ -94,12 +139,35 @@ private void OnThemeChanged(Theme currentTheme, Theme newTheme) public string GetTranslatedPluginTitle() { - return Properties.Resources.wox_plugin_calculator_plugin_name; + return Resources.wox_plugin_calculator_plugin_name; } public string GetTranslatedPluginDescription() { - return Properties.Resources.wox_plugin_calculator_plugin_description; + return Resources.wox_plugin_calculator_plugin_description; + } + + public Control CreateSettingPanel() + { + throw new NotImplementedException(); + } + + public void UpdateSettings(PowerLauncherPluginSettings settings) + { + var inputUseEnglishFormat = false; + var outputUseEnglishFormat = false; + + if (settings != null && settings.AdditionalOptions != null) + { + var optionInputEn = settings.AdditionalOptions.FirstOrDefault(x => x.Key == "InputUseEnglishFormat"); + inputUseEnglishFormat = optionInputEn?.Value ?? inputUseEnglishFormat; + + var optionOutputEn = settings.AdditionalOptions.FirstOrDefault(x => x.Key == "OutputUseEnglishFormat"); + outputUseEnglishFormat = optionOutputEn?.Value ?? outputUseEnglishFormat; + } + + _inputUseEnglishFormat = inputUseEnglishFormat; + _outputUseEnglishFormat = outputUseEnglishFormat; } public void Dispose() diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/NumberTranslator.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/NumberTranslator.cs index b3e6f4db4c81..eaaa1658a2ca 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/NumberTranslator.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/NumberTranslator.cs @@ -88,14 +88,7 @@ private static string Translate(string input, CultureInfo cultureFrom, CultureIn private static Regex GetSplitRegex(CultureInfo culture) { - // HACK: Specifically adding the decimal point here since some people expect that to work everywhere. - // This allows avoiding some unexpected errors users are getting when . is not part of the number representation. - // Users were getting errors where leading zeros were being removed from the decimal part of numbers like 56.0002. - // 56.0002 would be transformed into 56.2 due to it being translated as two different numbers and this would be accepted into the engine. - // Now, even if . is not part of the culture representation, users won't hit this error since the number will - // be passed as is to the calculator engine. - // This shouldn't add any regressions into accepted strings while it will have a behavior the users expect. - var splitPattern = $"((?:\\d|[a-fA-F]|\\.|{Regex.Escape(culture.NumberFormat.NumberDecimalSeparator)}"; + var splitPattern = $"((?:\\d|[a-fA-F]|{Regex.Escape(culture.NumberFormat.NumberDecimalSeparator)}"; if (!string.IsNullOrEmpty(culture.NumberFormat.NumberGroupSeparator)) { splitPattern += $"|{Regex.Escape(culture.NumberFormat.NumberGroupSeparator)}"; 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 3f3267b74726..1268957d4e90 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 @@ -19,7 +19,7 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator.Properties { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class Resources { @@ -45,7 +45,7 @@ internal Resources() { return resourceMan; } } - + /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. @@ -60,6 +60,15 @@ internal Resources() { } } + /// + /// Looks up a localized string similar to Failed to calculate the input. + /// + public static string wox_plugin_calculator_calculation_failed { + get { + return ResourceManager.GetString("wox_plugin_calculator_calculation_failed", resourceCulture); + } + } + /// /// Looks up a localized string similar to Copy failed, please try later. /// @@ -68,7 +77,7 @@ public static string wox_plugin_calculator_copy_failed { return ResourceManager.GetString("wox_plugin_calculator_copy_failed", resourceCulture); } } - + /// /// Looks up a localized string similar to Copy this number to the clipboard. /// @@ -78,6 +87,15 @@ public static string wox_plugin_calculator_copy_number_to_clipboard { } } + /// + /// Looks up a localized string similar to Unsupported use of square brackets. + /// + public static string wox_plugin_calculator_double_array_returned { + get { + return ResourceManager.GetString("wox_plugin_calculator_double_array_returned", resourceCulture); + } + } + /// /// Looks up a localized string similar to Expression wrong or incomplete (Did you forget some parentheses?). /// @@ -88,14 +106,58 @@ public static string wox_plugin_calculator_expression_not_complete { } /// - /// Looks up a localized string similar to Not a number (NaN). + /// Looks up a localized string similar to Use English (United States) number format for input. + /// + public static string wox_plugin_calculator_in_en_format { + get { + return ResourceManager.GetString("wox_plugin_calculator_in_en_format", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Ignores your system setting and expects numbers in the format '1,000.50'. + /// + public static string wox_plugin_calculator_in_en_format_description { + get { + return ResourceManager.GetString("wox_plugin_calculator_in_en_format_description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Calculation result is not a valid number (NaN). /// public static string wox_plugin_calculator_not_a_number { get { return ResourceManager.GetString("wox_plugin_calculator_not_a_number", resourceCulture); } } - + + /// + /// Looks up a localized string similar to Result value was either too large or too small for a decimal number. + /// + public static string wox_plugin_calculator_not_covert_to_decimal { + get { + return ResourceManager.GetString("wox_plugin_calculator_not_covert_to_decimal", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use English (United States) number format for output. + /// + public static string wox_plugin_calculator_out_en_format { + get { + return ResourceManager.GetString("wox_plugin_calculator_out_en_format", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Ignores your system setting and returns numbers in the format '1000.50'. + /// + public static string wox_plugin_calculator_out_en_format_description { + get { + return ResourceManager.GetString("wox_plugin_calculator_out_en_format_description", resourceCulture); + } + } /// /// Looks up a localized string similar to Does mathematical calculations (e.g. 5*3-2).. @@ -105,7 +167,7 @@ public static string wox_plugin_calculator_plugin_description { return ResourceManager.GetString("wox_plugin_calculator_plugin_description", resourceCulture); } } - + /// /// Looks up a localized string similar to Calculator. /// 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 c118d804a7e0..b3affa13f0d0 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 @@ -124,7 +124,7 @@ Does mathematical calculations (e.g. 5*3-2). - Not a number (NaN) + Calculation result is not a valid number (NaN) Expression wrong or incomplete (Did you forget some parentheses?) @@ -135,4 +135,25 @@ Copy failed, please try later + + Failed to calculate the input + + + Unsupported use of square brackets + + + Result value was either too large or too small for a decimal number + + + Use English (United States) number format for input + + + Ignores your system setting and expects numbers in the format '1,000.50' + + + Use English (United States) number format for output + + + Ignores your system setting and returns numbers in the format '1000.50' + \ 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 fb81e51f6fdd..0cc9033747cc 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) + public static Result CreateResult(CalculateResult result, string iconPath, CultureInfo culture) { - return CreateResult(result.RoundedResult, iconPath); + return CreateResult(result.RoundedResult, iconPath, culture); } - public static Result CreateResult(decimal? roundedResult, string iconPath) + public static Result CreateResult(decimal? roundedResult, string iconPath, CultureInfo culture) { // Return null when the expression is not a valid calculator query. if (roundedResult == null) @@ -28,15 +28,15 @@ public static Result CreateResult(decimal? roundedResult, string iconPath) return new Result { // Using CurrentCulture since this is user facing - Title = roundedResult?.ToString(CultureInfo.CurrentCulture), + Title = roundedResult?.ToString(culture), IcoPath = iconPath, Score = 300, SubTitle = Properties.Resources.wox_plugin_calculator_copy_number_to_clipboard, - Action = c => Action(roundedResult), + Action = c => Action(roundedResult, culture), }; } - public static bool Action(decimal? roundedResult) + public static bool Action(decimal? roundedResult, CultureInfo culture) { var ret = false; @@ -46,7 +46,7 @@ public static bool Action(decimal? roundedResult) { try { - Clipboard.SetText(roundedResult?.ToString(CultureInfo.CurrentCulture)); + Clipboard.SetText(roundedResult?.ToString(culture)); ret = true; } catch (ExternalException)