Java Code Examples for android.text.SpannableStringBuilder#delete()

The following examples show how to use android.text.SpannableStringBuilder#delete() . You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source File: InteractionUnit.java    From Dashchan with Apache License 2.0 6 votes vote down vote up
String getCopyReadyComment(CharSequence text, int start, int end) {
	if (text instanceof Spanned) {
		SpannableStringBuilder builder = new SpannableStringBuilder(text.subSequence(start, end));
		LinkSuffixSpan[] spans = builder.getSpans(0, builder.length(), LinkSuffixSpan.class);
		if (spans != null && spans.length > 0) {
			for (LinkSuffixSpan span : spans) {
				int spanStart = builder.getSpanStart(span);
				int spanEnd = builder.getSpanEnd(span);
				builder.delete(spanStart, spanEnd);
			}
		}
		return builder.toString();
	} else {
		return text.subSequence(start, end).toString();
	}
}
 
Example 2
Source File: TagHandlerImpl.java    From Markdown with MIT License 6 votes vote down vote up
@Override
public boolean link2(Line line) {
    line = line.get();
    SpannableStringBuilder builder = (SpannableStringBuilder) line.getStyle();
    Matcher matcher = obtain(Tag.LINK2, builder);
    if (matcher.find()) {
        String title = matcher.group(2);
        String id = matcher.group(3);
        Pair<String, String> link = idLinkLinks.get(id);
        if (link != null) {
            builder.delete(matcher.start(1), matcher.end(1));
            builder.insert(matcher.start(1), styleBuilder.link(title, link.first, link.second));
        } else {
            return false;
        }
        link2(line);
        return true;
    }
    return false;
}
 
