Rewrite QuoteHelper to integrate French quotes logics. Also reallow QuoteChars not followed by whitespace as indicated in XEP-0393.

Millesimus created

Change summary

src/main/java/eu/siacs/conversations/ui/util/QuoteHelper.java | 61 ++++
src/main/java/eu/siacs/conversations/utils/UIHelper.java      | 35 --
2 files changed, 59 insertions(+), 37 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/ui/util/QuoteHelper.java 🔗

@@ -5,8 +5,32 @@ import eu.siacs.conversations.utils.UIHelper;
 
 public class QuoteHelper {
 
+
+    public static final char QUOTE_CHAR = '>';
+    public static final char QUOTE_END_CHAR = '<'; // used for one check, not for actual quoting
+    public static final char QUOTE_ALT_CHAR = '»';
+    public static final char QUOTE_ALT_END_CHAR = '«';
+
     public static boolean isPositionQuoteCharacter(CharSequence body, int pos){
-        return body.charAt(pos) == '>';
+        // second part of logical check actually goes against the logic indicated in the method name, since it also checks for context
+        // but it's very useful
+        return body.charAt(pos) == QUOTE_CHAR || isPositionAltQuoteStart(body, pos);
+    }
+
+    public static boolean isPositionQuoteEndCharacter(CharSequence body, int pos){
+        return body.charAt(pos) == QUOTE_END_CHAR;
+    }
+
+    public static boolean isPositionAltQuoteCharacter (CharSequence body, int pos){
+        return body.charAt(pos) == QUOTE_ALT_CHAR;
+    }
+
+    public static boolean isPositionAltQuoteEndCharacter(CharSequence body, int pos){
+        return body.charAt(pos) == QUOTE_ALT_END_CHAR;
+    }
+
+    public static boolean isPositionAltQuoteStart(CharSequence body, int pos){
+        return isPositionAltQuoteCharacter(body, pos) && !isPositionFollowedByAltQuoteEnd(body, pos);
     }
 
     public static boolean isPositionFollowedByQuoteChar(CharSequence body, int pos) {
@@ -19,10 +43,10 @@ public class QuoteHelper {
     }
 
     public static boolean isPositionQuoteStart (CharSequence body, int pos){
-        return isPositionQuoteCharacter(body, pos)
+        return (isPositionQuoteCharacter(body, pos)
                 && isPositionPrecededByPrequote(body, pos)
-                && (UIHelper.isPositionFollowedByWhitespace(body, pos)
-                    || isPositionFollowedByQuoteChar(body, pos));
+                && (UIHelper.isPositionFollowedByQuoteableCharacter(body, pos)
+                    || isPositionFollowedByQuoteChar(body, pos)));
     }
 
     public static boolean bodyContainsQuoteStart (CharSequence body){
@@ -34,6 +58,24 @@ public class QuoteHelper {
         return false;
     }
 
+    public static boolean isPositionFollowedByAltQuoteEnd(CharSequence body, int pos) {
+        if (body.length() <= pos + 1 || Character.isWhitespace(body.charAt(pos + 1))) {
+            return false;
+        }
+        boolean previousWasWhitespace = false;
+        for (int i = pos + 1; i < body.length(); i++) {
+            char c = body.charAt(i);
+            if (c == '\n' || isPositionAltQuoteCharacter(body, i)) {
+                return false;
+            } else if (isPositionAltQuoteEndCharacter(body, i) && !previousWasWhitespace) {
+                return true;
+            } else {
+                previousWasWhitespace = Character.isWhitespace(c);
+            }
+        }
+        return false;
+    }
+
     public static boolean isNestedTooDeeply (CharSequence line){
         if (isPositionQuoteCharacter(line, 0)) {
             int nestingDepth = 1;
@@ -48,4 +90,13 @@ public class QuoteHelper {
         }
         return false;
     }
-}
+
+    public static String replaceAltQuoteCharsInText(String text){
+        for (int i = 0; i < text.length(); i++){
+            if (isPositionAltQuoteStart(text, i)){
+                text = text.substring(0, i) + QUOTE_CHAR + text.substring(i + 1);
+            }
+        }
+        return text;
+    }
+}

src/main/java/eu/siacs/conversations/utils/UIHelper.java 🔗

@@ -329,7 +329,7 @@ public class UIHelper {
                             continue;
                         }
                         char first = l.charAt(0);
-                        if ((!QuoteHelper.isPositionQuoteStart(l, 0)) && first != '\u00bb') {
+                        if ((!QuoteHelper.isPositionQuoteStart(l, 0))) {
                             CharSequence line = CharSequenceUtils.trim(l);
                             if (line.length() == 0) {
                                 continue;
@@ -373,14 +373,6 @@ public class UIHelper {
         return input.length() > 256 ? StylingHelper.subSequence(input, 0, 256) : input;
     }
 
-    public static boolean isPositionFollowedByWhitespace(CharSequence body, int pos){
-        return Character.isWhitespace(body.charAt(pos + 1));
-    }
-
-    public static boolean isPositionPrecededByWhitespace(CharSequence body, int pos){
-        return Character.isWhitespace(body.charAt(pos -1 ));
-    }
-
     public static boolean isPositionPrecededByBodyStart(CharSequence body, int pos){
         // true if not a single linebreak before current position
         for (int i = pos - 1; i >= 0; i--){
@@ -395,10 +387,7 @@ public class UIHelper {
         if (isPositionPrecededByBodyStart(body, pos)){
             return true;
         }
-        if (body.charAt(pos - 1) == '\n'){
-            return true;
-        }
-        return false;
+        return body.charAt(pos - 1) == '\n';
     }
 
     public static boolean isPositionFollowedByQuoteableCharacter(CharSequence body, int pos) {
@@ -442,31 +431,13 @@ public class UIHelper {
             final char c = body.charAt(i);
             if (Character.isWhitespace(c)) {
                 return false;
-            } else if (c == '<' || c == '>') {
+            } else if (QuoteHelper.isPositionQuoteCharacter(body, pos) || QuoteHelper.isPositionQuoteEndCharacter(body, pos)) {
                 return body.length() == i + 1 || Character.isWhitespace(body.charAt(i + 1));
             }
         }
         return false;
     }
 
-    public static boolean isPositionFollowedByQuote(CharSequence body, int pos) {
-        if (body.length() <= pos + 1 || Character.isWhitespace(body.charAt(pos + 1))) {
-            return false;
-        }
-        boolean previousWasWhitespace = false;
-        for (int i = pos + 1; i < body.length(); i++) {
-            char c = body.charAt(i);
-            if (c == '\n' || c == '»') {
-                return false;
-            } else if (c == '«' && !previousWasWhitespace) {
-                return true;
-            } else {
-                previousWasWhitespace = Character.isWhitespace(c);
-            }
-        }
-        return false;
-    }
-
     public static String getDisplayName(MucOptions.User user) {
         Contact contact = user.getContact();
         if (contact != null) {