public char lastChar() { return builder.charAt(length() - 1); }
/** * @since 2.0.0 to follow Appendable interface */ @NonNull @Override public SpannableBuilder append(CharSequence csq, int start, int end) { final CharSequence cs = csq.subSequence(start, end); copySpans(length(), cs); builder.append(cs); return this; }
@NonNull @Override public SpannableBuilder append(@NonNull CharSequence cs) { copySpans(length(), cs); builder.append(cs); return this; }
@NonNull public SpannableBuilder setSpan(@NonNull Object span, int start) { return setSpan(span, start, length()); }
private void setSpan(int start, @Nullable Object span) { SpannableBuilder.setSpans(builder, span, start, builder.length()); }
@NonNull public SpannableBuilder append(@NonNull CharSequence cs, @NonNull Object span) { final int length = length(); append(cs); setSpan(span, length); return this; }
private void newLine() { if (builder.length() > 0 && '\n' != builder.lastChar()) { builder.append('\n'); } }
@Override public void visit(Emphasis emphasis) { final int length = builder.length(); visitChildren(emphasis); setSpan(length, factory.emphasis()); }
@Override public void visit(StrongEmphasis strongEmphasis) { final int length = builder.length(); visitChildren(strongEmphasis); setSpan(length, factory.strongEmphasis()); }
@Override public void visit(Code code) { final int length = builder.length(); // NB, in order to provide a _padding_ feeling code is wrapped inside two unbreakable spaces // unfortunately we cannot use this for multiline code as we cannot control where a new line break will be inserted builder.append('\u00a0'); builder.append(code.getLiteral()); builder.append('\u00a0'); setSpan(length, factory.code(theme, false)); }
@Test public void set_spans_single() { // single span as `spans` argument correctly added builder.append('0'); assertTrue(builder.getSpans(0, builder.length()).isEmpty()); final Object span = new Object(); setSpans(builder, span, 0, 1); final List<SpannableBuilder.Span> spans = builder.getSpans(0, builder.length()); assertEquals(1, spans.size()); assertEquals(span, spans.get(0).what); }
@Override public void visit(Link link) { final int length = builder.length(); visitChildren(link); final String destination = configuration.urlProcessor().process(link.getDestination()); setSpan(length, factory.link(theme, destination, configuration.linkResolver())); }
@Override public void visit(ThematicBreak thematicBreak) { newLine(); final int length = builder.length(); builder.append('\u00a0'); // without space it won't render setSpan(length, factory.thematicBreak(theme)); if (hasNext(thematicBreak)) { newLine(); forceNewLine(); } }
@Override public void visit(BlockQuote blockQuote) { newLine(); final int length = builder.length(); blockIndent += 1; visitChildren(blockQuote); setSpan(length, factory.blockQuote(theme)); blockIndent -= 1; if (hasNext(blockQuote)) { newLine(); forceNewLine(); } }
@Override public void visit(Heading heading) { newLine(); final int length = builder.length(); visitChildren(heading); setSpan(length, factory.heading(theme, heading.getLevel())); if (hasNext(heading)) { newLine(); // after heading we add another line anyway (no additional checks) forceNewLine(); } }
@Override public void visit(Paragraph paragraph) { final boolean inTightList = isInTightList(paragraph); if (!inTightList) { newLine(); } final int length = builder.length(); visitChildren(paragraph); // @since 1.1.1 apply paragraph span setSpan(length, factory.paragraph(inTightList)); if (hasNext(paragraph) && !inTightList) { newLine(); forceNewLine(); } }