Example 3
Source File: CustomHtmlToSpannedConverter.java    From zulip-android with Apache License 2.0 6 votes vote down vote up
private static void endSpan(SpannableStringBuilder text) {
    int len = text.length();
    Object obj = getLast(text, Href.class);
    int where = text.getSpanStart(obj);
    text.removeSpan(obj);
    if (where != len) {
        if (isEmoji) {
            text.delete(where, len);
            return;
        }
        Href h = (Href) obj;
        if (h != null && h.mHref != null) {
            if (ZulipApp.get().getEmail().equals(h.mHref)) {
                text.setSpan(new ForegroundColorSpan(userMentionSelfColor), where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            } else {
                text.setSpan(new ProfileSpan(h.mHref, userMentionColor), where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            }
        }
    }
}
 
Example 4
Source File: PostItem.java    From Dashchan with Apache License 2.0 6 votes vote down vote up
private static CharSequence obtainThreadComment(String comment, String chanName, ChanMarkup.MarkupExtra extra) {
	SpannableStringBuilder commentBuilder = new SpannableStringBuilder(obtainComment(comment,
			chanName, null, extra));
	int linebreaks = 0;
	// Remove more than one linebreaks in sequence
	for (int i = commentBuilder.length() - 1; i >= 0; i--) {
		char c = commentBuilder.charAt(i);
		if (c == '\n') {
			linebreaks++;
		} else {
			if (linebreaks > 1) {
				// Remove linebreaks - 1 characters, keeping one line break
				commentBuilder.delete(i + 1, i + linebreaks);
			}
			linebreaks = 0;
		}
	}
	return commentBuilder;
}
 
Example 5
Source File: HtmlUtils.java    From materialup with Apache License 2.0 6 votes vote down vote up
/**
 * Parse the given input using {@link TouchableUrlSpan}s
 * rather than vanilla {@link URLSpan}s so that they respond to touch.
 *
 * @param input
 * @param linkTextColor
 * @param linkHighlightColor
 * @return
 */
public static Spanned parseHtml(String input,
                                ColorStateList linkTextColor,
                                @ColorInt int linkHighlightColor) {
    SpannableStringBuilder spanned = (SpannableStringBuilder) Html.fromHtml(input);

    // strip any trailing newlines
    while (spanned.charAt(spanned.length() - 1) == '\n') {
        spanned = spanned.delete(spanned.length() - 1, spanned.length());
    }

    URLSpan[] urlSpans = spanned.getSpans(0, spanned.length(), URLSpan.class);
    for (URLSpan urlSpan : urlSpans) {
        int start = spanned.getSpanStart(urlSpan);
        int end = spanned.getSpanEnd(urlSpan);
        spanned.removeSpan(urlSpan);
        // spanned.subSequence(start, start + 1) == "@" TODO send to our own user activity...
        // when i've written it
        spanned.setSpan(new TouchableUrlSpan(urlSpan.getURL(), linkTextColor,
                linkHighlightColor), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    }
    return spanned;
}
 
Example 6
Source File: TagHandlerImpl.java    From Markdown with MIT License 6 votes vote down vote up
@Override
public boolean em(Line line) {
    line = line.get();
    SpannableStringBuilder builder = (SpannableStringBuilder) line.getStyle();
    Matcher matcher = obtain(Tag.EM, builder);
    while (matcher.find()) {
        int start = matcher.start(1);
        int end = matcher.end(1);
        if (checkInCode(builder, start, end)) {
            continue;
        }
        SpannableStringBuilder sb = (SpannableStringBuilder) builder.subSequence(matcher.start(3), matcher.end(3));
        builder.delete(matcher.start(1), matcher.end(1));
        builder.insert(matcher.start(1), styleBuilder.em(sb));
        em(line);
        return true;
    }
    return false;
}
 
Example 7
Source File: TagHandlerImpl.java    From Markdown with MIT License 6 votes vote down vote up
@Override
public boolean italic(Line line) {
    line = line.get();
    SpannableStringBuilder builder = (SpannableStringBuilder) line.getStyle();
    Matcher matcher = obtain(Tag.ITALIC, builder);
    while (matcher.find()) {
        int start = matcher.start(1);
        int end = matcher.end(1);
        if (checkInCode(builder, start, end)) {
            continue;
        }
        SpannableStringBuilder sb = (SpannableStringBuilder) builder.subSequence(matcher.start(3), matcher.end(3));
        builder.delete(matcher.start(1), matcher.end(1));
        builder.insert(matcher.start(1), styleBuilder.italic(sb));
        italic(line);
        return true;
    }
    return false;
}
 
Example 8
Source File: MircColors.java    From Atomic with GNU General Public License v3.0 6 votes vote down vote up
/**
 * Replace the control codes
 *
 * @param m
 * @param ssb
 * @param style
 */
private static void replaceControlCodes(Matcher m, SpannableStringBuilder ssb, CharacterStyle style) {
  ArrayList<Integer> toremove = new ArrayList<Integer>();

  while ( m.find() ) {
    toremove.add(0, m.start());
    // Remove the ending control character unless it's \x0F
    if( m.group(2) != null && m.group(2) != m.group(3) ) {
      toremove.add(0, m.end() - 1);
    }
    ssb.setSpan(style, m.start(), m.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
  }

  for( Integer i : toremove ) {
    ssb.delete(i, i + 1);
  }
}
 
Example 9
Source File: OutlineItemView.java    From mOrgAnd with GNU General Public License v2.0 5 votes vote down vote up
private void setupUrls(SpannableStringBuilder stringBuilder) {
    Matcher matcher = OrgNodeUtils.urlPattern.matcher(stringBuilder);
    int currentIndex = 0;
    while(matcher.find(currentIndex)) {
        int beginIndex = matcher.start();
        final String url = matcher.group(1) != null ? matcher.group(1) : matcher.group(3);
        String alias = matcher.group(2) != null ? matcher.group(2) : url;

        stringBuilder.delete(matcher.start(), matcher.end());

        currentIndex = beginIndex + alias.length();
        stringBuilder.insert(beginIndex, alias);

        ClickableSpan clickable = new ClickableSpan() {
            public void onClick(View view) {
                Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                try {
                    Application.getInstace().getApplicationContext().startActivity(intent);
                } catch (ActivityNotFoundException ex) {
                    ex.printStackTrace();
                }
            }
        };

        stringBuilder.setSpan(clickable, beginIndex, currentIndex, 0);
        matcher = OrgNodeUtils.urlPattern.matcher(stringBuilder);
    }
}
 
Example 10
Source File: HtmlUtils.java    From android-proguards with Apache License 2.0 5 votes vote down vote up
/**
 * Parse the given input using {@link TouchableUrlSpan}s rather than vanilla {@link URLSpan}s
 * so that they respond to touch.
 */
public static SpannableStringBuilder parseHtml(
        String input,
        ColorStateList linkTextColor,
        @ColorInt int linkHighlightColor) {
    SpannableStringBuilder spanned = fromHtml(input);

    // strip any trailing newlines
    while (spanned.charAt(spanned.length() - 1) == '\n') {
        spanned = spanned.delete(spanned.length() - 1, spanned.length());
    }

    return linkifyPlainLinks(spanned, linkTextColor, linkHighlightColor);
}
 
Example 11
Source File: QuestionsView.java    From commcare-android with Apache License 2.0 5 votes vote down vote up
/**
 * Returns the hierarchy of groups to which the question belongs.
 */
private SpannableStringBuilder deriveGroupText(FormEntryCaption[] groups) {
    SpannableStringBuilder s = new SpannableStringBuilder("");
    String t;
    String m;
    int i;
    // list all groups in one string
    for (FormEntryCaption g : groups) {
        i = g.getMultiplicity() + 1;
        t = g.getLongText();
        m = g.getMarkdownText();

        if (m != null) {
            Spannable markdownSpannable = MarkupUtil.returnMarkdown(getContext(), m);
            s.append(markdownSpannable);
        } else if (t != null && !t.trim().equals("")) {
            s.append(t);
        } else {
            continue;
        }

        if (g.repeats() && i > 0) {
            s.append(" (").append(String.valueOf(i)).append(")");
        }
        s.append(" > ");
    }

    //remove the trailing " > "
    if (s.length() > 0) {
        s.delete(s.length() - 2, s.length());
    }

    return s;
}
 
Example 12
Source File: TagHandlerImpl.java    From Markdown with MIT License 5 votes vote down vote up
@Override
public boolean code(Line line) {
    line = line.get();
    SpannableStringBuilder builder = (SpannableStringBuilder) line.getStyle();
    Matcher matcher = obtain(Tag.CODE, builder);
    if (matcher.find()) {
        String content = matcher.group(3);
        builder.delete(matcher.start(1), matcher.end(1));
        builder.insert(matcher.start(1), styleBuilder.code(content));
        code(line);
        return true;
    }
    return false;
}
 
Example 13
Source File: MircColors.java    From Atomic with GNU General Public License v3.0 5 votes vote down vote up
/**
 * Removes mIRC color and style codes and returns the message without them.
 *
 * @param text A message with mirc colors and styles.
 * @return The same message with all the colors and styles removed.
 */
public static SpannableStringBuilder removeStyleAndColors(SpannableStringBuilder text) {
  ArrayList<int[]> toremove = new ArrayList<int[]>();
  Matcher m = cleanupPattern.matcher(text);
  while ( m.find() ) {
    toremove.add(0, new int[]{m.start(), m.end()});
  }
  for( int[] i : toremove ) {
    text.delete(i[0], i[1]);
  }
  return text;
}
 
Example 14
Source File: GitHistoryActivity.java    From APDE with GNU General Public License v2.0 4 votes vote down vote up
private Spannable cleanDiffText(String diffText) {
	String[] lines = diffText.split("\\r?\\n");
	SpannableStringBuilder output = new SpannableStringBuilder();
	
	boolean pastHeader = false;
	for (String line : lines) {
		if (!pastHeader) {
			//Filter out unwanted header text
			
			if (line.startsWith("diff --git")) continue;
			if (line.startsWith("index")) continue;
			if (line.startsWith("+++")) continue;
			if (line.startsWith("---")) continue;
			
			if (line.startsWith("old mode")) continue;
			if (line.startsWith("new mode")) continue;
			if (line.startsWith("new file mode")) continue;
			if (line.startsWith("deleted file mode")) continue;
			if (line.startsWith("similarity index")) continue;
			if (line.startsWith("rename from")) continue;
			if (line.startsWith("rename to")) continue;
			if (line.startsWith("copy from")) continue;
			if (line.startsWith("copy to")) continue;
			if (line.startsWith("dissimilarity index")) continue;
		}
		
		pastHeader = true;
		
		int outputLength = output.length();
		int lineLength = line.length();
		
		//We can remove the "+" and "-" characters from the beginning of the line if we want to
		//Currently opting not to for two reasons:
		// - People aren't used to it
		// - Blank lines aren't highlighted properly
		
		if (line.startsWith("+")) {
			output.append(line);
			output.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.git_diff_plus)), outputLength, outputLength + lineLength, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
		} else if (line.startsWith("-")) {
			output.append(line);
			output.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.git_diff_minus)), outputLength, outputLength + lineLength, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
		} else if (line.startsWith("@@") && line.endsWith("@@")) {
			output.append(line);
			output.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.git_diff_hunk_header)), outputLength, outputLength + lineLength, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
		} else if (line.equals("\\ No newline at end of file")) {
			output.append(line);
			output.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.git_diff_no_newline)), outputLength, outputLength + lineLength, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
		} else if (line.startsWith(" ")) {
			//Context lines
			output.append(line);
		} else {
			output.append(line);
		}
		
		output.append('\n');
	}
	
	//Trim off the trailing newline
	return output.length() > 0 ? output.delete(output.length() - 1, output.length()) : output;
}
 
