View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.fusesource.jansi;
20  
21  import java.util.ArrayList;
22  
23  /**
24   * Provides a fluent API for generating
25   * <a href="https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_sequences">ANSI escape sequences</a>.
26   *
27   * This class comes from Jansi and is provided for backward compatibility
28   * with maven-shared-utils, while Maven has migrated to JLine (into which Jansi has been merged
29   * since JLine 3.25.0).
30   */
31  @SuppressWarnings({"checkstyle:MagicNumber", "unused"})
32  public class Ansi implements Appendable {
33  
34      private static final char FIRST_ESC_CHAR = 27;
35      private static final char SECOND_ESC_CHAR = '[';
36  
37      /**
38       * <a href="https://en.wikipedia.org/wiki/ANSI_escape_code#Colors">ANSI 8 colors</a> for fluent API
39       */
40      public enum Color {
41          BLACK(0, "BLACK"),
42          RED(1, "RED"),
43          GREEN(2, "GREEN"),
44          YELLOW(3, "YELLOW"),
45          BLUE(4, "BLUE"),
46          MAGENTA(5, "MAGENTA"),
47          CYAN(6, "CYAN"),
48          WHITE(7, "WHITE"),
49          DEFAULT(9, "DEFAULT");
50  
51          private final int value;
52          private final String name;
53  
54          Color(int index, String name) {
55              this.value = index;
56              this.name = name;
57          }
58  
59          @Override
60          public String toString() {
61              return name;
62          }
63  
64          public int value() {
65              return value;
66          }
67  
68          public int fg() {
69              return value + 30;
70          }
71  
72          public int bg() {
73              return value + 40;
74          }
75  
76          public int fgBright() {
77              return value + 90;
78          }
79  
80          public int bgBright() {
81              return value + 100;
82          }
83      }
84  
85      /**
86       * Display attributes, also know as
87       * <a href="https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters">SGR
88       * (Select Graphic Rendition) parameters</a>.
89       */
90      public enum Attribute {
91          RESET(0, "RESET"),
92          INTENSITY_BOLD(1, "INTENSITY_BOLD"),
93          INTENSITY_FAINT(2, "INTENSITY_FAINT"),
94          ITALIC(3, "ITALIC_ON"),
95          UNDERLINE(4, "UNDERLINE_ON"),
96          BLINK_SLOW(5, "BLINK_SLOW"),
97          BLINK_FAST(6, "BLINK_FAST"),
98          NEGATIVE_ON(7, "NEGATIVE_ON"),
99          CONCEAL_ON(8, "CONCEAL_ON"),
100         STRIKETHROUGH_ON(9, "STRIKETHROUGH_ON"),
101         UNDERLINE_DOUBLE(21, "UNDERLINE_DOUBLE"),
102         INTENSITY_BOLD_OFF(22, "INTENSITY_BOLD_OFF"),
103         ITALIC_OFF(23, "ITALIC_OFF"),
104         UNDERLINE_OFF(24, "UNDERLINE_OFF"),
105         BLINK_OFF(25, "BLINK_OFF"),
106         NEGATIVE_OFF(27, "NEGATIVE_OFF"),
107         CONCEAL_OFF(28, "CONCEAL_OFF"),
108         STRIKETHROUGH_OFF(29, "STRIKETHROUGH_OFF");
109 
110         private final int value;
111         private final String name;
112 
113         Attribute(int index, String name) {
114             this.value = index;
115             this.name = name;
116         }
117 
118         @Override
119         public String toString() {
120             return name;
121         }
122 
123         public int value() {
124             return value;
125         }
126     }
127 
128     /**
129      * ED (Erase in Display) / EL (Erase in Line) parameter (see
130      * <a href="https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_sequences">CSI sequence J and K</a>)
131      * @see Ansi#eraseScreen(Erase)
132      * @see Ansi#eraseLine(Erase)
133      */
134     public enum Erase {
135         FORWARD(0, "FORWARD"),
136         BACKWARD(1, "BACKWARD"),
137         ALL(2, "ALL");
138 
139         private final int value;
140         private final String name;
141 
142         Erase(int index, String name) {
143             this.value = index;
144             this.name = name;
145         }
146 
147         @Override
148         public String toString() {
149             return name;
150         }
151 
152         public int value() {
153             return value;
154         }
155     }
156 
157     @FunctionalInterface
158     public interface Consumer {
159         void apply(Ansi ansi);
160     }
161 
162     public static boolean isEnabled() {
163         return org.apache.maven.cli.jline.MessageUtils.isColorEnabled() && org.jline.jansi.Ansi.isEnabled();
164     }
165 
166     public static Ansi ansi() {
167         if (isEnabled()) {
168             return new Ansi();
169         } else {
170             return new NoAnsi();
171         }
172     }
173 
174     public static Ansi ansi(StringBuilder builder) {
175         if (isEnabled()) {
176             return new Ansi(builder);
177         } else {
178             return new NoAnsi(builder);
179         }
180     }
181 
182     public static Ansi ansi(int size) {
183         if (isEnabled()) {
184             return new Ansi(size);
185         } else {
186             return new NoAnsi(size);
187         }
188     }
189 
190     private static class NoAnsi extends Ansi {
191         NoAnsi() {
192             super();
193         }
194 
195         NoAnsi(int size) {
196             super(size);
197         }
198 
199         NoAnsi(StringBuilder builder) {
200             super(builder);
201         }
202 
203         @Override
204         public Ansi fg(Color color) {
205             return this;
206         }
207 
208         @Override
209         public Ansi bg(Color color) {
210             return this;
211         }
212 
213         @Override
214         public Ansi fgBright(Color color) {
215             return this;
216         }
217 
218         @Override
219         public Ansi bgBright(Color color) {
220             return this;
221         }
222 
223         @Override
224         public Ansi fg(int color) {
225             return this;
226         }
227 
228         @Override
229         public Ansi fgRgb(int r, int g, int b) {
230             return this;
231         }
232 
233         @Override
234         public Ansi bg(int color) {
235             return this;
236         }
237 
238         @Override
239         public Ansi bgRgb(int r, int g, int b) {
240             return this;
241         }
242 
243         @Override
244         public Ansi a(Attribute attribute) {
245             return this;
246         }
247 
248         @Override
249         public Ansi cursor(int row, int column) {
250             return this;
251         }
252 
253         @Override
254         public Ansi cursorToColumn(int x) {
255             return this;
256         }
257 
258         @Override
259         public Ansi cursorUp(int y) {
260             return this;
261         }
262 
263         @Override
264         public Ansi cursorRight(int x) {
265             return this;
266         }
267 
268         @Override
269         public Ansi cursorDown(int y) {
270             return this;
271         }
272 
273         @Override
274         public Ansi cursorLeft(int x) {
275             return this;
276         }
277 
278         @Override
279         public Ansi cursorDownLine() {
280             return this;
281         }
282 
283         @Override
284         public Ansi cursorDownLine(final int n) {
285             return this;
286         }
287 
288         @Override
289         public Ansi cursorUpLine() {
290             return this;
291         }
292 
293         @Override
294         public Ansi cursorUpLine(final int n) {
295             return this;
296         }
297 
298         @Override
299         public Ansi eraseScreen() {
300             return this;
301         }
302 
303         @Override
304         public Ansi eraseScreen(Erase kind) {
305             return this;
306         }
307 
308         @Override
309         public Ansi eraseLine() {
310             return this;
311         }
312 
313         @Override
314         public Ansi eraseLine(Erase kind) {
315             return this;
316         }
317 
318         @Override
319         public Ansi scrollUp(int rows) {
320             return this;
321         }
322 
323         @Override
324         public Ansi scrollDown(int rows) {
325             return this;
326         }
327 
328         @Override
329         public Ansi saveCursorPosition() {
330             return this;
331         }
332 
333         @Override
334         @Deprecated
335         public Ansi restorCursorPosition() {
336             return this;
337         }
338 
339         @Override
340         public Ansi restoreCursorPosition() {
341             return this;
342         }
343 
344         @Override
345         public Ansi reset() {
346             return this;
347         }
348     }
349 
350     private final StringBuilder builder;
351     private final ArrayList<Integer> attributeOptions = new ArrayList<>(5);
352 
353     public Ansi() {
354         this(new StringBuilder(80));
355     }
356 
357     public Ansi(Ansi parent) {
358         this(new StringBuilder(parent.builder));
359         attributeOptions.addAll(parent.attributeOptions);
360     }
361 
362     public Ansi(int size) {
363         this(new StringBuilder(size));
364     }
365 
366     public Ansi(StringBuilder builder) {
367         this.builder = builder;
368     }
369 
370     public Ansi fg(Color color) {
371         attributeOptions.add(color.fg());
372         return this;
373     }
374 
375     public Ansi fg(int color) {
376         attributeOptions.add(38);
377         attributeOptions.add(5);
378         attributeOptions.add(color & 0xff);
379         return this;
380     }
381 
382     public Ansi fgRgb(int color) {
383         return fgRgb(color >> 16, color >> 8, color);
384     }
385 
386     public Ansi fgRgb(int r, int g, int b) {
387         attributeOptions.add(38);
388         attributeOptions.add(2);
389         attributeOptions.add(r & 0xff);
390         attributeOptions.add(g & 0xff);
391         attributeOptions.add(b & 0xff);
392         return this;
393     }
394 
395     public Ansi fgBlack() {
396         return this.fg(Color.BLACK);
397     }
398 
399     public Ansi fgBlue() {
400         return this.fg(Color.BLUE);
401     }
402 
403     public Ansi fgCyan() {
404         return this.fg(Color.CYAN);
405     }
406 
407     public Ansi fgDefault() {
408         return this.fg(Color.DEFAULT);
409     }
410 
411     public Ansi fgGreen() {
412         return this.fg(Color.GREEN);
413     }
414 
415     public Ansi fgMagenta() {
416         return this.fg(Color.MAGENTA);
417     }
418 
419     public Ansi fgRed() {
420         return this.fg(Color.RED);
421     }
422 
423     public Ansi fgYellow() {
424         return this.fg(Color.YELLOW);
425     }
426 
427     public Ansi bg(Color color) {
428         attributeOptions.add(color.bg());
429         return this;
430     }
431 
432     public Ansi bg(int color) {
433         attributeOptions.add(48);
434         attributeOptions.add(5);
435         attributeOptions.add(color & 0xff);
436         return this;
437     }
438 
439     public Ansi bgRgb(int color) {
440         return bgRgb(color >> 16, color >> 8, color);
441     }
442 
443     public Ansi bgRgb(int r, int g, int b) {
444         attributeOptions.add(48);
445         attributeOptions.add(2);
446         attributeOptions.add(r & 0xff);
447         attributeOptions.add(g & 0xff);
448         attributeOptions.add(b & 0xff);
449         return this;
450     }
451 
452     public Ansi bgCyan() {
453         return this.bg(Color.CYAN);
454     }
455 
456     public Ansi bgDefault() {
457         return this.bg(Color.DEFAULT);
458     }
459 
460     public Ansi bgGreen() {
461         return this.bg(Color.GREEN);
462     }
463 
464     public Ansi bgMagenta() {
465         return this.bg(Color.MAGENTA);
466     }
467 
468     public Ansi bgRed() {
469         return this.bg(Color.RED);
470     }
471 
472     public Ansi bgYellow() {
473         return this.bg(Color.YELLOW);
474     }
475 
476     public Ansi fgBright(Color color) {
477         attributeOptions.add(color.fgBright());
478         return this;
479     }
480 
481     public Ansi fgBrightBlack() {
482         return this.fgBright(Color.BLACK);
483     }
484 
485     public Ansi fgBrightBlue() {
486         return this.fgBright(Color.BLUE);
487     }
488 
489     public Ansi fgBrightCyan() {
490         return this.fgBright(Color.CYAN);
491     }
492 
493     public Ansi fgBrightDefault() {
494         return this.fgBright(Color.DEFAULT);
495     }
496 
497     public Ansi fgBrightGreen() {
498         return this.fgBright(Color.GREEN);
499     }
500 
501     public Ansi fgBrightMagenta() {
502         return this.fgBright(Color.MAGENTA);
503     }
504 
505     public Ansi fgBrightRed() {
506         return this.fgBright(Color.RED);
507     }
508 
509     public Ansi fgBrightYellow() {
510         return this.fgBright(Color.YELLOW);
511     }
512 
513     public Ansi bgBright(Color color) {
514         attributeOptions.add(color.bgBright());
515         return this;
516     }
517 
518     public Ansi bgBrightCyan() {
519         return this.bgBright(Color.CYAN);
520     }
521 
522     public Ansi bgBrightDefault() {
523         return this.bgBright(Color.DEFAULT);
524     }
525 
526     public Ansi bgBrightGreen() {
527         return this.bgBright(Color.GREEN);
528     }
529 
530     public Ansi bgBrightMagenta() {
531         return this.bgBright(Color.MAGENTA);
532     }
533 
534     public Ansi bgBrightRed() {
535         return this.bgBright(Color.RED);
536     }
537 
538     public Ansi bgBrightYellow() {
539         return this.bgBright(Color.YELLOW);
540     }
541 
542     public Ansi a(Attribute attribute) {
543         attributeOptions.add(attribute.value());
544         return this;
545     }
546 
547     /**
548      * Moves the cursor to row n, column m. The values are 1-based.
549      * Any values less than 1 are mapped to 1.
550      *
551      * @param row    row (1-based) from top
552      * @param column column (1 based) from left
553      * @return this Ansi instance
554      */
555     public Ansi cursor(final int row, final int column) {
556         return appendEscapeSequence('H', Math.max(1, row), Math.max(1, column));
557     }
558 
559     /**
560      * Moves the cursor to column n. The parameter n is 1-based.
561      * If n is less than 1 it is moved to the first column.
562      *
563      * @param x the index (1-based) of the column to move to
564      * @return this Ansi instance
565      */
566     public Ansi cursorToColumn(final int x) {
567         return appendEscapeSequence('G', Math.max(1, x));
568     }
569 
570     /**
571      * Moves the cursor up. If the parameter y is negative it moves the cursor down.
572      *
573      * @param y the number of lines to move up
574      * @return this Ansi instance
575      */
576     public Ansi cursorUp(final int y) {
577         return y > 0 ? appendEscapeSequence('A', y) : y < 0 ? cursorDown(-y) : this;
578     }
579 
580     /**
581      * Moves the cursor down. If the parameter y is negative it moves the cursor up.
582      *
583      * @param y the number of lines to move down
584      * @return this Ansi instance
585      */
586     public Ansi cursorDown(final int y) {
587         return y > 0 ? appendEscapeSequence('B', y) : y < 0 ? cursorUp(-y) : this;
588     }
589 
590     /**
591      * Moves the cursor right. If the parameter x is negative it moves the cursor left.
592      *
593      * @param x the number of characters to move right
594      * @return this Ansi instance
595      */
596     public Ansi cursorRight(final int x) {
597         return x > 0 ? appendEscapeSequence('C', x) : x < 0 ? cursorLeft(-x) : this;
598     }
599 
600     /**
601      * Moves the cursor left. If the parameter x is negative it moves the cursor right.
602      *
603      * @param x the number of characters to move left
604      * @return this Ansi instance
605      */
606     public Ansi cursorLeft(final int x) {
607         return x > 0 ? appendEscapeSequence('D', x) : x < 0 ? cursorRight(-x) : this;
608     }
609 
610     /**
611      * Moves the cursor relative to the current position. The cursor is moved right if x is
612      * positive, left if negative and down if y is positive and up if negative.
613      *
614      * @param x the number of characters to move horizontally
615      * @param y the number of lines to move vertically
616      * @return this Ansi instance
617      * @since 2.2
618      */
619     public Ansi cursorMove(final int x, final int y) {
620         return cursorRight(x).cursorDown(y);
621     }
622 
623     /**
624      * Moves the cursor to the beginning of the line below.
625      *
626      * @return this Ansi instance
627      */
628     public Ansi cursorDownLine() {
629         return appendEscapeSequence('E');
630     }
631 
632     /**
633      * Moves the cursor to the beginning of the n-th line below. If the parameter n is negative it
634      * moves the cursor to the beginning of the n-th line above.
635      *
636      * @param n the number of lines to move the cursor
637      * @return this Ansi instance
638      */
639     public Ansi cursorDownLine(final int n) {
640         return n < 0 ? cursorUpLine(-n) : appendEscapeSequence('E', n);
641     }
642 
643     /**
644      * Moves the cursor to the beginning of the line above.
645      *
646      * @return this Ansi instance
647      */
648     public Ansi cursorUpLine() {
649         return appendEscapeSequence('F');
650     }
651 
652     /**
653      * Moves the cursor to the beginning of the n-th line above. If the parameter n is negative it
654      * moves the cursor to the beginning of the n-th line below.
655      *
656      * @param n the number of lines to move the cursor
657      * @return this Ansi instance
658      */
659     public Ansi cursorUpLine(final int n) {
660         return n < 0 ? cursorDownLine(-n) : appendEscapeSequence('F', n);
661     }
662 
663     public Ansi eraseScreen() {
664         return appendEscapeSequence('J', Erase.ALL.value());
665     }
666 
667     public Ansi eraseScreen(final Erase kind) {
668         return appendEscapeSequence('J', kind.value());
669     }
670 
671     public Ansi eraseLine() {
672         return appendEscapeSequence('K');
673     }
674 
675     public Ansi eraseLine(final Erase kind) {
676         return appendEscapeSequence('K', kind.value());
677     }
678 
679     public Ansi scrollUp(final int rows) {
680         if (rows == Integer.MIN_VALUE) {
681             return scrollDown(Integer.MAX_VALUE);
682         }
683         return rows > 0 ? appendEscapeSequence('S', rows) : rows < 0 ? scrollDown(-rows) : this;
684     }
685 
686     public Ansi scrollDown(final int rows) {
687         if (rows == Integer.MIN_VALUE) {
688             return scrollUp(Integer.MAX_VALUE);
689         }
690         return rows > 0 ? appendEscapeSequence('T', rows) : rows < 0 ? scrollUp(-rows) : this;
691     }
692 
693     @Deprecated
694     public Ansi restorCursorPosition() {
695         return restoreCursorPosition();
696     }
697 
698     public Ansi saveCursorPosition() {
699         saveCursorPositionSCO();
700         return saveCursorPositionDEC();
701     }
702 
703     // SCO command
704     public Ansi saveCursorPositionSCO() {
705         return appendEscapeSequence('s');
706     }
707 
708     // DEC command
709     public Ansi saveCursorPositionDEC() {
710         builder.append(FIRST_ESC_CHAR);
711         builder.append('7');
712         return this;
713     }
714 
715     public Ansi restoreCursorPosition() {
716         restoreCursorPositionSCO();
717         return restoreCursorPositionDEC();
718     }
719 
720     // SCO command
721     public Ansi restoreCursorPositionSCO() {
722         return appendEscapeSequence('u');
723     }
724 
725     // DEC command
726     public Ansi restoreCursorPositionDEC() {
727         builder.append(FIRST_ESC_CHAR);
728         builder.append('8');
729         return this;
730     }
731 
732     public Ansi reset() {
733         return a(Attribute.RESET);
734     }
735 
736     public Ansi bold() {
737         return a(Attribute.INTENSITY_BOLD);
738     }
739 
740     public Ansi boldOff() {
741         return a(Attribute.INTENSITY_BOLD_OFF);
742     }
743 
744     public Ansi a(String value) {
745         flushAttributes();
746         builder.append(value);
747         return this;
748     }
749 
750     public Ansi a(boolean value) {
751         flushAttributes();
752         builder.append(value);
753         return this;
754     }
755 
756     public Ansi a(char value) {
757         flushAttributes();
758         builder.append(value);
759         return this;
760     }
761 
762     public Ansi a(char[] value, int offset, int len) {
763         flushAttributes();
764         builder.append(value, offset, len);
765         return this;
766     }
767 
768     public Ansi a(char[] value) {
769         flushAttributes();
770         builder.append(value);
771         return this;
772     }
773 
774     public Ansi a(CharSequence value, int start, int end) {
775         flushAttributes();
776         builder.append(value, start, end);
777         return this;
778     }
779 
780     public Ansi a(CharSequence value) {
781         flushAttributes();
782         builder.append(value);
783         return this;
784     }
785 
786     public Ansi a(double value) {
787         flushAttributes();
788         builder.append(value);
789         return this;
790     }
791 
792     public Ansi a(float value) {
793         flushAttributes();
794         builder.append(value);
795         return this;
796     }
797 
798     public Ansi a(int value) {
799         flushAttributes();
800         builder.append(value);
801         return this;
802     }
803 
804     public Ansi a(long value) {
805         flushAttributes();
806         builder.append(value);
807         return this;
808     }
809 
810     public Ansi a(Object value) {
811         flushAttributes();
812         builder.append(value);
813         return this;
814     }
815 
816     public Ansi a(StringBuffer value) {
817         flushAttributes();
818         builder.append(value);
819         return this;
820     }
821 
822     public Ansi newline() {
823         flushAttributes();
824         builder.append(System.getProperty("line.separator"));
825         return this;
826     }
827 
828     public Ansi format(String pattern, Object... args) {
829         flushAttributes();
830         builder.append(String.format(pattern, args));
831         return this;
832     }
833 
834     /**
835      * Applies another function to this Ansi instance.
836      *
837      * @param fun the function to apply
838      * @return this Ansi instance
839      * @since 2.2
840      */
841     public Ansi apply(Consumer fun) {
842         fun.apply(this);
843         return this;
844     }
845 
846     /**
847      * Uses the {@link org.jline.jansi.AnsiRenderer}
848      * to generate the ANSI escape sequences for the supplied text.
849      *
850      * @param text text
851      * @return this
852      * @since 2.2
853      */
854     public Ansi render(final String text) {
855         a(new org.jline.jansi.Ansi().render(text).toString());
856         return this;
857     }
858 
859     /**
860      * String formats and renders the supplied arguments.  Uses the {@link org.jline.jansi.AnsiRenderer}
861      * to generate the ANSI escape sequences.
862      *
863      * @param text format
864      * @param args arguments
865      * @return this
866      * @since 2.2
867      */
868     public Ansi render(final String text, Object... args) {
869         a(String.format(new org.jline.jansi.Ansi().render(text).toString(), args));
870         return this;
871     }
872 
873     @Override
874     public String toString() {
875         flushAttributes();
876         return builder.toString();
877     }
878 
879     ///////////////////////////////////////////////////////////////////
880     // Private Helper Methods
881     ///////////////////////////////////////////////////////////////////
882 
883     private Ansi appendEscapeSequence(char command) {
884         flushAttributes();
885         builder.append(FIRST_ESC_CHAR);
886         builder.append(SECOND_ESC_CHAR);
887         builder.append(command);
888         return this;
889     }
890 
891     private Ansi appendEscapeSequence(char command, int option) {
892         flushAttributes();
893         builder.append(FIRST_ESC_CHAR);
894         builder.append(SECOND_ESC_CHAR);
895         builder.append(option);
896         builder.append(command);
897         return this;
898     }
899 
900     private Ansi appendEscapeSequence(char command, Object... options) {
901         flushAttributes();
902         return doAppendEscapeSequence(command, options);
903     }
904 
905     private void flushAttributes() {
906         if (attributeOptions.isEmpty()) {
907             return;
908         }
909         if (attributeOptions.size() == 1 && attributeOptions.get(0) == 0) {
910             builder.append(FIRST_ESC_CHAR);
911             builder.append(SECOND_ESC_CHAR);
912             builder.append('m');
913         } else {
914             doAppendEscapeSequence('m', attributeOptions.toArray());
915         }
916         attributeOptions.clear();
917     }
918 
919     private Ansi doAppendEscapeSequence(char command, Object... options) {
920         builder.append(FIRST_ESC_CHAR);
921         builder.append(SECOND_ESC_CHAR);
922         int size = options.length;
923         for (int i = 0; i < size; i++) {
924             if (i != 0) {
925                 builder.append(';');
926             }
927             if (options[i] != null) {
928                 builder.append(options[i]);
929             }
930         }
931         builder.append(command);
932         return this;
933     }
934 
935     @Override
936     public Ansi append(CharSequence csq) {
937         builder.append(csq);
938         return this;
939     }
940 
941     @Override
942     public Ansi append(CharSequence csq, int start, int end) {
943         builder.append(csq, start, end);
944         return this;
945     }
946 
947     @Override
948     public Ansi append(char c) {
949         builder.append(c);
950         return this;
951     }
952 }