Refactor `Menu` class: Use static imports for `Math` methods, optimize code in `filte...
authorSvjatoslav Agejenko <svjatoslav@svjatoslav.eu>
Sun, 25 Jan 2026 12:20:27 +0000 (14:20 +0200)
committerSvjatoslav Agejenko <svjatoslav@svjatoslav.eu>
Sun, 25 Jan 2026 12:20:27 +0000 (14:20 +0200)
src/main/java/eu/svjatoslav/commons/cli_helper/Menu.java

index 73356fb..ac68d82 100644 (file)
@@ -7,6 +7,9 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
+import static java.lang.Math.max;
+import static java.lang.Math.min;
+
 /**
  * Prompts the user to select an option from a list using fuzzy matching and cursor keys.
  * <p>
@@ -22,6 +25,9 @@ import java.util.List;
  */
 public class Menu {
 
+    /**
+     * Presents an interactive menu; returns selection or null
+     */
     public static String askSelection(String prompt, List<String> options) throws IOException {
         if (options == null || options.isEmpty()) {
             throw new IllegalArgumentException("Options list cannot be empty");
@@ -37,15 +43,14 @@ public class Menu {
 
             while (true) {
 
-                List<String> filtered = filterOptions(options, filterString);
+                List<String> filteredOptions = filterOptions(options, filterString);
 
                 // Clamp selected to valid range
-                selectedIndex = Math.max(0, Math.min(selectedIndex, filtered.size() - 1));
+                selectedIndex = max(0, min(selectedIndex, filteredOptions.size() - 1));
 
-                updateDisplay(prompt, filtered, selectedIndex, filterString, lastTotalLines);
-                lastTotalLines = getCurrentTotalLines(options, filterString);
+                lastTotalLines = updateDisplay(prompt, filteredOptions, selectedIndex, filterString, lastTotalLines);
 
-                int c = System.in.read();
+                int c = System.in.read(); // read a single character from user input
 
                 // Handle Escape key or arrow keys
                 if (c == 27) { // ESC
@@ -67,10 +72,10 @@ public class Menu {
 
                 // Handle Enter key
                 else if (c == 13 || c == 10) {
-                    if (filtered.isEmpty()) {
+                    if (filteredOptions.isEmpty()) {
                         return null;
                     }
-                    return filtered.get(selectedIndex);
+                    return filteredOptions.get(selectedIndex);
                 }
 
                 // Handle Backspace (8 or 127)
@@ -140,17 +145,15 @@ public class Menu {
      * @return filtered list of options that match the fuzzy pattern
      */
     private static List<String> filterOptions(List<String> options, String filter) {
-        if (filter == null || filter.isEmpty()) {
-            return new ArrayList<>(options);
-        }
+        if (filter == null || filter.isEmpty()) return new ArrayList<>(options);
 
-        List<String> filtered = new ArrayList<>();
-        for (String option : options) {
-            if (matchesFuzzy(filter, option)) {
-                filtered.add(option);
-            }
-        }
-        return filtered;
+        List<String> result = new ArrayList<>();
+
+        for (String option : options)
+            if (matchesFuzzy(filter, option))
+                result.add(option);
+
+        return result;
     }
 
     /**
@@ -165,58 +168,46 @@ public class Menu {
         filter = filter.toLowerCase();
         option = option.toLowerCase();
         int filterIndex = 0;
-        for (char c : option.toCharArray()) {
-            if (filterIndex < filter.length() && c == filter.charAt(filterIndex)) {
+
+        for (char c : option.toCharArray())
+            if (filterIndex < filter.length() && c == filter.charAt(filterIndex))
                 filterIndex++;
-            }
-        }
+
         return filterIndex == filter.length();
     }
 
-
     /**
-     * Updates the display based on the current state and returns the adjusted selected index.
+     * Updates the display based on the current state.
+     * @return the total number of lines in the display
      */
-    private static void updateDisplay(String prompt, List<String> filtered, int selected, String filter, int lastTotalLines) {
+    private static int updateDisplay(String prompt, List<String> filteredOptions, int selectedIndex, String filterString, int lastTotalLines) {
 
-        if (filtered.isEmpty()) {
-            filtered = Collections.singletonList("No matches found");
-        }
+        if (filteredOptions.isEmpty()) filteredOptions = Collections.singletonList("No matches found");
 
-        // ────────────────────────────────────────────────
-        // 1. Position cursor at the START of our display area
-        // ────────────────────────────────────────────────
-        if (lastTotalLines > 0) {
+        // Position cursor at the START of the display area
+        if (lastTotalLines > 0)
             System.out.print("\033[" + lastTotalLines + "A");   // move cursor up to prompt line
-        }
+
         System.out.print("\r");                                 // go to column 1 (just in case)
 
-        // ────────────────────────────────────────────────
-        // 2. Clear from here downward (removes old leftover lines perfectly)
-        // ────────────────────────────────────────────────
+        // Clear from here downward (removes old leftover lines perfectly)
         System.out.print("\033[J");                             // ESC [ J = clear from cursor to end of screen
 
-        // ────────────────────────────────────────────────
-        // 3. Print fresh content
-        // ────────────────────────────────────────────────
+        // Print fresh content:
         // Prompt + current filter (on one line)
-        System.out.println(prompt + ": " + filter);
+        System.out.println(prompt + ": " + filterString);
 
         // Menu items
-        for (int i = 0; i < filtered.size(); i++) {
-            String line = (i == selected ? "> " : "  ") + filtered.get(i);
+        for (int i = 0; i < filteredOptions.size(); i++) {
+            String line = (i == selectedIndex ? "> " : "  ") + filteredOptions.get(i);
             System.out.print("\r");
             System.out.println(line);
         }
         System.out.print("\r");
 
         System.out.flush();  // ensure immediate display
-    }
 
-    private static int getCurrentTotalLines(List<String> options, String filter) {
-        List<String> filtered = filterOptions(options, filter);
-        int menuLines = filtered.isEmpty() ? 1 : filtered.size();
-        return 1 + menuLines;  // 1 for prompt line + menu lines
+        return filteredOptions.size() + 1;  // 1 for prompt line + menu lines
     }
 
 }