Example 15
Source File: MessageAdapter.java    From Pix-Art-Messenger with GNU General Public License v3.0 4 votes vote down vote up
/**
 * Applies QuoteSpan to group of lines which starts with > or » characters.
 * Appends likebreaks and applies DividerSpan to them to show a padding between quote and text.
 */
private boolean handleTextQuotes(SpannableStringBuilder body, boolean darkBackground) {
    boolean startsWithQuote = false;
    char previous = '\n';
    int lineStart = -1;
    int lineTextStart = -1;
    int quoteStart = -1;
    for (int i = 0; i <= body.length(); i++) {
        char current = body.length() > i ? body.charAt(i) : '\n';
        if (lineStart == -1) {
            if (previous == '\n') {
                if ((current == '>' && UIHelper.isPositionFollowedByQuoteableCharacter(body, i))
                        || current == '\u00bb' && !UIHelper.isPositionFollowedByQuote(body, i)) {
                    // Line start with quote
                    lineStart = i;
                    if (quoteStart == -1) quoteStart = i;
                    if (i == 0) startsWithQuote = true;
                } else if (quoteStart >= 0) {
                    // Line start without quote, apply spans there
                    applyQuoteSpan(body, quoteStart, i - 1, darkBackground);
                    quoteStart = -1;
                }
            }
        } else {
            // Remove extra spaces between > and first character in the line
            // > character will be removed too
            if (current != ' ' && lineTextStart == -1) {
                lineTextStart = i;
            }
            if (current == '\n') {
                body.delete(lineStart, lineTextStart);
                i -= lineTextStart - lineStart;
                if (i == lineStart) {
                    // Avoid empty lines because span over empty line can be hidden
                    body.insert(i++, " ");
                }
                lineStart = -1;
                lineTextStart = -1;
            }
        }
        previous = current;
    }
    if (quoteStart >= 0) {
        // Apply spans to finishing open quote
        applyQuoteSpan(body, quoteStart, body.length(), darkBackground);
    }
    return startsWithQuote;
}
 
