@@ -25,6 +25,8 @@ public class Main : IPlugin, IPluginI18n, ISettingProvider
2525 @")+$" , RegexOptions . Compiled ) ;
2626 private static readonly Regex RegBrackets = new Regex ( @"[\(\)\[\]]" , RegexOptions . Compiled ) ;
2727 private static readonly Regex ThousandGroupRegex = new Regex ( @"\B(?=(\d{3})+(?!\d))" ) ;
28+ private static readonly Regex NumberRegex = new Regex ( @"[\d\.,]+" , RegexOptions . Compiled ) ;
29+
2830 private static Engine MagesEngine ;
2931 private const string comma = "," ;
3032 private const string dot = "." ;
@@ -34,8 +36,15 @@ public class Main : IPlugin, IPluginI18n, ISettingProvider
3436 private static Settings _settings ;
3537 private static SettingsViewModel _viewModel ;
3638
37- private string _inputDecimalSeparator ;
38- private bool _inputUsesGroupSeparators ;
39+ /// <summary>
40+ /// Holds the formatting information for a single query.
41+ /// This is used to ensure thread safety by keeping query state local.
42+ /// </summary>
43+ private class ParsingContext
44+ {
45+ public string InputDecimalSeparator { get ; set ; }
46+ public bool InputUsesGroupSeparators { get ; set ; }
47+ }
3948
4049 public void Init ( PluginInitContext context )
4150 {
@@ -59,13 +68,11 @@ public List<Result> Query(Query query)
5968 return new List < Result > ( ) ;
6069 }
6170
62- _inputDecimalSeparator = null ;
63- _inputUsesGroupSeparators = false ;
71+ var context = new ParsingContext ( ) ;
6472
6573 try
6674 {
67- var numberRegex = new Regex ( @"[\d\.,]+" ) ;
68- var expression = numberRegex . Replace ( query . Search , m => NormalizeNumber ( m . Value ) ) ;
75+ var expression = NumberRegex . Replace ( query . Search , m => NormalizeNumber ( m . Value , context ) ) ;
6976
7077 var result = MagesEngine . Interpret ( expression ) ;
7178
@@ -78,7 +85,7 @@ public List<Result> Query(Query query)
7885 if ( ! string . IsNullOrEmpty ( result ? . ToString ( ) ) )
7986 {
8087 decimal roundedResult = Math . Round ( Convert . ToDecimal ( result ) , _settings . MaxDecimalPlaces , MidpointRounding . AwayFromZero ) ;
81- string newResult = FormatResult ( roundedResult ) ;
88+ string newResult = FormatResult ( roundedResult , context ) ;
8289
8390 return new List < Result >
8491 {
@@ -117,10 +124,10 @@ public List<Result> Query(Query query)
117124 /// <summary>
118125 /// Parses a string representation of a number, detecting its format. It uses structural analysis
119126 /// (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 .
127+ /// It populates the provided ParsingContext with the detected format for later use .
121128 /// </summary>
122129 /// <returns>A normalized number string with '.' as the decimal separator for the Mages engine.</returns>
123- private string NormalizeNumber ( string numberStr )
130+ private string NormalizeNumber ( string numberStr , ParsingContext context )
124131 {
125132 var systemFormat = CultureInfo . CurrentCulture . NumberFormat ;
126133 string systemDecimalSeparator = systemFormat . NumberDecimalSeparator ;
@@ -131,18 +138,18 @@ private string NormalizeNumber(string numberStr)
131138 // Unambiguous case: both separators are present. The last one wins as decimal separator.
132139 if ( hasDot && hasComma )
133140 {
134- _inputUsesGroupSeparators = true ;
141+ context . InputUsesGroupSeparators = true ;
135142 int lastDotPos = numberStr . LastIndexOf ( dot ) ;
136143 int lastCommaPos = numberStr . LastIndexOf ( comma ) ;
137144
138145 if ( lastDotPos > lastCommaPos ) // e.g. 1,234.56
139146 {
140- _inputDecimalSeparator = dot ;
147+ context . InputDecimalSeparator = dot ;
141148 return numberStr . Replace ( comma , string . Empty ) ;
142149 }
143150 else // e.g. 1.234,56
144151 {
145- _inputDecimalSeparator = comma ;
152+ context . InputDecimalSeparator = comma ;
146153 return numberStr . Replace ( dot , string . Empty ) . Replace ( comma , dot ) ;
147154 }
148155 }
@@ -158,19 +165,19 @@ private string NormalizeNumber(string numberStr)
158165 // Ambiguous case: "1,234". Resolve using culture.
159166 if ( systemDecimalSeparator == comma )
160167 {
161- _inputDecimalSeparator = comma ;
168+ context . InputDecimalSeparator = comma ;
162169 return numberStr . Replace ( comma , dot ) ;
163170 }
164171 else
165172 {
166- _inputUsesGroupSeparators = true ;
173+ context . InputUsesGroupSeparators = true ;
167174 return numberStr . Replace ( comma , string . Empty ) ;
168175 }
169176 }
170177 else
171178 {
172179 // Unambiguous decimal: "123,45" or "1,2,345"
173- _inputDecimalSeparator = comma ;
180+ context . InputDecimalSeparator = comma ;
174181 return numberStr . Replace ( comma , dot ) ;
175182 }
176183 }
@@ -184,18 +191,18 @@ private string NormalizeNumber(string numberStr)
184191 {
185192 if ( systemDecimalSeparator == dot )
186193 {
187- _inputDecimalSeparator = dot ;
194+ context . InputDecimalSeparator = dot ;
188195 return numberStr ;
189196 }
190197 else
191198 {
192- _inputUsesGroupSeparators = true ;
199+ context . InputUsesGroupSeparators = true ;
193200 return numberStr . Replace ( dot , string . Empty ) ;
194201 }
195202 }
196203 else
197204 {
198- _inputDecimalSeparator = dot ;
205+ context . InputDecimalSeparator = dot ;
199206 return numberStr ; // Already in Mages-compatible format
200207 }
201208 }
@@ -204,10 +211,10 @@ private string NormalizeNumber(string numberStr)
204211 return numberStr ;
205212 }
206213
207- private string FormatResult ( decimal roundedResult )
214+ private string FormatResult ( decimal roundedResult , ParsingContext context )
208215 {
209216 // Use the detected decimal separator from the input; otherwise, fall back to settings.
210- string decimalSeparator = _inputDecimalSeparator ?? GetDecimalSeparator ( ) ;
217+ string decimalSeparator = context . InputDecimalSeparator ?? GetDecimalSeparator ( ) ;
211218 string groupSeparator = decimalSeparator == dot ? comma : dot ;
212219
213220 string resultStr = roundedResult . ToString ( CultureInfo . InvariantCulture ) ;
@@ -216,7 +223,7 @@ private string FormatResult(decimal roundedResult)
216223 string integerPart = parts [ 0 ] ;
217224 string fractionalPart = parts . Length > 1 ? parts [ 1 ] : string . Empty ;
218225
219- if ( _inputUsesGroupSeparators )
226+ if ( context . InputUsesGroupSeparators )
220227 {
221228 integerPart = ThousandGroupRegex . Replace ( integerPart , groupSeparator ) ;
222229 }
0 commit comments