src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedStringBuilder.java
changeset 58903 eeb1c0da2126
parent 52938 5ff7480c9e28
equal deleted inserted replaced
58902:197238c30630 58903:eeb1c0da2126
     2  * Copyright (c) 2002-2018, the original author or authors.
     2  * Copyright (c) 2002-2018, the original author or authors.
     3  *
     3  *
     4  * This software is distributable under the BSD license. See the terms of the
     4  * This software is distributable under the BSD license. See the terms of the
     5  * BSD license in the documentation provided with this software.
     5  * BSD license in the documentation provided with this software.
     6  *
     6  *
     7  * http://www.opensource.org/licenses/bsd-license.php
     7  * https://opensource.org/licenses/BSD-3-Clause
     8  */
     8  */
     9 package jdk.internal.org.jline.utils;
     9 package jdk.internal.org.jline.utils;
    10 
    10 
       
    11 import java.util.ArrayList;
    11 import java.util.Arrays;
    12 import java.util.Arrays;
       
    13 import java.util.List;
    12 import java.util.function.Consumer;
    14 import java.util.function.Consumer;
    13 import java.util.function.Function;
    15 import java.util.function.Function;
    14 import java.util.regex.Matcher;
    16 import java.util.regex.Matcher;
    15 import java.util.regex.Pattern;
    17 import java.util.regex.Pattern;
    16 
    18 
    22 public class AttributedStringBuilder extends AttributedCharSequence implements Appendable {
    24 public class AttributedStringBuilder extends AttributedCharSequence implements Appendable {
    23 
    25 
    24     private char[] buffer;
    26     private char[] buffer;
    25     private int[] style;
    27     private int[] style;
    26     private int length;
    28     private int length;
    27     private int tabs = 0;
    29     private TabStops tabs = new TabStops(0);
    28     private int lastLineLength = 0;
    30     private int lastLineLength = 0;
    29     private AttributedStyle current = AttributedStyle.DEFAULT;
    31     private AttributedStyle current = AttributedStyle.DEFAULT;
    30 
    32 
    31     public static AttributedString append(CharSequence... strings) {
    33     public static AttributedString append(CharSequence... strings) {
    32         AttributedStringBuilder sb = new AttributedStringBuilder();
    34         AttributedStringBuilder sb = new AttributedStringBuilder();
   149     public AttributedStringBuilder append(AttributedCharSequence str, int start, int end) {
   151     public AttributedStringBuilder append(AttributedCharSequence str, int start, int end) {
   150         ensureCapacity(length + end - start);
   152         ensureCapacity(length + end - start);
   151         for (int i = start; i < end; i++) {
   153         for (int i = start; i < end; i++) {
   152             char c = str.charAt(i);
   154             char c = str.charAt(i);
   153             int s = str.styleCodeAt(i) & ~current.getMask() | current.getStyle();
   155             int s = str.styleCodeAt(i) & ~current.getMask() | current.getStyle();
   154             if (tabs > 0 && c == '\t') {
   156             if (tabs.defined() && c == '\t') {
   155                 insertTab(new AttributedStyle(s, 0));
   157                 insertTab(new AttributedStyle(s, 0));
   156             } else {
   158             } else {
   157                 ensureCapacity(length + 1);
   159                 ensureCapacity(length + 1);
   158                 buffer[length] = c;
   160                 buffer[length] = c;
   159                 style[length] = s;
   161                 style[length] = s;
   330                     ansiState = 0;
   332                     ansiState = 0;
   331                 } else if (!(c >= '0' && c <= '9' || c == ';')) {
   333                 } else if (!(c >= '0' && c <= '9' || c == ';')) {
   332                     // This is not a SGR code, so ignore
   334                     // This is not a SGR code, so ignore
   333                     ansiState = 0;
   335                     ansiState = 0;
   334                 }
   336                 }
   335             } else if (c == '\t' && tabs > 0) {
   337             } else if (c == '\t' && tabs.defined()) {
   336                 insertTab(current);
   338                 insertTab(current);
   337             } else {
   339             } else {
   338                 ensureCapacity(length + 1);
   340                 ensureCapacity(length + 1);
   339                 buffer[length] = c;
   341                 buffer[length] = c;
   340                 style[length] = this.current.getStyle();
   342                 style[length] = this.current.getStyle();
   348         }
   350         }
   349         return this;
   351         return this;
   350     }
   352     }
   351 
   353 
   352     protected void insertTab(AttributedStyle s) {
   354     protected void insertTab(AttributedStyle s) {
   353         int nb = tabs - lastLineLength % tabs;
   355         int nb = tabs.spaces(lastLineLength);
   354         ensureCapacity(length + nb);
   356         ensureCapacity(length + nb);
   355         for (int i = 0; i < nb; i++) {
   357         for (int i = 0; i < nb; i++) {
   356             buffer[length] = ' ';
   358             buffer[length] = ' ';
   357             style[length] = s.getStyle();
   359             style[length] = s.getStyle();
   358             length++;
   360             length++;
   371      * If tab size is set to 0, tabs are not expanded (the default).
   373      * If tab size is set to 0, tabs are not expanded (the default).
   372      * @param tabsize Spaces per tab or 0 for no tab expansion. Must be non-negative
   374      * @param tabsize Spaces per tab or 0 for no tab expansion. Must be non-negative
   373      * @return this
   375      * @return this
   374      */
   376      */
   375     public AttributedStringBuilder tabs(int tabsize) {
   377     public AttributedStringBuilder tabs(int tabsize) {
       
   378         if (tabsize < 0) {
       
   379             throw new IllegalArgumentException("Tab size must be non negative");
       
   380         }
       
   381         return tabs(Arrays.asList(tabsize));
       
   382     }
       
   383 
       
   384     public AttributedStringBuilder tabs(List<Integer> tabs) {
   376         if (length > 0) {
   385         if (length > 0) {
   377             throw new IllegalStateException("Cannot change tab size after appending text");
   386             throw new IllegalStateException("Cannot change tab size after appending text");
   378         }
   387         }
   379         if (tabsize < 0) {
   388         this.tabs = new TabStops(tabs);
   380             throw new IllegalArgumentException("Tab size must be non negative");
       
   381         }
       
   382         this.tabs = tabsize;
       
   383         return this;
   389         return this;
   384     }
   390     }
   385 
   391 
   386     public AttributedStringBuilder styleMatches(Pattern pattern, AttributedStyle s) {
   392     public AttributedStringBuilder styleMatches(Pattern pattern, AttributedStyle s) {
   387         Matcher matcher = pattern.matcher(this);
   393         Matcher matcher = pattern.matcher(this);
   391             }
   397             }
   392         }
   398         }
   393         return this;
   399         return this;
   394     }
   400     }
   395 
   401 
       
   402     public AttributedStringBuilder styleMatches(Pattern pattern, List<AttributedStyle> styles) {
       
   403         Matcher matcher = pattern.matcher(this);
       
   404         while (matcher.find()) {
       
   405             for (int group = 0; group < matcher.groupCount(); group++) {
       
   406                 AttributedStyle s = styles.get(group);
       
   407                 for (int i = matcher.start(group + 1); i < matcher.end(group + 1); i++) {
       
   408                     style[i] = (style[i] & ~s.getMask()) | s.getStyle();
       
   409                 }
       
   410             }
       
   411         }
       
   412         return this;
       
   413     }
       
   414 
       
   415     private class TabStops {
       
   416         private List<Integer> tabs = new ArrayList<>();
       
   417         private int lastStop = 0;
       
   418         private int lastSize = 0;
       
   419 
       
   420         public TabStops(int tabs) {
       
   421             this.lastSize = tabs;
       
   422         }
       
   423 
       
   424         public TabStops(List<Integer> tabs) {
       
   425             this.tabs = tabs;
       
   426             int p = 0;
       
   427             for (int s: tabs) {
       
   428                 if (s <= p) {
       
   429                     continue;
       
   430                 }
       
   431                 lastStop = s;
       
   432                 lastSize = s - p;
       
   433                 p = s;
       
   434             }
       
   435         }
       
   436 
       
   437         boolean defined() {
       
   438             return lastSize > 0;
       
   439         }
       
   440 
       
   441         int spaces(int lastLineLength) {
       
   442             int out = 0;
       
   443             if (lastLineLength >= lastStop) {
       
   444                 out = lastSize - (lastLineLength - lastStop) % lastSize;
       
   445             } else {
       
   446                 for (int s: tabs) {
       
   447                     if (s > lastLineLength) {
       
   448                         out = s - lastLineLength;
       
   449                         break;
       
   450                     }
       
   451                 }
       
   452             }
       
   453             return out;
       
   454         }
       
   455 
       
   456     }
       
   457 
   396 }
   458 }