Example 16
Source File: TtmlNode.java    From TelePlus-Android with GNU General Public License v2.0 4 votes vote down vote up
private SpannableStringBuilder cleanUpText(SpannableStringBuilder builder) {
  // Having joined the text elements, we need to do some final cleanup on the result.
  // 1. Collapse multiple consecutive spaces into a single space.
  int builderLength = builder.length();
  for (int i = 0; i < builderLength; i++) {
    if (builder.charAt(i) == ' ') {
      int j = i + 1;
      while (j < builder.length() && builder.charAt(j) == ' ') {
        j++;
      }
      int spacesToDelete = j - (i + 1);
      if (spacesToDelete > 0) {
        builder.delete(i, i + spacesToDelete);
        builderLength -= spacesToDelete;
      }
    }
  }
  // 2. Remove any spaces from the start of each line.
  if (builderLength > 0 && builder.charAt(0) == ' ') {
    builder.delete(0, 1);
    builderLength--;
  }
  for (int i = 0; i < builderLength - 1; i++) {
    if (builder.charAt(i) == '\n' && builder.charAt(i + 1) == ' ') {
      builder.delete(i + 1, i + 2);
      builderLength--;
    }
  }
  // 3. Remove any spaces from the end of each line.
  if (builderLength > 0 && builder.charAt(builderLength - 1) == ' ') {
    builder.delete(builderLength - 1, builderLength);
    builderLength--;
  }
  for (int i = 0; i < builderLength - 1; i++) {
    if (builder.charAt(i) == ' ' && builder.charAt(i + 1) == '\n') {
      builder.delete(i, i + 1);
      builderLength--;
    }
  }
  // 4. Trim a trailing newline, if there is one.
  if (builderLength > 0 && builder.charAt(builderLength - 1) == '\n') {
    builder.delete(builderLength - 1, builderLength);
    /*builderLength--;*/
  }
  return builder;
}
 
