Skip to content

Conversation

dcog989
Copy link
Contributor

@dcog989 dcog989 commented Sep 12, 2025

Backout 'smart' digit separation & Fix Mages functions for Calculator plugin

Fixes #3750, #3912, #3974

@prlabeler prlabeler bot added the bug Something isn't working label Sep 12, 2025
@github-actions github-actions bot added this to the 2.1.0 milestone Sep 12, 2025
Copy link

gitstream-cm bot commented Sep 12, 2025

🥷 Code experts: VictoriousRaptor, jjw24

VictoriousRaptor, jjw24 have most 👩‍💻 activity in the files.
jjw24, VictoriousRaptor have most 🧠 knowledge in the files.

See details

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

Activity based on git-commit:

VictoriousRaptor jjw24
SEP
AUG
JUL 18 additions & 20 deletions
JUN
MAY
APR

Knowledge based on git-blame:
jjw24: 18%
VictoriousRaptor: 13%

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

Activity based on git-commit:

VictoriousRaptor jjw24
SEP
AUG
JUL 19 additions & 0 deletions
JUN
MAY
APR

Knowledge based on git-blame:
VictoriousRaptor: 100%

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

Activity based on git-commit:

VictoriousRaptor jjw24
SEP
AUG
JUL
JUN 2 additions & 2 deletions
MAY 1 additions & 1 deletions
APR

Knowledge based on git-blame:
jjw24: 58%
VictoriousRaptor: 8%

✨ Comment /gs review for LinearB AI review. Learn how to automate it here.

Copy link

gitstream-cm bot commented Sep 12, 2025

Be a legend 🏆 by adding a before and after screenshot of the changes you made, especially if they are around UI/UX.

Copy link
Contributor

coderabbitai bot commented Sep 12, 2025

📝 Walkthrough

Walkthrough

Refactors calculator plugin to be stateless and culture-aware: adds function detection and pow/log/ln rewrites, improves number normalization and formatting with configurable decimal/group separators, updates engine result mapping and copy-to-clipboard behaviour, introduces ShowErrorMessage setting/UI, regex updates, and adds unit tests.

Changes

Cohort / File(s) Summary
Core logic
Plugins/Flow.Launcher.Plugin.Calculator/Main.cs
Removed per-query ParsingContext; added IcoPath and EmptyResults; early exit on empty/whitespace queries; detect functions via FunctionRegex; determine decimal/group separators; NormalizeNumber(...) now distinguishes function vs non-function parsing; iterative pow(...), log(...), ln(...) rewrites; engine-result mapping updated (null/Function/NaN handling, rounding, FormatResult); exceptions return NotComplete-like result when ShowErrorMessage enabled; copy-to-clipboard sets CopyText/SubTitle and handles copy errors.
Regex helpers
Plugins/Flow.Launcher.Plugin.Calculator/MainRegexHelper.cs
Removed GetRegBrackets() and GetRegValidExpressChar(); expanded GetNumberRegex() to allow optional leading minus and extra grouping chars; added GetPowRegex(), GetLogRegex(), GetLnRegex() (with RightToLeft for nested args where appropriate) and GetFunctionRegex() to detect function calls.
Settings & UI
Plugins/Flow.Launcher.Plugin.Calculator/Settings.cs
Plugins/Flow.Launcher.Plugin.Calculator/Views/CalculatorSettings.xaml
Plugins/Flow.Launcher.Plugin.Calculator/Views/CalculatorSettings.xaml.cs
Plugins/Flow.Launcher.Plugin.Calculator/ViewModels/SettingsViewModel.cs
Flattened Settings type (removed nested inner class); added public bool ShowErrorMessage { get; set; } = false; settings UI adds CheckBox bound to ShowErrorMessage; view/viewmodel and code-behind converted to file-scoped namespaces and simplified constructors/initialization.
Manifest & localization
Plugins/Flow.Launcher.Plugin.Calculator/plugin.json
Plugins/Flow.Launcher.Plugin.Calculator/Languages/en.xaml
Updated plugin description to mention hex values and advanced functions with examples; added flowlauncher_plugin_calculator_show_error_message resource string.
Tests & project refs
Flow.Launcher.Test/Flow.Launcher.Test.csproj
Flow.Launcher.Test/Plugins/CalculatorTest.cs
Test project now references Calculator plugin; added CalculatorPluginTest with a suite of expression test cases and injected engine scope for deterministic results.

Sequence Diagram(s)

