Skip to content

Commit 113baac

Browse files
committed
Smart thousands and decimals
## Core Logic * **Advanced Number Parsing:** We now process numbers with various decimal and thousand-separator formats (e.g., `1,234.56` and `1.234,56`). We distinguish between separator types based on their position and surrounding digits. * **Context-Aware Output Formatting:** We now mirror the output format based on the user's input. If a query includes thousand separators, the result will also have them. The decimal separator in the result will match the one used in the query. ## Code Cleanup * **Deleted Unused File:** `NumberTranslator.cs` was unused and therefore removed. * **Removed Redundant UI Code:** The `CalculatorSettings_Loaded` event handler in `CalculatorSettings.xaml.cs` (and its XAML registration) was removed. The functionality was already handled automatically by data binding. ## Maintainability * **Added Code Documentation:** An XML summary comment was added to the new `NormalizeNumber` method in `Main.cs` to clarify its purpose. So, the plugin is now much more flexible, and will accept whatever format the user gives it regardless of Windows region settings.
1 parent 1da7e1e commit 113baac

File tree

5 files changed

+129
-134
lines changed

5 files changed

+129
-134
lines changed

Plugins/Flow.Launcher.Plugin.Calculator/Main.cs

Lines changed: 126 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Globalization;
4+
using System.Linq;
45
using System.Runtime.InteropServices;
56
using System.Text.RegularExpressions;
67
using System.Windows.Controls;
@@ -23,6 +24,7 @@ public class Main : IPlugin, IPluginI18n, ISettingProvider
2324
@"[ei]|[0-9]|0x[\da-fA-F]+|[\+\%\-\*\/\^\., ""]|[\(\)\|\!\[\]]" +
2425
@")+$", RegexOptions.Compiled);
2526
private static readonly Regex RegBrackets = new Regex(@"[\(\)\[\]]", RegexOptions.Compiled);
27+
private static readonly Regex ThousandGroupRegex = new Regex(@"\B(?=(\d{3})+(?!\d))");
2628
private static Engine MagesEngine;
2729
private const string comma = ",";
2830
private const string dot = ".";
@@ -32,6 +34,9 @@ public class Main : IPlugin, IPluginI18n, ISettingProvider
3234
private static Settings _settings;
3335
private static SettingsViewModel _viewModel;
3436

37+
private string _inputDecimalSeparator;
38+
private bool _inputUsesGroupSeparators;
39+
3540
public void Init(PluginInitContext context)
3641
{
3742
Context = context;
@@ -54,20 +59,13 @@ public List<Result> Query(Query query)
5459
return new List<Result>();
5560
}
5661