Example 17
Source File: TtmlNode.java    From Telegram-FOSS with GNU General Public License v2.0 4 votes vote down vote up
private SpannableStringBuilder cleanUpText(SpannableStringBuilder builder) {
  // Having joined the text elements, we need to do some final cleanup on the result.
  // 1. Collapse multiple consecutive spaces into a single space.
  int builderLength = builder.length();
  for (int i = 0; i < builderLength; i++) {
    if (builder.charAt(i) == ' ') {
      int j = i + 1;
      while (j < builder.length() && builder.charAt(j) == ' ') {
        j++;
      }
      int spacesToDelete = j - (i + 1);
      if (spacesToDelete > 0) {
        builder.delete(i, i + spacesToDelete);
        builderLength -= spacesToDelete;
      }
    }
  }
  // 2. Remove any spaces from the start of each line.
  if (builderLength > 0 && builder.charAt(0) == ' ') {
    builder.delete(0, 1);
    builderLength--;
  }
  for (int i = 0; i < builderLength - 1; i++) {
    if (builder.charAt(i) == '\n' && builder.charAt(i + 1) == ' ') {
      builder.delete(i + 1, i + 2);
      builderLength--;
    }
  }
  // 3. Remove any spaces from the end of each line.
  if (builderLength > 0 && builder.charAt(builderLength - 1) == ' ') {
    builder.delete(builderLength - 1, builderLength);
    builderLength--;
  }
  for (int i = 0; i < builderLength - 1; i++) {
    if (builder.charAt(i) == ' ' && builder.charAt(i + 1) == '\n') {
      builder.delete(i, i + 1);
      builderLength--;
    }
  }
  // 4. Trim a trailing newline, if there is one.
  if (builderLength > 0 && builder.charAt(builderLength - 1) == '\n') {
    builder.delete(builderLength - 1, builderLength);
    /*builderLength--;*/
  }
  return builder;
}
 
Example 18
Source File: TtmlNode.java    From TelePlus-Android with GNU General Public License v2.0 4 votes vote down vote up
private SpannableStringBuilder cleanUpText(SpannableStringBuilder builder) {
  // Having joined the text elements, we need to do some final cleanup on the result.
  // 1. Collapse multiple consecutive spaces into a single space.
  int builderLength = builder.length();
  for (int i = 0; i < builderLength; i++) {
    if (builder.charAt(i) == ' ') {
      int j = i + 1;
      while (j < builder.length() && builder.charAt(j) == ' ') {
        j++;
      }
      int spacesToDelete = j - (i + 1);
      if (spacesToDelete > 0) {
        builder.delete(i, i + spacesToDelete);
        builderLength -= spacesToDelete;
      }
    }
  }
  // 2. Remove any spaces from the start of each line.
  if (builderLength > 0 && builder.charAt(0) == ' ') {
    builder.delete(0, 1);
    builderLength--;
  }
  for (int i = 0; i < builderLength - 1; i++) {
    if (builder.charAt(i) == '\n' && builder.charAt(i + 1) == ' ') {
      builder.delete(i + 1, i + 2);
      builderLength--;
    }
  }
  // 3. Remove any spaces from the end of each line.
  if (builderLength > 0 && builder.charAt(builderLength - 1) == ' ') {
    builder.delete(builderLength - 1, builderLength);
    builderLength--;
  }
  for (int i = 0; i < builderLength - 1; i++) {
    if (builder.charAt(i) == ' ' && builder.charAt(i + 1) == '\n') {
      builder.delete(i, i + 1);
      builderLength--;
    }
  }
  // 4. Trim a trailing newline, if there is one.
  if (builderLength > 0 && builder.charAt(builderLength - 1) == '\n') {
    builder.delete(builderLength - 1, builderLength);
    /*builderLength--;*/
  }
  return builder;
}
 
