From 506587a9510d94338028613fdab1e8e3827e4ca4 Mon Sep 17 00:00:00 2001 From: jannisCode Date: Mon, 14 Oct 2024 15:04:49 +0200 Subject: [PATCH] Show multiline error message for invalid regex in searches when possible When using a regular expression for a search, if there is enough information as to where the invalid character of the expression is then a multiline error message will be shown. The first line contains the regular expression and the second line contains an arrow (^) that points to the offending character. --- .../eclipse/ui/internal/SearchDecoration.java | 54 +++++++++++++++---- 1 file changed, 44 insertions(+), 10 deletions(-) diff --git a/bundles/org.eclipse.ui/src/org/eclipse/ui/internal/SearchDecoration.java b/bundles/org.eclipse.ui/src/org/eclipse/ui/internal/SearchDecoration.java index 47e4e148356..da709d7f8c4 100644 --- a/bundles/org.eclipse.ui/src/org/eclipse/ui/internal/SearchDecoration.java +++ b/bundles/org.eclipse.ui/src/org/eclipse/ui/internal/SearchDecoration.java @@ -19,7 +19,9 @@ import org.eclipse.jface.fieldassist.ControlDecoration; import org.eclipse.jface.fieldassist.FieldDecorationRegistry; +import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Control; /** * This class contains methods to validate and decorate search fields. @@ -41,11 +43,10 @@ private SearchDecoration() { * the validation. */ public static boolean validateRegex(String regex, ControlDecoration targetDecoration) { - String errorMessage = getValidationError(regex); + String errorMessage = getValidationError(regex, targetDecoration.getControl()); if (errorMessage.isEmpty()) { targetDecoration.hide(); return true; - } Image decorationImage = FieldDecorationRegistry.getDefault() @@ -62,21 +63,54 @@ public static boolean validateRegex(String regex, ControlDecoration targetDecora * @return The appropriate error message if the regex is invalid or an empty * string if the regex is valid. */ - private static String getValidationError(String regex) { + private static String getValidationError(String regex, Control targetControl) { try { Pattern.compile(regex); return ""; //$NON-NLS-1$ } catch (PatternSyntaxException e) { - String message = e.getLocalizedMessage(); + return buildValidationErrorString(e, targetControl); + } + } + + private static String buildValidationErrorString(PatternSyntaxException e, Control targetControl) { + String description = e.getDescription(); + int errorIndex = e.getIndex(); + + if (errorIndex == -1) { + return description; + } + + GC gc = new GC(targetControl); + String pattern = e.getPattern(); - // Only preserve the first line of the original error message. - int i = 0; - while (i < message.length() && "\n\r".indexOf(message.charAt(i)) == -1) { //$NON-NLS-1$ - i++; - } + StringBuilder validationErrorMessage = new StringBuilder(); - return message.substring(0, i); + validationErrorMessage.append(description); + validationErrorMessage.append(" at index ").append(errorIndex).append(System.lineSeparator()); //$NON-NLS-1$ + validationErrorMessage.append(pattern).append(System.lineSeparator()); + + String stringToIndexString = pattern.substring(0, errorIndex + 1); + String hairSpace = "\u200A"; //$NON-NLS-1$ + int hairSpaceWidth = gc.stringExtent(hairSpace).x; + + int stringToIndex = gc.stringExtent(stringToIndexString).x; + String lastCharacter = stringToIndexString.substring(stringToIndexString.length() - 1); + + int widthLastChar = gc.stringExtent(lastCharacter).x; + int upWidth = gc.stringExtent("^").x; //$NON-NLS-1$ + + double howFar = stringToIndex - widthLastChar / 2 - upWidth / 2; + int currentWidth = 0; + + while (currentWidth < howFar) { + currentWidth += hairSpaceWidth; + validationErrorMessage.append(hairSpace); } + + validationErrorMessage.append("^"); //$NON-NLS-1$ + gc.dispose(); + + return validationErrorMessage.toString(); } } \ No newline at end of file