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