src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedStringBuilder.java
equal
deleted
inserted
replaced
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 } |