62+
_inputDecimalSeparator = null;
63+
_inputUsesGroupSeparators = false;
64+
5765
try
5866
{
59-
string expression;
60-
61-
switch (_settings.DecimalSeparator)
62-
{
63-
case DecimalSeparator.Comma:
64-
case DecimalSeparator.UseSystemLocale when CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator == ",":
65-
expression = query.Search.Replace(",", ".");
66-
break;
67-
default:
68-
expression = query.Search;
69-
break;
70-
}
67+
var numberRegex = new Regex(@"[\d\.,]+");
68+
var expression = numberRegex.Replace(query.Search, m => NormalizeNumber(m.Value));
7169

7270
var result = MagesEngine.Interpret(expression);
7371

@@ -80,7 +78,7 @@ public List<Result> Query(Query query)
8078
if (!string.IsNullOrEmpty(result?.ToString()))
8179
{
8280
decimal roundedResult = Math.Round(Convert.ToDecimal(result), _settings.MaxDecimalPlaces, MidpointRounding.AwayFromZero);
83-
string newResult = ChangeDecimalSeparator(roundedResult, GetDecimalSeparator());
81+
string newResult = FormatResult(roundedResult);
8482

8583
return new List<Result>
8684
{
@@ -115,6 +113,121 @@ public List<Result> Query(Query query)
115113

116114
return new List<Result>();
117115
}
116+
117+
/// <summary>
118+
/// Parses a string representation of a number, detecting its format. It uses structural analysis
119+
/// (checking for 3-digit groups) and falls back to system culture for ambiguous cases (e.g., "1,234").
120+
/// It sets instance fields to remember the user's format for later output formatting.
121+
/// </summary>
122+
/// <returns>A normalized number string with '.' as the decimal separator for the Mages engine.</returns>
123+
private string NormalizeNumber(string numberStr)
124+
{
125+
var systemFormat = CultureInfo.CurrentCulture.NumberFormat;
126+
string systemDecimalSeparator = systemFormat.NumberDecimalSeparator;
127+
128+
bool hasDot = numberStr.Contains(dot);
129+
bool hasComma = numberStr.Contains(comma);
130+
131+
// Unambiguous case: both separators are present. The last one wins as decimal separator.
132+
if (hasDot && hasComma)
133+
{
134+
_inputUsesGroupSeparators = true;
135+
int lastDotPos = numberStr.LastIndexOf(dot);
136+
int lastCommaPos = numberStr.LastIndexOf(comma);
137+
138+
if (lastDotPos > lastCommaPos) // e.g. 1,234.56
139+
{
140+
_inputDecimalSeparator = dot;
141+
return numberStr.Replace(comma, string.Empty);
142+
}
143+
else // e.g. 1.234,56
144+
{
145+
_inputDecimalSeparator = comma;
146+
return numberStr.Replace(dot, string.Empty).Replace(comma, dot);
147+
}
148+
}
149+
150+
if (hasComma)
151+
{
152+
string[] parts = numberStr.Split(',');
153+
// If all parts after the first are 3 digits, it's a potential group separator.
154+
bool isGroupCandidate = parts.Length > 1 && parts.Skip(1).All(p => p.Length == 3);
155+
156+
if (isGroupCandidate)
157+
{
158+
// Ambiguous case: "1,234". Resolve using culture.
159+
if (systemDecimalSeparator == comma)
160+
{
161+
_inputDecimalSeparator = comma;
162+
return numberStr.Replace(comma, dot);
163+
}
164+
else
165+
{
166+
_inputUsesGroupSeparators = true;
167+
return numberStr.Replace(comma, string.Empty);
168+
}
169+
}
170+
else
171+
{
172+
// Unambiguous decimal: "123,45" or "1,2,345"
173+
_inputDecimalSeparator = comma;
174+
return numberStr.Replace(comma, dot);
175+
}
176+
}
177+
178+
if (hasDot)
179+
{
180+
string[] parts = numberStr.Split('.');
181+
bool isGroupCandidate = parts.Length > 1 && parts.Skip(1).All(p => p.Length == 3);
182+
183+
if (isGroupCandidate)
184+
{
185+
if (systemDecimalSeparator == dot)
186+
{
187+
_inputDecimalSeparator = dot;
188+
return numberStr;
189+
}
190+
else
191+
{
192+
_inputUsesGroupSeparators = true;
193+
return numberStr.Replace(dot, string.Empty);
194+
}
195+
}
196+
else
197+
{
198+
_inputDecimalSeparator = dot;
199+
return numberStr; // Already in Mages-compatible format
200+
}
201+
}
202+
203+
// No separators.
204+
return numberStr;
205+
}
206+
207+
private string FormatResult(decimal roundedResult)
208+
{
209+
// Use the detected decimal separator from the input; otherwise, fall back to settings.
210+
string decimalSeparator = _inputDecimalSeparator ?? GetDecimalSeparator();
211+
string groupSeparator = decimalSeparator == dot ? comma : dot;
212+
213+
string resultStr = roundedResult.ToString(CultureInfo.InvariantCulture);
214+
215+
string[] parts = resultStr.Split('.');
216+
string integerPart = parts[0];
217+
string fractionalPart = parts.Length > 1 ? parts[1] : string.Empty;
218+
219+
if (_inputUsesGroupSeparators)
220+
{
221+
integerPart = ThousandGroupRegex.Replace(integerPart, groupSeparator);
222+
}
223+
224+
if (!string.IsNullOrEmpty(fractionalPart))
225+
{
226+
return integerPart + decimalSeparator + fractionalPart;
227+
}
228+
229+
return integerPart;
230+
}
118231

119232
private bool CanCalculate(Query query)
120233
{
@@ -134,27 +247,9 @@ private bool CanCalculate(Query query)
134247
return false;
135248
}
136249

137-
if ((query.Search.Contains(dot) && GetDecimalSeparator() != dot) ||
138-
(query.Search.Contains(comma) && GetDecimalSeparator() != comma))
139-
return false;
140-
141250
return true;
142251
}
143252

144-
private string ChangeDecimalSeparator(decimal value, string newDecimalSeparator)
145-
{
146-
if (String.IsNullOrEmpty(newDecimalSeparator))
147-
{
148-
return value.ToString();
149-
}
150-
151-
var numberFormatInfo = new NumberFormatInfo
152-
{
153-
NumberDecimalSeparator = newDecimalSeparator
154-
};
155-
return value.ToString(numberFormatInfo);
156-
}
157-
158253
private static string GetDecimalSeparator()
159254
{
160255
string systemDecimalSeparator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;

Plugins/Flow.Launcher.Plugin.Calculator/NumberTranslator.cs

Lines changed: 0 additions & 91 deletions
This file was deleted.

Plugins/Flow.Launcher.Plugin.Calculator/Views/CalculatorSettings.xaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
xmlns:viewModels="clr-namespace:Flow.Launcher.Plugin.Calculator.ViewModels"
1111
d:DesignHeight="450"
1212
d:DesignWidth="800"
13-
Loaded="CalculatorSettings_Loaded"
1413
mc:Ignorable="d">
1514

1615
<UserControl.Resources>

Plugins/Flow.Launcher.Plugin.Calculator/Views/CalculatorSettings.xaml.cs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
using System.Windows;
21
using System.Windows.Controls;
32
using Flow.Launcher.Plugin.Calculator.ViewModels;
43

@@ -19,13 +18,6 @@ public CalculatorSettings(SettingsViewModel viewModel)
1918
DataContext = viewModel;
2019
InitializeComponent();
2120
}
22-
23-
private void CalculatorSettings_Loaded(object sender, RoutedEventArgs e)
24-
{
25-
DecimalSeparatorComboBox.SelectedItem = _settings.DecimalSeparator;
26-
MaxDecimalPlaces.SelectedItem = _settings.MaxDecimalPlaces;
27-
}
2821
}
2922

30-
3123
}

Plugins/Flow.Launcher.Plugin.Calculator/plugin.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
"ID": "CEA0FDFC6D3B4085823D60DC76F28855",
33
"ActionKeyword": "*",
44
"Name": "Calculator",
5-
"Description": "Perform mathematical calculations (including hexadecimal values)",
6-
"Author": "cxfksword",
7-
"Version": "1.0.0",
5+
"Description": "Perform mathematical calculations (including hexadecimal values). Use ',' or '.' as thousand separator or decimal place.",
6+
"Author": "cxfksword, dcog989",
7+
"Version": "1.1.0",
88
"Language": "csharp",
99
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
1010
"ExecuteFileName": "Flow.Launcher.Plugin.Calculator.dll",

0 commit comments

Comments
 (0)