sequenceDiagram
  participant U as User
  participant PL as Calculator Plugin
  participant RH as Regex Helpers
  participant EN as Expression Engine

  U->>PL: Query(text)
  PL->>PL: Trim + check empty/whitespace
  alt empty/whitespace
    PL-->>U: NotComplete / EmptyResults (depends on ShowErrorMessage)
  else
    PL->>RH: FunctionRegex.IsMatch(text)
    RH-->>PL: isFunctionPresent
    PL->>PL: GetDecimalSeparator/GetGroupSeparator
    PL->>PL: NormalizeNumber(text, isFunctionPresent, decimalSep, groupSep)
    loop rewrite nested pow (RightToLeft)
      PL->>RH: PowRegex.Match
      RH-->>PL: pow match
      PL->>PL: PowMatchEvaluator -> replace pow(...) with (a^b)
    end
    loop rewrite log/ln
      PL->>RH: LogRegex / LnRegex.Match
      RH-->>PL: match
      PL->>PL: Log/LnMatchEvaluator -> rewrite to log10()/log()
    end
    PL->>EN: Evaluate(rewrittenExpression)
    EN-->>PL: result
    alt null / Function / empty
      PL-->>U: NotComplete-like result (with icon) or EmptyResults
    else NaN
      PL-->>U: Localized "Not a number"
    else numeric/string
      PL->>PL: Round to MaxDecimalPlaces & FormatResult
      PL-->>U: Result (Title, SubTitle, Copy action, CopyText)
    end
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • jjw24
  • VictoriousRaptor

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning Although the PR addresses the linked decimal-separator issue, the changeset also includes several edits beyond that single objective: Mages function rewrites and pow/log/ln regexes, a ShowErrorMessage setting plus UI/resource changes, a plugin.json description update, removal/flattening of a nested Settings type, and a new test project reference with many NUnit tests. Those items appear to address other bugs and refactors and therefore extend the scope beyond the linked issue provided. Either narrow the PR to the locale-related changes or explicitly link and document the additional issues this PR resolves (for example #3912 and #3974), and ensure the Settings refactor preserves backward compatibility; alternatively split the UI/settings/refactor and large test additions into separate PRs so reviewers can review scope-limited changes.
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The title succinctly and accurately summarizes the primary work in this changeset: reverting the "smart" digit-separation behavior and fixing Mages-related function parsing for the Calculator plugin, which matches the normalization, regex, and function-rewrite changes in the diff. It is specific, concise, and directly related to the main code changes.
Linked Issues Check ✅ Passed The code implements the core requirements of the linked issue [#3750]: it restores locale-aware decimal/grouping handling via GetDecimalSeparator/GetGroupSeparator, updates number normalization to strip or respect grouping separators as appropriate, adjusts the number regex to accept locale group characters, and adds tests covering comma/dot scenarios, which together satisfy the issue's objective to accept user-locale number input.
Description Check ✅ Passed The PR description is on-topic and directly states the two main goals—restoring locale-based digit separators and adding a workaround for the Mages issue—while referencing the fixed issue numbers, so it relates to the changeset.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (10)
Plugins/Flow.Launcher.Plugin.Calculator/MainRegexHelper.cs (1)

11-12: Include locale space-separators (NBSP/narrow NBSP, apostrophe) in number regex.

This helps parse inputs like “12 345”, “12 345”, or “12'345” that many locales produce, while still supporting negatives.

-    [GeneratedRegex(@"-?[\d\.,]+", RegexOptions.Compiled)]
+    // Allow NBSP (\u00A0), narrow NBSP (\u202F), and apostrophe (') as group separators.
+    [GeneratedRegex(@"-?[\d\.,\u00A0\u202F']+", RegexOptions.Compiled)]
     public static partial Regex GetNumberRegex();
Plugins/Flow.Launcher.Plugin.Calculator/plugin.json (1)

5-5: Fix description punctuation and wording.

Double period and minor grammar; tighten phrasing.

-  "Description": "Perform mathematical calculations, including hex values and advanced math functions, such as 'min(1,2,3)', 'sqrt(123)', 'cos(123)', etc.. User locale determines thousand separator and decimal place.",
+  "Description": "Perform mathematical calculations, including hex values and advanced math functions such as 'min(1,2,3)', 'sqrt(123)', 'cos(123)', etc. User locale determines the thousands and decimal separators.",
Plugins/Flow.Launcher.Plugin.Calculator/Main.cs (8)

53-54: Consider NBSP/narrow NBSP grouping in NumberRegex matches.

If users paste results containing “12 345” or “12 345”, current regex won’t match them as a single token. The proposed regex change in MainRegexHelper.cs addresses this. Ensure both files land together.


69-80: Avoid repeated ToString() and handle Infinity explicitly.

Cache once and guard NaN/Infinity across numeric types. Minor perf/readability win.

-                if (result == null || string.IsNullOrEmpty(result.ToString()))
+                var resultStr = result?.ToString();
+                if (string.IsNullOrEmpty(resultStr))
                 {
                     return new List<Result>
                     {
                         new Result
                         {
                             Title = Localize.flowlauncher_plugin_calculator_expression_not_complete(),
                             IcoPath = "Images/calculator.png"
                         }
                     };
                 }

And just below:

-                if (result.ToString() == "NaN")
+                if ((result is double dd && (double.IsNaN(dd) || double.IsInfinity(dd))) ||
+                    (result is float ff && (float.IsNaN(ff) || float.IsInfinity(ff))) ||
+                    string.Equals(resultStr, "NaN", StringComparison.Ordinal))
                     result = Localize.flowlauncher_plugin_calculator_not_a_number();

87-91: Guard decimal conversion for large exponents.

Convert.ToDecimal can overflow for big results (e.g., 2^1000). Consider falling back to invariant string formatting when conversion fails.

Example pattern:

decimal roundedResult;
try { roundedResult = Math.Round(Convert.ToDecimal(result), _settings.MaxDecimalPlaces, MidpointRounding.AwayFromZero); }
catch (OverflowException) { return ResultFromString(resultStr); } // formatting path without decimal

138-173: Minor fixes: use cached decimalSep; strip NBSP variants when normalizing.

  • Use the local decimalSep variable consistently.
  • Remove both NBSP and narrow NBSP groupers inside matched numbers to accommodate pasted locale output.
-                    if (i == parts.Length - 1 && part.Contains(culture.NumberFormat.NumberDecimalSeparator))
+                    if (i == parts.Length - 1 && part.Contains(decimalSep))
                     {
-                        part = part.Split(culture.NumberFormat.NumberDecimalSeparator)[0];
+                        part = part.Split(decimalSep)[0];
                     }
-            string processedStr = numberStr.Replace(groupSep, "");
-            processedStr = processedStr.Replace(decimalSep, ".");
+            string processedStr = numberStr.Replace(groupSep, "");
+            // Also strip common space-based group separators if present.
+            processedStr = processedStr.Replace("\u00A0", "").Replace("\u202F", "");
+            processedStr = processedStr.Replace(decimalSep, ".");
             return processedStr;

175-197: Locale-aware grouping sizes (nice-to-have).

Indian locales (e.g., hi-IN) use 3,2 grouping. If UseSystemLocale is selected, consider NumberFormatInfo.NumberGroupSizes for insertion instead of regex.


199-209: Group separator selection: clarify behavior in UI.

When not using system locale, you flip between “.” and “,”. Document in settings tooltip that grouping will inverse the chosen decimal separator.


213-224: Pre-validation is very permissive now; that’s fine, but add quick length guard.

Optional: short-circuit extremely long inputs to avoid heavy regex work during Replace.


238-255: Bracket completeness doesn’t detect mis-nesting.

“([)]” passes. Use a small stack to enforce proper pairing; still cheap.

Example:

private static bool IsBracketCompleteStrict(string s)
{
    var st = new Stack<char>();
    foreach (var c in s)
    {
        if (c is '(' or '[') st.Push(c);
        else if (c is ')' or ']')
        {
            if (st.Count == 0) return false;
            var open = st.Pop();
            if ((c == ')' && open != '(') || (c == ']' && open != '[')) return false;
        }
    }
    return st.Count == 0;
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b898c46 and bd186e7.

📒 Files selected for processing (3)
  • Plugins/Flow.Launcher.Plugin.Calculator/Main.cs (4 hunks)
  • Plugins/Flow.Launcher.Plugin.Calculator/MainRegexHelper.cs (1 hunks)
  • Plugins/Flow.Launcher.Plugin.Calculator/plugin.json (1 hunks)
🧰 Additional context used
🪛 GitHub Check: Check Spelling
Plugins/Flow.Launcher.Plugin.Calculator/Main.cs

[warning] 63-63:
bpow is not a recognized word. (unrecognized-spelling)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: gitStream.cm
  • GitHub Check: build

dcog989 and others added 2 commits September 13, 2025 13:17
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
Plugins/Flow.Launcher.Plugin.Calculator/Main.cs (4)

19-20: Harden pow-regex: add timeout and CultureInvariant; silence linter noise.

Current PowRegex has no match timeout and uses culture-sensitive boundaries, which risks pathological inputs hanging the UI and triggers the spelling checker's “bpow” warning. Safer to use a culture-invariant boundary and a small timeout.

-        private static readonly Regex PowRegex = new(@"\bpow(\((?:[^()\[\]]|\((?<Depth>)|\)(?<-Depth>)|\[(?<Depth>)|\](?<-Depth>))*(?(Depth)(?!))\))", RegexOptions.Compiled | RegexOptions.RightToLeft);
+        private static readonly Regex PowRegex = new(
+            @"(?<!\w)pow(\((?:[^()\[\]]|\((?<Depth>)|\)(?<-Depth>)|\[(?<Depth>)|\](?<-Depth>))*(?(Depth)(?!))\))",
+            RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.RightToLeft,
+            TimeSpan.FromMilliseconds(100));

83-94: Minor: avoid repeated ToString() calls.

Cache result.ToString() once to reduce allocations and double evaluation.

-                if (result == null || string.IsNullOrEmpty(result.ToString()))
+                var resultStr = result?.ToString();
+                if (string.IsNullOrEmpty(resultStr))
                 {
                     ...
                 }

95-100: Use type checks for NaN/Infinity instead of string comparison.

Comparing ToString() to "NaN" is brittle. Prefer numeric checks.

-                if (result.ToString() == "NaN")
-                    result = Localize.flowlauncher_plugin_calculator_not_a_number();
+                if (result is double d && (double.IsNaN(d) || double.IsInfinity(d)))
+                    result = Localize.flowlauncher_plugin_calculator_not_a_number();

240-274: Consider using NumberFormatInfo for locale-correct grouping (e.g., Indian 2-2-3).

Regex-based 3-digit grouping won’t match all locales. You can render via a tailored NumberFormatInfo to respect both separators and group sizes.

Example approach (outside this range):

  • Build a NumberFormatInfo based on system when UseSystemLocale, else clone Invariant and set NumberDecimalSeparator/NumberGroupSeparator.
  • Format with roundedResult.ToString("N" + _settings.MaxDecimalPlaces, nfi).
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bd186e7 and 6462023.

📒 Files selected for processing (1)
  • Plugins/Flow.Launcher.Plugin.Calculator/Main.cs (6 hunks)
🧰 Additional context used
🪛 GitHub Check: Check Spelling
Plugins/Flow.Launcher.Plugin.Calculator/Main.cs

[warning] 19-19:
bpow is not a recognized word. (unrecognized-spelling)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: gitStream.cm
  • GitHub Check: build
🔇 Additional comments (5)
Plugins/Flow.Launcher.Plugin.Calculator/Main.cs (5)

48-63: Nice UX: early-return with “expression not complete”.

Bracket-completeness guard reduces noisy errors and guides the user well.


69-79: Bracket-aware pow(x,y) rewrite: good fix for Mages bug.

Handles nesting and commas correctly; loop converges. Once Mages fixes pow, this remains harmless.


117-127: Clipboard action with exception handling looks good.

Robust copy flow and user feedback are well handled.


148-197: PowMatchEvaluator logic LGTM.

Top-level comma detection with bracket depth across () and [] is correct; malformed cases safely fall back.


288-310: Balanced-bracket scan is straightforward and safe.

Immediate negative check prevents false completeness; good.

@Jack251970 Jack251970 changed the title Calculator plugin - backout 'smart' digit separation, fix Mages functions Backout 'smart' digit separation & Fix Mages functions for Calculator plugin Sep 14, 2025
Jack251970
Jack251970 previously approved these changes Sep 14, 2025
Copy link
Member

@Jack251970 Jack251970 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, it works well for me

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (7)
Plugins/Flow.Launcher.Plugin.Calculator/MainRegexHelper.cs (1)

16-17: Pow(...) detector is solid; add CultureInvariant for stability.

Add RegexOptions.CultureInvariant to avoid any culture-specific word-boundary quirks.

-    [GeneratedRegex(@"\bpow(\((?:[^()\[\]]|\((?<Depth>)|\)(?<-Depth>)|\[(?<Depth>)|\](?<-Depth>))*(?(Depth)(?!))\))", RegexOptions.Compiled | RegexOptions.RightToLeft)]
+    [GeneratedRegex(@"\bpow(\((?:[^()\[\]]|\((?<Depth>)|\)(?<-Depth>)|\[(?<Depth>)|\](?<-Depth>))*(?(Depth)(?!))\))", RegexOptions.Compiled | RegexOptions.RightToLeft | RegexOptions.CultureInvariant)]
Plugins/Flow.Launcher.Plugin.Calculator/Main.cs (6)

65-65: Avoid per-match culture lookups in NormalizeNumber.

Cache separators once and pass into the evaluator to reduce allocations/GC in long inputs.

-                var expression = NumberRegex.Replace(query.Search, m => NormalizeNumber(m.Value));
+                var culture = CultureInfo.CurrentCulture;
+                var groupSep = culture.NumberFormat.NumberGroupSeparator;
+                var decimalSep = culture.NumberFormat.NumberDecimalSeparator;
+                var expression = NumberRegex.Replace(query.Search, m => NormalizeNumber(m.Value, groupSep, decimalSep));

And change signature accordingly (see lines 197-206).


93-95: Use type checks for NaN/Infinity instead of string comparison.

To avoid culture/format surprises, check numeric types directly; also handle ±Infinity to prevent Convert.ToDecimal exceptions.

-                if (result.ToString() == "NaN")
-                    result = Localize.flowlauncher_plugin_calculator_not_a_number();
+                if (result is double d && double.IsNaN(d))
+                    result = Localize.flowlauncher_plugin_calculator_not_a_number();
+                else if (result is double d2 && double.IsInfinity(d2))
+                    result = Localize.flowlauncher_plugin_calculator_expression_not_complete();

132-140: Swallowing all exceptions hides diagnostics; add a debug trace.

Log the exception (at least in debug) to aid field investigations; keep user-facing message unchanged.

-            catch (Exception)
+            catch (Exception ex)
             {
+                System.Diagnostics.Debug.WriteLine($"Calculator evaluation error: {ex}");
                 // Mages engine can throw various exceptions, for simplicity we catch them all and show a generic message.

146-196: PowMatchEvaluator: handles nesting and commas correctly.

Good bracket-depth split. One edge: if someone passes 3+ args (unexpected), we currently fold arg2..n into the right side. Consider bailing out when additional top‑level commas exist in arg2 to defer to Mages’ error.

-            var arg2 = argsContent.Substring(splitIndex + 1).Trim();
+            var arg2 = argsContent.Substring(splitIndex + 1).Trim();
+            // If there is another top-level comma in arg2, treat as non-2-arg pow and bail.
+            var depth = 0;
+            for (int i = 0; i < arg2.Length; i++)
+            {
+                var c = arg2[i];
+                if (c == '(' || c == '[') depth++;
+                else if (c == ')' || c == ']') depth--;
+                else if (c == ',' && depth == 0) return m.Value;
+            }

197-236: NormalizeNumber: keep validation; pass separators as params for clarity/perf.

Intent (use system locale for input; plugin setting for output) is sound and preserves min(2,3). Make separators explicit to document the contract and avoid repeated CultureInfo lookups.

-        private static string NormalizeNumber(string numberStr)
+        private static string NormalizeNumber(string numberStr, string groupSep, string decimalSep)
         {
-            var culture = CultureInfo.CurrentCulture;
-            var groupSep = culture.NumberFormat.NumberGroupSeparator;
-            var decimalSep = culture.NumberFormat.NumberDecimalSeparator;
+            // Input normalization intentionally uses system locale separators.

Also update the decimal check inside the loop to use the passed-in decimalSep.

-                    if (i == parts.Length - 1 && part.Contains(culture.NumberFormat.NumberDecimalSeparator))
+                    if (i == parts.Length - 1 && part.Contains(decimalSep))

286-308: Minor: variable name nit.

Rename “matchs” → “matches”.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6462023 and 495ace1.

📒 Files selected for processing (4)
  • Plugins/Flow.Launcher.Plugin.Calculator/Languages/en.xaml (1 hunks)
  • Plugins/Flow.Launcher.Plugin.Calculator/Main.cs (6 hunks)
  • Plugins/Flow.Launcher.Plugin.Calculator/MainRegexHelper.cs (1 hunks)
  • Plugins/Flow.Launcher.Plugin.Calculator/plugin.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • Plugins/Flow.Launcher.Plugin.Calculator/plugin.json
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: dcog989
PR: Flow-Launcher/Flow.Launcher#3971
File: Plugins/Flow.Launcher.Plugin.Calculator/Main.cs:67-67
Timestamp: 2025-09-13T12:50:05.270Z
Learning: In Flow.Launcher Calculator plugin, NormalizeNumber must validate thousand separator usage before stripping them. If a comma-separated token doesn't follow proper thousand separator rules (3 digits between separators), it should be left unmodified to preserve function arguments like in "min(2,3)".
Learnt from: dcog989
PR: Flow-Launcher/Flow.Launcher#3971
File: Plugins/Flow.Launcher.Plugin.Calculator/Main.cs:67-67
Timestamp: 2025-09-13T12:50:05.271Z
Learning: NumberRegex pattern `-?[\d\.,]+` in Flow.Launcher Calculator will match function arguments like `2,3` in `min(2,3)`. The NormalizeNumber validation that checks for proper 3-digit thousand separator grouping is essential to avoid breaking function calls by incorrectly stripping commas from function arguments.
📚 Learning: 2025-09-13T12:50:05.271Z
Learnt from: dcog989
PR: Flow-Launcher/Flow.Launcher#3971
File: Plugins/Flow.Launcher.Plugin.Calculator/Main.cs:67-67
Timestamp: 2025-09-13T12:50:05.271Z
Learning: NumberRegex pattern `-?[\d\.,]+` in Flow.Launcher Calculator will match function arguments like `2,3` in `min(2,3)`. The NormalizeNumber validation that checks for proper 3-digit thousand separator grouping is essential to avoid breaking function calls by incorrectly stripping commas from function arguments.

Applied to files:

  • Plugins/Flow.Launcher.Plugin.Calculator/MainRegexHelper.cs
  • Plugins/Flow.Launcher.Plugin.Calculator/Main.cs
📚 Learning: 2025-09-13T12:50:05.270Z
Learnt from: dcog989
PR: Flow-Launcher/Flow.Launcher#3971
File: Plugins/Flow.Launcher.Plugin.Calculator/Main.cs:67-67
Timestamp: 2025-09-13T12:50:05.270Z
Learning: In Flow.Launcher Calculator plugin, NormalizeNumber must validate thousand separator usage before stripping them. If a comma-separated token doesn't follow proper thousand separator rules (3 digits between separators), it should be left unmodified to preserve function arguments like in "min(2,3)".

Applied to files:

  • Plugins/Flow.Launcher.Plugin.Calculator/Main.cs
🧬 Code graph analysis (1)
Plugins/Flow.Launcher.Plugin.Calculator/Main.cs (1)
Plugins/Flow.Launcher.Plugin.Calculator/MainRegexHelper.cs (1)
  • MainRegexHelper (5-18)
🪛 GitHub Check: Check Spelling
Plugins/Flow.Launcher.Plugin.Calculator/MainRegexHelper.cs

[warning] 16-16:
bpow is not a recognized word. (unrecognized-spelling)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (8)
Plugins/Flow.Launcher.Plugin.Calculator/Languages/en.xaml (1)

7-7: Description update looks good; ensure other locales are synced.

Please propagate this wording to all localized xaml/json resources to avoid inconsistent plugin descriptions across languages.

Plugins/Flow.Launcher.Plugin.Calculator/MainRegexHelper.cs (1)

10-11: Number regex change to allow leading minus: LGTM.

Covers negatives without altering existing grouping/decimal handling.

Plugins/Flow.Launcher.Plugin.Calculator/Main.cs (6)

46-50: Early empty-query return: LGTM.


51-61: Bracket completeness UX: LGTM.

Nice guard; prevents noisy exceptions from the engine.


67-77: Pow rewrite: correct and bracket-aware.

Loop with RightToLeft + evaluator handles nesting; termination is guaranteed. Nice.


81-92: Null/empty result handling: LGTM.


99-103: Result formatting path: LGTM.

Rounding then culture-aware formatting is consistent.


238-272: Formatting: negative numbers and grouping — regex is fine.

Regex-based grouping works with negatives via \B; fallback mapping of group sep vs decimal sep is reasonable.

@Jack251970 Jack251970 requested a review from jjw24 September 14, 2025 07:51
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Plugins/Flow.Launcher.Plugin.Calculator/Main.cs (1)

81-116: Bug: converting string results to decimal throws and masks intended messages.

After mapping NaN/function results to localized strings, the code still runs Convert.ToDecimal(result) which will throw, hit the catch, and show the generic “expression not complete” instead of the intended message. Handle these cases with early returns and only format numeric results.

-                if (result.ToString() == "NaN")
-                    result = Localize.flowlauncher_plugin_calculator_not_a_number();
-
-                if (result is Function)
-                    result = Localize.flowlauncher_plugin_calculator_expression_not_complete();
-
-                if (!string.IsNullOrEmpty(result.ToString()))
-                {
-                    decimal roundedResult = Math.Round(Convert.ToDecimal(result), _settings.MaxDecimalPlaces, MidpointRounding.AwayFromZero);
-                    string newResult = FormatResult(roundedResult);
+                // Handle non-numeric outcomes first
+                if (result is double d && (double.IsNaN(d) || double.IsInfinity(d)))
+                {
+                    return new List<Result>
+                    {
+                        new Result { Title = Localize.flowlauncher_plugin_calculator_not_a_number(), IcoPath = "Images/calculator.png" }
+                    };
+                }
+                if (result is Function)
+                {
+                    return new List<Result>
+                    {
+                        new Result { Title = Localize.flowlauncher_plugin_calculator_expression_not_complete(), IcoPath = "Images/calculator.png" }
+                    };
+                }
+
+                // Numeric formatting path
+                if (!string.IsNullOrEmpty(result.ToString()))
+                {
+                    decimal roundedResult = Math.Round(Convert.ToDecimal(result, CultureInfo.InvariantCulture), _settings.MaxDecimalPlaces, MidpointRounding.AwayFromZero);
+                    string newResult = FormatResult(roundedResult);
🧹 Nitpick comments (4)
Plugins/Flow.Launcher.Plugin.Calculator/Main.cs (4)

53-66: Bracket-aware pow() rewrite: solid approach; loop likely unnecessary.

Using a balancing‑group regex plus a top‑level comma splitter fixes nested cases (e.g., pow(min(2,3),4)) reliably. Since Regex.Replace processes all matches, the do/while is probably redundant and can be removed for a tiny perf/readability win.

-                string previous;
-                do
-                {
-                    previous = expression;
-                    expression = PowRegex.Replace(previous, PowMatchEvaluator);
-                } while (previous != expression);
+                expression = PowRegex.Replace(expression, PowMatchEvaluator);

134-183: Guard against extra top-level commas in pow(...) to avoid malformed rewrites.

pow(2,3,4) should not rewrite to “(2^3,4)”. If more than one top‑level comma exists, leave the match unchanged and let Mages error out.

-            // Find the top-level comma that separates the two arguments of pow.
+            // Find the top-level comma that separates the two arguments of pow.
             for (var i = 0; i < argsContent.Length; i++)
             {
                 switch (argsContent[i])
                 {
                     case '(':
                     case '[':
                         bracketCount++;
                         break;
                     case ')':
                     case ']':
                         bracketCount--;
                         break;
                     case ',' when bracketCount == 0:
                         splitIndex = i;
                         break;
                 }
 
                 if (splitIndex != -1)
                     break;
             }
 
             if (splitIndex == -1)
             {
                 // This indicates malformed arguments for pow, e.g., pow(5) or pow().
                 // Return original string to let Mages handle the error.
                 return m.Value;
             }
 
             var arg1 = argsContent.Substring(0, splitIndex).Trim();
-            var arg2 = argsContent.Substring(splitIndex + 1).Trim();
+            var arg2 = argsContent.Substring(splitIndex + 1).Trim();
+
+            // Ensure there is no second top-level comma (i.e., exactly two args)
+            bracketCount = 0;
+            for (var i = splitIndex + 1; i < argsContent.Length; i++)
+            {
+                var c = argsContent[i];
+                if (c == '(' || c == '[') bracketCount++;
+                else if (c == ')' || c == ']') bracketCount--;
+                else if (c == ',' && bracketCount == 0) return m.Value; // >2 args: don't rewrite
+            }

189-224: Input normalization uses OS locale only; confirm intent vs plugin DecimalSeparator setting.

NormalizeNumber relies on CultureInfo.CurrentCulture. If a user selects DecimalSeparator=Comma on an en‑US system, inputs like “1,23+4” will likely not evaluate as intended because commas are preserved to avoid breaking function args. If the feature goal is “output honors setting, input honors OS locale,” please document this; otherwise, consider aligning input normalization with the plugin setting while preserving the “3‑digit thousand grouping” safeguard.

Would you like a patch that respects the plugin DecimalSeparator for input only when the token is an unambiguous decimal (e.g., contains a single decimal sep and no group seps), falling back to “leave as-is” when ambiguous (like “2,3”)?


226-248: Large number/precision path: avoid decimal overflow and preserve precision.

Convert.ToDecimal(double) can overflow for very large magnitudes, falling into the catch and showing a generic message. Consider:

  • If result is decimal -> round directly.
  • If result is double -> handle NaN/Infinity earlier (already suggested) and use decimal.TryParse from invariant string as fallback; if that fails, format as double with invariant and skip grouping/rounding.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 495ace1 and daf35a4.

📒 Files selected for processing (1)
  • Plugins/Flow.Launcher.Plugin.Calculator/Main.cs (5 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: dcog989
PR: Flow-Launcher/Flow.Launcher#3971
File: Plugins/Flow.Launcher.Plugin.Calculator/Main.cs:67-67
Timestamp: 2025-09-13T12:50:05.270Z
Learning: In Flow.Launcher Calculator plugin, NormalizeNumber must validate thousand separator usage before stripping them. If a comma-separated token doesn't follow proper thousand separator rules (3 digits between separators), it should be left unmodified to preserve function arguments like in "min(2,3)".
Learnt from: dcog989
PR: Flow-Launcher/Flow.Launcher#3971
File: Plugins/Flow.Launcher.Plugin.Calculator/Main.cs:67-67
Timestamp: 2025-09-13T12:50:05.271Z
Learning: NumberRegex pattern `-?[\d\.,]+` in Flow.Launcher Calculator will match function arguments like `2,3` in `min(2,3)`. The NormalizeNumber validation that checks for proper 3-digit thousand separator grouping is essential to avoid breaking function calls by incorrectly stripping commas from function arguments.
📚 Learning: 2025-09-13T12:50:05.270Z
Learnt from: dcog989
PR: Flow-Launcher/Flow.Launcher#3971
File: Plugins/Flow.Launcher.Plugin.Calculator/Main.cs:67-67
Timestamp: 2025-09-13T12:50:05.270Z
Learning: In Flow.Launcher Calculator plugin, NormalizeNumber must validate thousand separator usage before stripping them. If a comma-separated token doesn't follow proper thousand separator rules (3 digits between separators), it should be left unmodified to preserve function arguments like in "min(2,3)".

Applied to files:

  • Plugins/Flow.Launcher.Plugin.Calculator/Main.cs
📚 Learning: 2025-09-13T12:50:05.271Z
Learnt from: dcog989
PR: Flow-Launcher/Flow.Launcher#3971
File: Plugins/Flow.Launcher.Plugin.Calculator/Main.cs:67-67
Timestamp: 2025-09-13T12:50:05.271Z
Learning: NumberRegex pattern `-?[\d\.,]+` in Flow.Launcher Calculator will match function arguments like `2,3` in `min(2,3)`. The NormalizeNumber validation that checks for proper 3-digit thousand separator grouping is essential to avoid breaking function calls by incorrectly stripping commas from function arguments.

Applied to files:

  • Plugins/Flow.Launcher.Plugin.Calculator/Main.cs
🧬 Code graph analysis (1)
Plugins/Flow.Launcher.Plugin.Calculator/Main.cs (1)
Plugins/Flow.Launcher.Plugin.Calculator/MainRegexHelper.cs (1)
  • MainRegexHelper (5-18)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: gitStream.cm
  • GitHub Check: build
🔇 Additional comments (2)
Plugins/Flow.Launcher.Plugin.Calculator/Main.cs (2)

18-18: PowRegex field addition looks good.

Localizing the pattern into MainRegexHelper and caching it here is a good move for perf and cohesion.


46-46: Early return on empty/whitespace query is fine.

Prevents unnecessary engine calls.

@Jack251970
Copy link
Member

Jack251970 commented Sep 14, 2025

@dcog989 I removed IsBracketComplete check because I think this is unnecessary. Let me if you have any other thoughts here.

I have observed one issue here - some expressions cannot work in the latest commit of this PR.

image image

Original behaviour:

image

Copy link
Contributor Author

@dcog989 dcog989 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

edit - pressed send too quickly! looking at feedback now

@dcog989
Copy link
Contributor Author

dcog989 commented Sep 14, 2025

I have observed one issue here - some expressions cannot work

@Jack251970 - what region / language / number format are you using?

@Jack251970
Copy link
Member

I have observed one issue here - some expressions cannot work

@Jack251970 - what region / language / number format are you using?

This is my language & region setting.

image

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Plugins/Flow.Launcher.Plugin.Calculator/Main.cs (1)

301-323: Use NumberFormatInfo for culture-correct grouping (e.g., hi-IN) and avoid padding zeros.

Regex-based 3-digit grouping won’t honor locales with non-3 group sizes. Format with NFI, then trim trailing fractional zeros.

Apply this diff:

-        private string FormatResult(decimal roundedResult)
-        {
-            string decimalSeparator = GetDecimalSeparator();
-            string groupSeparator = GetGroupSeparator(decimalSeparator);
-
-            string resultStr = roundedResult.ToString(CultureInfo.InvariantCulture);
-
-            string[] parts = resultStr.Split('.');
-            string integerPart = parts[0];
-            string fractionalPart = parts.Length > 1 ? parts[1] : string.Empty;
-
-            if (integerPart.Length > 3)
-            {
-                integerPart = ThousandGroupRegex.Replace(integerPart, groupSeparator);
-            }
-
-            if (!string.IsNullOrEmpty(fractionalPart))
-            {
-                return integerPart + decimalSeparator + fractionalPart;
-            }
-
-            return integerPart;
-        }
+        private string FormatResult(decimal roundedResult)
+        {
+            var nfi = (NumberFormatInfo)CultureInfo.CurrentCulture.NumberFormat.Clone();
+            nfi.NumberDecimalSeparator = GetDecimalSeparator();
+            nfi.NumberGroupSeparator = GetGroupSeparator(nfi.NumberDecimalSeparator);
+
+            var s = roundedResult.ToString("N" + _settings.MaxDecimalPlaces, nfi);
+            // Trim trailing fractional zeros to match previous behavior
+            if (_settings.MaxDecimalPlaces > 0 && s.Contains(nfi.NumberDecimalSeparator))
+            {
+                // Remove trailing zeros, then remove decimal separator if now trailing
+                int i = s.Length - 1;
+                while (i >= 0 && s[i] == '0') i--;
+                if (i >= 0 && s[i].ToString() == nfi.NumberDecimalSeparator) i--;
+                s = s.Substring(0, i + 1);
+            }
+            return s;
+        }
♻️ Duplicate comments (1)
Plugins/Flow.Launcher.Plugin.Calculator/Main.cs (1)

56-65: Global isFunctionPresent gate may leave valid grouped numbers un-normalized.

Example to double‑check: min(2,3)+1,234 should still parse 1,234 → 1234 even when a function exists elsewhere. If this already passes with Mages in your tests, ignore; otherwise consider per‑token validation (strip only when grouping is valid).

You can quickly sanity‑check behavior with these expressions:

  • min(2,3)+1,234
  • max(1, 2,3)
  • pow(min(2,3), 4)
🧹 Nitpick comments (1)
Plugins/Flow.Launcher.Plugin.Calculator/Main.cs (1)

202-243: Fix: dot-grouping validation should ignore the decimal fraction before IsValidGrouping.

With cultures like 1.234.567,89 (group '.'; decimal ','), parts[^1] contains the decimal part, causing IsValidGrouping to fail and dots not to be stripped, then replacing ','→'.' yields an invalid 1.234.567.89. Strip the decimal portion from the last group before validation.

Apply this diff inside the '.' branch:

-                        var parts = processedStr.Split('.');
-                        if (parts.Length > 1)
-                        {
-                            var culture = CultureInfo.CurrentCulture;
-                            if (IsValidGrouping(parts, culture.NumberFormat.NumberGroupSizes))
-                            {
-                                processedStr = processedStr.Replace(groupSep, "");
-                            }
-                            // If not grouped, it's likely a decimal number, so we don't strip dots.
-                        }
+                        var parts = processedStr.Split('.');
+                        if (parts.Length > 1)
+                        {
+                            // Exclude decimal fraction from the last group before validating grouping.
+                            var last = parts[^1];
+                            var decIdx = !string.IsNullOrEmpty(decimalSep) ? last.IndexOf(decimalSep, StringComparison.Ordinal) : -1;
+                            if (decIdx >= 0)
+                                parts[^1] = last[..decIdx];
+
+                            var culture = CultureInfo.CurrentCulture;
+                            if (IsValidGrouping(parts, culture.NumberFormat.NumberGroupSizes))
+                            {
+                                processedStr = processedStr.Replace(groupSep, "");
+                            }
+                            // If not grouped, it's likely a decimal number; don't strip dots.
+                        }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1906d68 and f9facda.

📒 Files selected for processing (4)
  • Plugins/Flow.Launcher.Plugin.Calculator/Main.cs (5 hunks)
  • Plugins/Flow.Launcher.Plugin.Calculator/Settings.cs (1 hunks)
  • Plugins/Flow.Launcher.Plugin.Calculator/ViewModels/SettingsViewModel.cs (1 hunks)
  • Plugins/Flow.Launcher.Plugin.Calculator/Views/CalculatorSettings.xaml.cs (1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: dcog989
PR: Flow-Launcher/Flow.Launcher#3971
File: Plugins/Flow.Launcher.Plugin.Calculator/Main.cs:67-67
Timestamp: 2025-09-13T12:50:05.270Z
Learning: In Flow.Launcher Calculator plugin, NormalizeNumber must validate thousand separator usage before stripping them. If a comma-separated token doesn't follow proper thousand separator rules (3 digits between separators), it should be left unmodified to preserve function arguments like in "min(2,3)".
Learnt from: dcog989
PR: Flow-Launcher/Flow.Launcher#3971
File: Plugins/Flow.Launcher.Plugin.Calculator/Main.cs:67-67
Timestamp: 2025-09-13T12:50:05.271Z
Learning: NumberRegex pattern `-?[\d\.,]+` in Flow.Launcher Calculator will match function arguments like `2,3` in `min(2,3)`. The NormalizeNumber validation that checks for proper 3-digit thousand separator grouping is essential to avoid breaking function calls by incorrectly stripping commas from function arguments.
📚 Learning: 2025-09-13T12:50:05.270Z
Learnt from: dcog989
PR: Flow-Launcher/Flow.Launcher#3971
File: Plugins/Flow.Launcher.Plugin.Calculator/Main.cs:67-67
Timestamp: 2025-09-13T12:50:05.270Z
Learning: In Flow.Launcher Calculator plugin, NormalizeNumber must validate thousand separator usage before stripping them. If a comma-separated token doesn't follow proper thousand separator rules (3 digits between separators), it should be left unmodified to preserve function arguments like in "min(2,3)".

Applied to files:

  • Plugins/Flow.Launcher.Plugin.Calculator/Settings.cs
  • Plugins/Flow.Launcher.Plugin.Calculator/Main.cs
📚 Learning: 2025-09-13T12:50:05.271Z
Learnt from: dcog989
PR: Flow-Launcher/Flow.Launcher#3971
File: Plugins/Flow.Launcher.Plugin.Calculator/Main.cs:67-67
Timestamp: 2025-09-13T12:50:05.271Z
Learning: NumberRegex pattern `-?[\d\.,]+` in Flow.Launcher Calculator will match function arguments like `2,3` in `min(2,3)`. The NormalizeNumber validation that checks for proper 3-digit thousand separator grouping is essential to avoid breaking function calls by incorrectly stripping commas from function arguments.

Applied to files:

  • Plugins/Flow.Launcher.Plugin.Calculator/Main.cs
🧬 Code graph analysis (3)
Plugins/Flow.Launcher.Plugin.Calculator/Views/CalculatorSettings.xaml.cs (2)
Plugins/Flow.Launcher.Plugin.Calculator/ViewModels/SettingsViewModel.cs (1)
  • SettingsViewModel (6-26)
Plugins/Flow.Launcher.Plugin.Calculator/Settings.cs (1)
  • Settings (4-11)
Plugins/Flow.Launcher.Plugin.Calculator/ViewModels/SettingsViewModel.cs (2)
Plugins/Flow.Launcher.Plugin.Calculator/Settings.cs (1)
  • Settings (4-11)
Plugins/Flow.Launcher.Plugin.Calculator/Main.cs (1)
  • List (47-149)
Plugins/Flow.Launcher.Plugin.Calculator/Main.cs (1)
Plugins/Flow.Launcher.Plugin.Calculator/MainRegexHelper.cs (1)
  • MainRegexHelper (5-18)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: gitStream.cm
  • GitHub Check: build
🔇 Additional comments (8)
Plugins/Flow.Launcher.Plugin.Calculator/Settings.cs (1)

6-10: LGTM: settings surface looks clean and consistent.

Defaults make sense; new ShowErrorMessage flag is wired across the UI per PR.

Plugins/Flow.Launcher.Plugin.Calculator/ViewModels/SettingsViewModel.cs (2)

6-8: Nice refactor to primary ctor + init-only property.

Simple and idiomatic; no behavioral changes.


14-23: Setter change is correct (fires only on change).

This avoids redundant notifications.

Plugins/Flow.Launcher.Plugin.Calculator/Views/CalculatorSettings.xaml.cs (1)

10-14: ViewModel initialization order is fine.

Setting DataContext before InitializeComponent is OK; if any XAML uses x:Bind-like patterns (not typical in WPF), consider moving after. No action required.

Plugins/Flow.Launcher.Plugin.Calculator/Main.cs (4)

66-76: Pow rewrite: bracket-aware approach looks solid.

Iterative Replace with the balanced‑parens regex plus evaluator resolves nested cases.


80-91: Graceful “incomplete” handling gated by ShowErrorMessage.

Matches the new setting; good UX.


151-200: PowMatchEvaluator handles nesting and empty args.

Good defensive checks; trims args; returns original on malformed input.


24-26: Icon path verified — no action required.
Images/calculator.png exists at Plugins/Flow.Launcher.Plugin.Calculator/Images/calculator.png (also at Flow.Launcher/Images/calculator.png); plugin.json and Main.cs reference the same Images/calculator.png path — icon will be loaded at runtime.

Jack251970
Jack251970 previously approved these changes Sep 15, 2025
Copy link
Member

@Jack251970 Jack251970 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM👍

@dcog989
Copy link
Contributor Author

dcog989 commented Sep 15, 2025

@Jack251970 - thanks for your help / additions to this. Looks good but lots of 'moving parts' with this one so real world testing needed!

@Jack251970
Copy link
Member

Jack251970 commented Sep 16, 2025

@Jack251970 - thanks for your help / additions to this. Looks good but lots of 'moving parts' with this one so real world testing needed!

Thanks for your suggestion! I resolved one issue with log & ln function (FlorianRappl/Mages#137). I also have added unit tests for calculator and I wonder if you want to add any other unit tests like divider , & . tests?

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
Plugins/Flow.Launcher.Plugin.Calculator/Main.cs (1)

256-312: Strict comma rule blocks grouped numbers when any function exists.

Tokens like “min(2,3)+1,234” won’t normalize the “1,234” part. Consider validating per‑token grouping (only preserve commas when grouping is invalid), not a global gate.

Would you like a focused diff that uses IsValidGrouping to decide per token?

🧹 Nitpick comments (6)
Plugins/Flow.Launcher.Plugin.Calculator/MainRegexHelper.cs (1)

7-8: Include curly apostrophe U+2019 in number-token regex.

Many locales use U+2019 (’). Without it, tokens like 1’234 won’t normalize.

Apply:

-    [GeneratedRegex(@"-?[\d\.,'\u00A0\u202F]+", RegexOptions.Compiled | RegexOptions.CultureInvariant)]
+    [GeneratedRegex(@"-?[\d\.,'\u2019\u00A0\u202F]+", RegexOptions.Compiled | RegexOptions.CultureInvariant)]
Flow.Launcher.Test/Plugins/CalculatorTest.cs (2)

23-27: Make tests locale‑deterministic (set DecimalSeparator and precision).

CI culture may not be dot-decimal; explicit settings avoid flaky expectations like “0.0000000000”.

Apply:

-            settingField.SetValue(_plugin, new Settings
-            {
-                ShowErrorMessage = false // Make sure we return the empty results when error occurs
-            });
+            settingField.SetValue(_plugin, new Settings
+            {
+                ShowErrorMessage = false,                  // return empty results on error
+                DecimalSeparator = DecimalSeparator.Dot,   // deterministic output (matches expected test strings)
+                MaxDecimalPlaces = 10
+            });

31-38: Avoid static engine leakage across fixtures.

Since MagesEngine is static, consider resetting it after the fixture to prevent cross‑test interference.

Example:

@@
         public CalculatorPluginTest()
         {
@@
         }
+
+        [OneTimeTearDown]
+        public void TearDown()
+        {
+            var engineField = typeof(Main).GetField("MagesEngine", BindingFlags.NonPublic | BindingFlags.Static);
+            engineField?.SetValue(null, null);
+        }
Plugins/Flow.Launcher.Plugin.Calculator/Main.cs (3)

119-127: Handle Infinity explicitly (parity with NaN).

Division by zero may yield ±Infinity; treat like NaN for clearer UX instead of relying on Convert.ToDecimal throwing.

Apply:

-                if (result.ToString() == "NaN")
-                {
-                    result = Localize.flowlauncher_plugin_calculator_not_a_number();
-                }
+                if ((result is double d && (double.IsNaN(d) || double.IsInfinity(d))) ||
+                    (result is float f && (float.IsNaN(f) || float.IsInfinity(f))) ||
+                    string.Equals(result.ToString(), "NaN", StringComparison.Ordinal))
+                {
+                    result = Localize.flowlauncher_plugin_calculator_not_a_number();
+                }

355-377: Format with real culture group sizes (hi‑IN, etc.) while preserving fractional output.

Current regex assumes 3‑digit groups. Use NumberFormatInfo to format the integer part, keep fractional part from the rounded decimal so tests like “sin(pi)” still show 10 zeros.

Apply:

-        private string FormatResult(decimal roundedResult)
-        {
-            string decimalSeparator = GetDecimalSeparator();
-            string groupSeparator = GetGroupSeparator(decimalSeparator);
-
-            string resultStr = roundedResult.ToString(CultureInfo.InvariantCulture);
-
-            string[] parts = resultStr.Split('.');
-            string integerPart = parts[0];
-            string fractionalPart = parts.Length > 1 ? parts[1] : string.Empty;
-
-            if (integerPart.Length > 3)
-            {
-                integerPart = ThousandGroupRegex.Replace(integerPart, groupSeparator);
-            }
-
-            if (!string.IsNullOrEmpty(fractionalPart))
-            {
-                return integerPart + decimalSeparator + fractionalPart;
-            }
-
-            return integerPart;
-        }
+        private string FormatResult(decimal roundedResult)
+        {
+            var decSep = GetDecimalSeparator();
+            var grpSep = GetGroupSeparator(decSep);
+
+            // Preserve fractional digits exactly as produced by the rounded decimal
+            // (e.g., "0.0000000000" for sin(pi)).
+            var invariant = roundedResult.ToString(CultureInfo.InvariantCulture);
+            var parts = invariant.Split('.');
+            var fractional = parts.Length > 1 ? parts[1] : string.Empty;
+
+            // Format the integer part with the culture’s real group sizes
+            var nfi = (NumberFormatInfo)CultureInfo.CurrentCulture.NumberFormat.Clone();
+            nfi.NumberDecimalSeparator = decSep;
+            nfi.NumberGroupSeparator = grpSep;
+            var intGrouped = decimal.Truncate(roundedResult).ToString("N0", nfi);
+
+            return string.IsNullOrEmpty(fractional)
+                ? intGrouped
+                : intGrouped + decSep + fractional;
+        }

239-241: Comment nit: “log(x) -> log10(x)” is common log, not natural.

Adjust comment to avoid confusion.

Apply:

-            // log(x) -> log10(x)   (natural log)
+            // log(x) -> log10(x)   (common/base‑10 log)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 684fafd and e10b925.

📒 Files selected for processing (4)
  • Flow.Launcher.Test/Flow.Launcher.Test.csproj (1 hunks)
  • Flow.Launcher.Test/Plugins/CalculatorTest.cs (1 hunks)
  • Plugins/Flow.Launcher.Plugin.Calculator/Main.cs (5 hunks)
  • Plugins/Flow.Launcher.Plugin.Calculator/MainRegexHelper.cs (1 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: dcog989
PR: Flow-Launcher/Flow.Launcher#3971
File: Plugins/Flow.Launcher.Plugin.Calculator/Main.cs:67-67
Timestamp: 2025-09-13T12:50:05.270Z
Learning: In Flow.Launcher Calculator plugin, NormalizeNumber must validate thousand separator usage before stripping them. If a comma-separated token doesn't follow proper thousand separator rules (3 digits between separators), it should be left unmodified to preserve function arguments like in "min(2,3)".
Learnt from: dcog989
PR: Flow-Launcher/Flow.Launcher#3971
File: Plugins/Flow.Launcher.Plugin.Calculator/Main.cs:67-67
Timestamp: 2025-09-13T12:50:05.271Z
Learning: NumberRegex pattern `-?[\d\.,]+` in Flow.Launcher Calculator will match function arguments like `2,3` in `min(2,3)`. The NormalizeNumber validation that checks for proper 3-digit thousand separator grouping is essential to avoid breaking function calls by incorrectly stripping commas from function arguments.
📚 Learning: 2024-10-08T15:52:58.573Z
Learnt from: taooceros
PR: Flow-Launcher/Flow.Launcher#2616
File: Flow.Launcher/Flow.Launcher.csproj:7-7
Timestamp: 2024-10-08T15:52:58.573Z
Learning: In the Flow Launcher project, the version number in the `Flow.Launcher.csproj` file is dynamically updated during the CI/CD process.

Applied to files:

  • Flow.Launcher.Test/Flow.Launcher.Test.csproj
📚 Learning: 2025-09-13T12:50:05.271Z
Learnt from: dcog989
PR: Flow-Launcher/Flow.Launcher#3971
File: Plugins/Flow.Launcher.Plugin.Calculator/Main.cs:67-67
Timestamp: 2025-09-13T12:50:05.271Z
Learning: NumberRegex pattern `-?[\d\.,]+` in Flow.Launcher Calculator will match function arguments like `2,3` in `min(2,3)`. The NormalizeNumber validation that checks for proper 3-digit thousand separator grouping is essential to avoid breaking function calls by incorrectly stripping commas from function arguments.

Applied to files:

  • Plugins/Flow.Launcher.Plugin.Calculator/Main.cs
  • Plugins/Flow.Launcher.Plugin.Calculator/MainRegexHelper.cs
📚 Learning: 2025-09-13T12:50:05.270Z
Learnt from: dcog989
PR: Flow-Launcher/Flow.Launcher#3971
File: Plugins/Flow.Launcher.Plugin.Calculator/Main.cs:67-67
Timestamp: 2025-09-13T12:50:05.270Z
Learning: In Flow.Launcher Calculator plugin, NormalizeNumber must validate thousand separator usage before stripping them. If a comma-separated token doesn't follow proper thousand separator rules (3 digits between separators), it should be left unmodified to preserve function arguments like in "min(2,3)".

Applied to files:

  • Plugins/Flow.Launcher.Plugin.Calculator/Main.cs
  • Plugins/Flow.Launcher.Plugin.Calculator/MainRegexHelper.cs
🧬 Code graph analysis (2)
Flow.Launcher.Test/Plugins/CalculatorTest.cs (1)
Plugins/Flow.Launcher.Plugin.Calculator/Main.cs (1)
  • Main (14-431)
Plugins/Flow.Launcher.Plugin.Calculator/Main.cs (1)
Plugins/Flow.Launcher.Plugin.Calculator/MainRegexHelper.cs (1)
  • MainRegexHelper (5-24)
🪛 GitHub Check: Check Spelling
Flow.Launcher.Test/Plugins/CalculatorTest.cs

[warning] 6-6:
NUnit is not a recognized word. (unrecognized-spelling)


[warning] 7-7:
NUnit is not a recognized word. (unrecognized-spelling)

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

[warning] 141-141:
nullability is not a recognized word. (unrecognized-spelling)

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

[warning] 22-22:
isinfty is not a recognized word. (unrecognized-spelling)


[warning] 22-22:
isprime is not a recognized word. (unrecognized-spelling)


[warning] 22-22:
isint is not a recognized word. (unrecognized-spelling)


[warning] 19-19:
bln is not a recognized word. (unrecognized-spelling)


[warning] 13-13:
bpow is not a recognized word. (unrecognized-spelling)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: gitStream.cm
  • GitHub Check: build
🔇 Additional comments (3)
Plugins/Flow.Launcher.Plugin.Calculator/MainRegexHelper.cs (1)

13-23: Pow/log/ln/function patterns look solid.

Balanced‑group handling + CultureInvariant is appropriate here.

Flow.Launcher.Test/Flow.Launcher.Test.csproj (1)

42-42: Add Calculator plugin reference — LGTM.

Helps ensure tests compile against the real plugin.

Plugins/Flow.Launcher.Plugin.Calculator/Main.cs (1)

62-66: Function lower‑casing + early locale capture — LGTM.

Good to normalize function names and compute separators once per query.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (4)
Flow.Launcher.Test/Plugins/CalculatorTest.cs (4)

11-13: Guard against parallel test interference with static engine

The test mutates a static field (MagesEngine). Mark the fixture non‑parallel to avoid cross‑test races.

Apply:

-    [TestFixture]
+    [TestFixture]
+    [NonParallelizable]
     public class CalculatorPluginTest

1-8: Pin test culture to Invariant for reproducible formatting

FormatResult and grouping can vary by OS culture. Pinning Invariant prevents surprises on CI.

Apply:

-using NUnit.Framework.Legacy;
+using NUnit.Framework.Legacy;
+using System.Globalization;
@@
     public class CalculatorPluginTest
     {
@@
         public CalculatorPluginTest()
         {
@@
         }
+
+        [OneTimeSetUp]
+        public void OneTimeSetupCulture()
+        {
+            CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture;
+            CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.InvariantCulture;
+        }

74-77: Swap expected/actual in assertion

ClassicAssert.AreEqual(expected, actual) — current order is reversed, leading to confusing failure messages.

Apply:

-            ClassicAssert.AreEqual(GetCalculationResult(expression), result);
+            ClassicAssert.AreEqual(result, GetCalculationResult(expression));

40-69: Add regression tests for comma as function-argument separator vs decimal/grouping

To lock in the “strict mode” behavior (don’t strip commas inside functions) and the thousand‑group validation, add a couple of focused tests.

Suggested additions:

  • Ensure commas inside functions are preserved:

    • [TestCase(@"min(2,3)", "2")]
    • [TestCase(@"pow(min(2,3),3)", "8")]
  • Ensure invalid grouping isn’t stripped in strict mode (from our learning):

    • With any function present, tokens like "1000,000" should remain unchanged and not be misparsed as 1000000.
      Would you like me to open a follow‑up PR with these tests?
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e10b925 and 552b654.

📒 Files selected for processing (1)
  • Flow.Launcher.Test/Plugins/CalculatorTest.cs (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: dcog989
PR: Flow-Launcher/Flow.Launcher#3971
File: Plugins/Flow.Launcher.Plugin.Calculator/Main.cs:67-67
Timestamp: 2025-09-13T12:50:05.270Z
Learning: In Flow.Launcher Calculator plugin, NormalizeNumber must validate thousand separator usage before stripping them. If a comma-separated token doesn't follow proper thousand separator rules (3 digits between separators), it should be left unmodified to preserve function arguments like in "min(2,3)".
Learnt from: dcog989
PR: Flow-Launcher/Flow.Launcher#3971
File: Plugins/Flow.Launcher.Plugin.Calculator/Main.cs:67-67
Timestamp: 2025-09-13T12:50:05.271Z
Learning: NumberRegex pattern `-?[\d\.,]+` in Flow.Launcher Calculator will match function arguments like `2,3` in `min(2,3)`. The NormalizeNumber validation that checks for proper 3-digit thousand separator grouping is essential to avoid breaking function calls by incorrectly stripping commas from function arguments.
🧬 Code graph analysis (1)
Flow.Launcher.Test/Plugins/CalculatorTest.cs (1)
Plugins/Flow.Launcher.Plugin.Calculator/Main.cs (1)
  • Main (14-431)
🪛 GitHub Check: Check Spelling
Flow.Launcher.Test/Plugins/CalculatorTest.cs

[warning] 6-6:
NUnit is not a recognized word. (unrecognized-spelling)


[warning] 7-7:
NUnit is not a recognized word. (unrecognized-spelling)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: gitStream.cm
🔇 Additional comments (2)
Flow.Launcher.Test/Plugins/CalculatorTest.cs (2)

31-37: Good: inject custom Mages engine and define e

Setting e in the engine scope aligns with the plugin’s Init path and stabilizes tests involving ln(e).


52-63: Good coverage for pow/log/ln workarounds and common functions

Cases for pow, log, ln, and multi‑arg functions exercise the recent parser/rewriter changes.

@jjw24
Copy link
Member

jjw24 commented Sep 16, 2025

@Jack251970 I haven't had the chance to look at the code closely, but looks like you guys have done some good changes, happy for you to approve + merge when good.

@Jack251970
Copy link
Member

@Jack251970 I haven't had the chance to look at the code closely, but looks like you guys have done some good changes, happy for you to approve + merge when good.

Yeah, I will do if this PR is fully tested

@dcog989
Copy link
Contributor Author

dcog989 commented Sep 16, 2025

if you want to add any other unit tests like divider , & . tests?

I will add that to my list!

I will do if this PR is fully tested

I feel the code now is in a better condition than current production. Plus I think we cannot cover every variable in unit testing, for every language pack, and every possible customisation a user might make and how they might try to use the calculator???

So, LGTM and then react to issues if they are submitted?

Copy link
Member

@Jack251970 Jack251970 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your contribution!

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (3)
Flow.Launcher.Test/Plugins/CalculatorTest.cs (3)

60-60: Align expected output for sin(pi)

FormatResult does not pad trailing zeros; with rounding it returns "0".

-[TestCase(@"sin(pi)", "0.0000000000")]
+[TestCase(@"sin(pi)", "0")]

85-89: Fix compile error: resolve Plugin.Query type

Plugin alias isn’t defined; this won’t compile. Use fully-qualified type or add an alias.

Apply one of:

-            var results = _plugin.Query(new Plugin.Query()
+            var results = _plugin.Query(new Flow.Launcher.Plugin.Query()

or add using Plugin = Flow.Launcher.Plugin; at the top.


15-20: Make tests locale-stable (freeze culture and decimal separator)

Current tests depend on machine culture (UseSystemLocale). Set a deterministic culture and decimal separator to avoid CI flakiness.

Apply:

+using System.Globalization;
@@
-            DecimalSeparator = DecimalSeparator.UseSystemLocale,
+            DecimalSeparator = DecimalSeparator.Dot,
             MaxDecimalPlaces = 10,
             ShowErrorMessage = false // Make sure we return the empty results when error occurs
         };
@@
-        public CalculatorPluginTest()
+        private CultureInfo? _originalCulture;
+        private CultureInfo? _originalUICulture;
+
+        public CalculatorPluginTest()
         {
             _plugin = new Main();
@@
             engineField.SetValue(null, _engine);
         }
+
+        [OneTimeSetUp]
+        public void OneTimeSetUp()
+        {
+            _originalCulture = CultureInfo.CurrentCulture;
+            _originalUICulture = CultureInfo.CurrentUICulture;
+            CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
+            CultureInfo.CurrentUICulture = CultureInfo.InvariantCulture;
+        }
+
+        [OneTimeTearDown]
+        public void OneTimeTearDown()
+        {
+            if (_originalCulture != null) CultureInfo.CurrentCulture = _originalCulture;
+            if (_originalUICulture != null) CultureInfo.CurrentUICulture = _originalUICulture;
+        }

To verify determinism, run the test suite twice while temporarily setting a different process culture (e.g., de-DE) and confirm identical outputs.

🧹 Nitpick comments (2)
Flow.Launcher.Test/Plugins/CalculatorTest.cs (2)

29-42: Optionally avoid reflection and static state leakage

Tests poke private fields and a static engine. Consider adding an injection hook (ctor overload or internal setter with InternalsVisibleTo) and restoring the previous static engine in teardown to prevent cross-fixture bleed.

If preferred, I can draft a small refactor in the plugin to expose safe test-only injection points.


55-67: Add two edge-case tests (nested pow; comma inside functions)

Strengthen regressions around rewrites and “comma-as-arg” handling.

         // Functions
         [TestCase(@"pow(2,3)", "8")]
+        [TestCase(@"pow(2,pow(2,3))", "256")]
         [TestCase(@"min(1,-1,-2)", "-2")]
+        [TestCase(@"min(2,3)", "2")]
         [TestCase(@"max(1,-1,-2)", "1")]
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 552b654 and 8321e40.

📒 Files selected for processing (1)
  • Flow.Launcher.Test/Plugins/CalculatorTest.cs (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: dcog989
PR: Flow-Launcher/Flow.Launcher#3971
File: Plugins/Flow.Launcher.Plugin.Calculator/Main.cs:67-67
Timestamp: 2025-09-13T12:50:05.270Z
Learning: In Flow.Launcher Calculator plugin, NormalizeNumber must validate thousand separator usage before stripping them. If a comma-separated token doesn't follow proper thousand separator rules (3 digits between separators), it should be left unmodified to preserve function arguments like in "min(2,3)".
Learnt from: dcog989
PR: Flow-Launcher/Flow.Launcher#3971
File: Plugins/Flow.Launcher.Plugin.Calculator/Main.cs:67-67
Timestamp: 2025-09-13T12:50:05.271Z
Learning: NumberRegex pattern `-?[\d\.,]+` in Flow.Launcher Calculator will match function arguments like `2,3` in `min(2,3)`. The NormalizeNumber validation that checks for proper 3-digit thousand separator grouping is essential to avoid breaking function calls by incorrectly stripping commas from function arguments.
📚 Learning: 2025-09-13T12:50:05.270Z
Learnt from: dcog989
PR: Flow-Launcher/Flow.Launcher#3971
File: Plugins/Flow.Launcher.Plugin.Calculator/Main.cs:67-67
Timestamp: 2025-09-13T12:50:05.270Z
Learning: In Flow.Launcher Calculator plugin, NormalizeNumber must validate thousand separator usage before stripping them. If a comma-separated token doesn't follow proper thousand separator rules (3 digits between separators), it should be left unmodified to preserve function arguments like in "min(2,3)".

Applied to files:

  • Flow.Launcher.Test/Plugins/CalculatorTest.cs
🧬 Code graph analysis (1)
Flow.Launcher.Test/Plugins/CalculatorTest.cs (1)
Plugins/Flow.Launcher.Plugin.Calculator/Main.cs (1)
  • Main (14-431)
🪛 GitHub Check: Check Spelling
Flow.Launcher.Test/Plugins/CalculatorTest.cs

[warning] 6-6:
NUnit is not a recognized word. (unrecognized-spelling)


[warning] 7-7:
NUnit is not a recognized word. (unrecognized-spelling)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (2)
Flow.Launcher.Test/Plugins/CalculatorTest.cs (2)

56-67: Good coverage for function parsing and Mages workarounds

These cases validate pow/log/ln rewrites and multi-arg functions. Nice.


44-55: Core arithmetic and parentheses cases look solid

Baseline scenarios and combined expressions are well covered.

Also applies to: 69-72

@Jack251970 Jack251970 merged commit 8bac530 into Flow-Launcher:dev Sep 16, 2025
5 checks passed
@Jack251970 Jack251970 linked an issue Sep 16, 2025 that may be closed by this pull request
3 tasks
@dcog989
Copy link
Contributor Author

dcog989 commented Sep 16, 2025

Thanks for your contribution!

My pleasure - even if it made my brain hurt at times!! :)

@dcog989 dcog989 deleted the calculator-min-fix branch September 16, 2025 14:14
TBM13 added a commit to TBM13/Flow.Launcher that referenced this pull request Sep 17, 2025
@jjw24 jjw24 modified the milestones: 2.1.0, 2.0.1 Sep 21, 2025
jjw24 pushed a commit that referenced this pull request Sep 21, 2025
Backout 'smart' digit separation & Fix Mages functions for Calculator plugin
@jjw24 jjw24 mentioned this pull request Sep 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
4 participants