Example 19
Source File: MessageAdapter.java    From Conversations with GNU General Public License v3.0 4 votes vote down vote up
/**
 * Applies QuoteSpan to group of lines which starts with > or » characters.
 * Appends likebreaks and applies DividerSpan to them to show a padding between quote and text.
 */
private boolean handleTextQuotes(SpannableStringBuilder body, boolean darkBackground) {
    boolean startsWithQuote = false;
    char previous = '\n';
    int lineStart = -1;
    int lineTextStart = -1;
    int quoteStart = -1;
    for (int i = 0; i <= body.length(); i++) {
        char current = body.length() > i ? body.charAt(i) : '\n';
        if (lineStart == -1) {
            if (previous == '\n') {
                if ((current == '>' && UIHelper.isPositionFollowedByQuoteableCharacter(body, i))
                        || current == '\u00bb' && !UIHelper.isPositionFollowedByQuote(body, i)) {
                    // Line start with quote
                    lineStart = i;
                    if (quoteStart == -1) quoteStart = i;
                    if (i == 0) startsWithQuote = true;
                } else if (quoteStart >= 0) {
                    // Line start without quote, apply spans there
                    applyQuoteSpan(body, quoteStart, i - 1, darkBackground);
                    quoteStart = -1;
                }
            }
        } else {
            // Remove extra spaces between > and first character in the line
            // > character will be removed too
            if (current != ' ' && lineTextStart == -1) {
                lineTextStart = i;
            }
            if (current == '\n') {
                body.delete(lineStart, lineTextStart);
                i -= lineTextStart - lineStart;
                if (i == lineStart) {
                    // Avoid empty lines because span over empty line can be hidden
                    body.insert(i++, " ");
                }
                lineStart = -1;
                lineTextStart = -1;
            }
        }
        previous = current;
    }
    if (quoteStart >= 0) {
        // Apply spans to finishing open quote
        applyQuoteSpan(body, quoteStart, body.length(), darkBackground);
    }
    return startsWithQuote;
}
 
Example 20
Source File: TtmlNode.java    From MediaSDK with Apache License 2.0 4 votes vote down vote up
private SpannableStringBuilder cleanUpText(SpannableStringBuilder builder) {
  // Having joined the text elements, we need to do some final cleanup on the result.
  // 1. Collapse multiple consecutive spaces into a single space.
  int builderLength = builder.length();
  for (int i = 0; i < builderLength; i++) {
    if (builder.charAt(i) == ' ') {
      int j = i + 1;
      while (j < builder.length() && builder.charAt(j) == ' ') {
        j++;
      }
      int spacesToDelete = j - (i + 1);
      if (spacesToDelete > 0) {
        builder.delete(i, i + spacesToDelete);
        builderLength -= spacesToDelete;
      }
    }
  }
  // 2. Remove any spaces from the start of each line.
  if (builderLength > 0 && builder.charAt(0) == ' ') {
    builder.delete(0, 1);
    builderLength--;
  }
  for (int i = 0; i < builderLength - 1; i++) {
    if (builder.charAt(i) == '\n' && builder.charAt(i + 1) == ' ') {
      builder.delete(i + 1, i + 2);
      builderLength--;
    }
  }
  // 3. Remove any spaces from the end of each line.
  if (builderLength > 0 && builder.charAt(builderLength - 1) == ' ') {
    builder.delete(builderLength - 1, builderLength);
    builderLength--;
  }
  for (int i = 0; i < builderLength - 1; i++) {
    if (builder.charAt(i) == ' ' && builder.charAt(i + 1) == '\n') {
      builder.delete(i, i + 1);
      builderLength--;
    }
  }
  // 4. Trim a trailing newline, if there is one.
  if (builderLength > 0 && builder.charAt(builderLength - 1) == '\n') {
    builder.delete(builderLength - 1, builderLength);
    /*builderLength--;*/
  }
  return builder;
}