org.commonmark.internal.util.Parsing Java Examples

The following examples show how to use org.commonmark.internal.util.Parsing. 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: DocumentParser.java    From commonmark-java with BSD 2-Clause "Simplified" License 6 votes vote down vote up
/**
 * Add line content to the active block parser. We assume it can accept lines -- that check should be done before
 * calling this.
 */
private void addLine() {
    CharSequence content;
    if (columnIsInTab) {
        // Our column is in a partially consumed tab. Expand the remaining columns (to the next tab stop) to spaces.
        int afterTab = index + 1;
        CharSequence rest = line.subSequence(afterTab, line.length());
        int spaces = Parsing.columnsToNextTabStop(column);
        StringBuilder sb = new StringBuilder(spaces + rest.length());
        for (int i = 0; i < spaces; i++) {
            sb.append(' ');
        }
        sb.append(rest);
        content = sb.toString();
    } else {
        content = line.subSequence(index, line.length());
    }
    getActiveBlockParser().addLine(content);
}
 
Example #2
Source File: HeadingParser.java    From commonmark-java with BSD 2-Clause "Simplified" License 6 votes vote down vote up
@Override
public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) {
    if (state.getIndent() >= Parsing.CODE_BLOCK_INDENT) {
        return BlockStart.none();
    }

    CharSequence line = state.getLine();
    int nextNonSpace = state.getNextNonSpaceIndex();
    HeadingParser atxHeading = getAtxHeading(line, nextNonSpace);
    if (atxHeading != null) {
        return BlockStart.of(atxHeading).atIndex(line.length());
    }

    int setextHeadingLevel = getSetextHeadingLevel(line, nextNonSpace);
    if (setextHeadingLevel > 0) {
        CharSequence paragraph = matchedBlockParser.getParagraphContent();
        if (paragraph != null) {
            String content = paragraph.toString();
            return BlockStart.of(new HeadingParser(setextHeadingLevel, content))
                    .atIndex(line.length())
                    .replaceActiveBlockParser();
        }
    }

    return BlockStart.none();
}
 
Example #3
Source File: IndentedCodeBlockParser.java    From commonmark-java with BSD 2-Clause "Simplified" License 6 votes vote down vote up
@Override
public void closeBlock() {
    int lastNonBlank = lines.size() - 1;
    while (lastNonBlank >= 0) {
        if (!Parsing.isBlank(lines.get(lastNonBlank))) {
            break;
        }
        lastNonBlank--;
    }

    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < lastNonBlank + 1; i++) {
        sb.append(lines.get(i));
        sb.append('\n');
    }

    String literal = sb.toString();
    block.setLiteral(literal);
}
 
Example #4
Source File: FencedCodeBlockParser.java    From commonmark-java with BSD 2-Clause "Simplified" License 6 votes vote down vote up
@Override
public BlockContinue tryContinue(ParserState state) {
    int nextNonSpace = state.getNextNonSpaceIndex();
    int newIndex = state.getIndex();
    CharSequence line = state.getLine();
    boolean closing = state.getIndent() < Parsing.CODE_BLOCK_INDENT && isClosing(line, nextNonSpace);
    if (closing) {
        // closing fence - we're at end of line, so we can finalize now
        return BlockContinue.finished();
    } else {
        // skip optional spaces of fence indent
        int i = block.getFenceIndent();
        int length = line.length();
        while (i > 0 && newIndex < length && line.charAt(newIndex) == ' ') {
            newIndex++;
            i--;
        }
    }
    return BlockContinue.atIndex(newIndex);
}
 
Example #5
Source File: NoticeBlockParser.java    From maven-confluence-plugin with Apache License 2.0 6 votes vote down vote up
@Override
public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) {
    final int nextNonSpace = state.getNextNonSpaceIndex();

    return isStartedMarker(state, nextNonSpace)
            .map( m -> {
                int newColumn = state.getColumn() + state.getIndent() + 1;
                // optional following space or tab

                if (Parsing.isSpaceOrTab(state.getLine(), nextNonSpace + 1)) {
                    newColumn++;
                }
                final NoticeBlock block =  new NoticeBlock( NoticeBlock.Type.fromString(m.group(1)), m.group(2)) ;

                final NoticeBlockParser parser = new NoticeBlockParser( block );

                return BlockStart.of(parser).atColumn(newColumn +m.end());
            })
            .orElseGet( () ->  BlockStart.none() );
}
 
Example #6
Source File: JLatexMathBlockParser.java    From Markwon with Apache License 2.0 6 votes vote down vote up
@Override
public BlockContinue tryContinue(ParserState parserState) {
    final int nextNonSpaceIndex = parserState.getNextNonSpaceIndex();
    final CharSequence line = parserState.getLine();
    final int length = line.length();

    // check for closing
    if (parserState.getIndent() < Parsing.CODE_BLOCK_INDENT) {
        if (consume(DOLLAR, line, nextNonSpaceIndex, length) == signs) {
            // okay, we have our number of signs
            // let's consume spaces until the end
            if (Parsing.skip(SPACE, line, nextNonSpaceIndex + signs, length) == length) {
                return BlockContinue.finished();
            }
        }
    }

    return BlockContinue.atIndex(parserState.getIndex());
}
 
Example #7
Source File: ParagraphParser.java    From 1Rramp-Android with MIT License 6 votes vote down vote up
public void closeBlock(ReferenceParser inlineParser) {
    String contentString = content.getString();
    boolean hasReferenceDefs = false;

    int pos;
    // try parsing the beginning as link reference definitions:
    while (contentString.length() > 3 && contentString.charAt(0) == '[' &&
            (pos = inlineParser.parseReference(contentString)) != 0) {
        contentString = contentString.substring(pos);
        hasReferenceDefs = true;
    }
    if (hasReferenceDefs && Parsing.isBlank(contentString)) {
        block.unlink();
        content = null;
    } else {
        content = new BlockContent(contentString);
    }
}
 
Example #8
Source File: LinkReferenceDefinitionParser.java    From commonmark-java with BSD 2-Clause "Simplified" License 6 votes vote down vote up
private int startDefinition(CharSequence line, int i) {
    i = Parsing.skipSpaceTab(line, i, line.length());
    if (i >= line.length() || line.charAt(i) != '[') {
        return -1;
    }

    state = State.LABEL;
    label = new StringBuilder();

    int labelStart = i + 1;
    if (labelStart >= line.length()) {
        label.append('\n');
    }

    return labelStart;
}
 
Example #9
Source File: DocumentParser.java    From 1Rramp-Android with MIT License 6 votes vote down vote up
/**
 * Add line content to the active block parser. We assume it can accept lines -- that check should be done before
 * calling this.
 */
private void addLine() {
    CharSequence content;
    if (columnIsInTab) {
        // Our column is in a partially consumed tab. Expand the remaining columns (to the next tab stop) to spaces.
        int afterTab = index + 1;
        CharSequence rest = line.subSequence(afterTab, line.length());
        int spaces = Parsing.columnsToNextTabStop(column);
        StringBuilder sb = new StringBuilder(spaces + rest.length());
        for (int i = 0; i < spaces; i++) {
            sb.append(' ');
        }
        sb.append(rest);
        content = sb.toString();
    } else {
        content = line.subSequence(index, line.length());
    }
    getActiveBlockParser().addLine(content);
}
 
Example #10
Source File: DocumentParser.java    From 1Rramp-Android with MIT License 6 votes vote down vote up
/**
 * The main parsing function. Returns a parsed document AST.
 */
public Document parse(String input) {
    int lineStart = 0;
    int lineBreak;
    while ((lineBreak = Parsing.findLineBreak(input, lineStart)) != -1) {
        CharSequence line = Substring.of(input, lineStart, lineBreak);
        incorporateLine(line);
        if (lineBreak + 1 < input.length() && input.charAt(lineBreak) == '\r' && input.charAt(lineBreak + 1) == '\n') {
            lineStart = lineBreak + 2;
        } else {
            lineStart = lineBreak + 1;
        }
    }
    if (input.length() > 0 && (lineStart == 0 || lineStart < input.length())) {
        incorporateLine(Substring.of(input, lineStart, input.length()));
    }
    return finalizeAndProcess();
}
 
Example #11
Source File: LinkReferenceDefinitionParser.java    From commonmark-java with BSD 2-Clause "Simplified" License 6 votes vote down vote up
private int destination(CharSequence line, int i) {
    i = Parsing.skipSpaceTab(line, i, line.length());
    int afterDestination = LinkScanner.scanLinkDestination(line, i);
    if (afterDestination == -1) {
        return -1;
    }

    destination = (line.charAt(i) == '<')
            ? line.subSequence(i + 1, afterDestination - 1).toString()
            : line.subSequence(i, afterDestination).toString();

    int afterSpace = Parsing.skipSpaceTab(line, afterDestination, line.length());
    if (afterSpace >= line.length()) {
        // Destination was at end of line, so this is a valid reference for sure (and maybe a title).
        // If not at end of line, wait for title to be valid first.
        referenceValid = true;
        paragraph.setLength(0);
    } else if (afterSpace == afterDestination) {
        // spec: The title must be separated from the link destination by whitespace
        return -1;
    }

    state = State.START_TITLE;
    return afterSpace;
}
 
Example #12
Source File: BlockQuoteParser.java    From commonmark-java with BSD 2-Clause "Simplified" License 5 votes vote down vote up
public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) {
    int nextNonSpace = state.getNextNonSpaceIndex();
    if (isMarker(state, nextNonSpace)) {
        int newColumn = state.getColumn() + state.getIndent() + 1;
        // optional following space or tab
        if (Parsing.isSpaceOrTab(state.getLine(), nextNonSpace + 1)) {
            newColumn++;
        }
        return BlockStart.of(new BlockQuoteParser()).atColumn(newColumn);
    } else {
        return BlockStart.none();
    }
}
 
Example #13
Source File: BlockQuoteParser.java    From commonmark-java with BSD 2-Clause "Simplified" License 5 votes vote down vote up
@Override
public BlockContinue tryContinue(ParserState state) {
    int nextNonSpace = state.getNextNonSpaceIndex();
    if (isMarker(state, nextNonSpace)) {
        int newColumn = state.getColumn() + state.getIndent() + 1;
        // optional following space or tab
        if (Parsing.isSpaceOrTab(state.getLine(), nextNonSpace + 1)) {
            newColumn++;
        }
        return BlockContinue.atColumn(newColumn);
    } else {
        return BlockContinue.none();
    }
}
 
Example #14
Source File: IndentedCodeBlockParser.java    From commonmark-java with BSD 2-Clause "Simplified" License 5 votes vote down vote up
@Override
public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) {
    // An indented code block cannot interrupt a paragraph.
    if (state.getIndent() >= Parsing.CODE_BLOCK_INDENT && !state.isBlank() && !(state.getActiveBlockParser().getBlock() instanceof Paragraph)) {
        return BlockStart.of(new IndentedCodeBlockParser()).atColumn(state.getColumn() + Parsing.CODE_BLOCK_INDENT);
    } else {
        return BlockStart.none();
    }
}
 
Example #15
Source File: LinkReferenceDefinitionParser.java    From commonmark-java with BSD 2-Clause "Simplified" License 5 votes vote down vote up
private int label(CharSequence line, int i) {
    int afterLabel = LinkScanner.scanLinkLabelContent(line, i);
    if (afterLabel == -1) {
        return -1;
    }

    label.append(line, i, afterLabel);

    if (afterLabel >= line.length()) {
        // label might continue on next line
        label.append('\n');
        return afterLabel;
    } else if (line.charAt(afterLabel) == ']') {
        int colon = afterLabel + 1;
        // end of label
        if (colon >= line.length() || line.charAt(colon) != ':') {
            return -1;
        }

        // spec: A link label can have at most 999 characters inside the square brackets.
        if (label.length() > 999) {
            return -1;
        }

        String normalizedLabel = Escaping.normalizeLabelContent(label.toString());
        if (normalizedLabel.isEmpty()) {
            return -1;
        }

        this.normalizedLabel = normalizedLabel;
        state = State.DESTINATION;

        return Parsing.skipSpaceTab(line, colon + 1, line.length());
    } else {
        return -1;
    }
}
 
Example #16
Source File: DocumentParser.java    From commonmark-java with BSD 2-Clause "Simplified" License 5 votes vote down vote up
private void advance() {
    char c = line.charAt(index);
    if (c == '\t') {
        index++;
        column += Parsing.columnsToNextTabStop(column);
    } else {
        index++;
        column++;
    }
}
 
Example #17
Source File: ListBlockParser.java    From commonmark-java with BSD 2-Clause "Simplified" License 5 votes vote down vote up
@Override
public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) {
    BlockParser matched = matchedBlockParser.getMatchedBlockParser();

    if (state.getIndent() >= Parsing.CODE_BLOCK_INDENT) {
        return BlockStart.none();
    }
    int markerIndex = state.getNextNonSpaceIndex();
    int markerColumn = state.getColumn() + state.getIndent();
    boolean inParagraph = matchedBlockParser.getParagraphContent() != null;
    ListData listData = parseList(state.getLine(), markerIndex, markerColumn, inParagraph);
    if (listData == null) {
        return BlockStart.none();
    }

    int newColumn = listData.contentColumn;
    ListItemParser listItemParser = new ListItemParser(newColumn - state.getColumn());

    // prepend the list block if needed
    if (!(matched instanceof ListBlockParser) ||
            !(listsMatch((ListBlock) matched.getBlock(), listData.listBlock))) {

        ListBlockParser listBlockParser = new ListBlockParser(listData.listBlock);
        // We start out with assuming a list is tight. If we find a blank line, we set it to loose later.
        listData.listBlock.setTight(true);

        return BlockStart.of(listBlockParser, listItemParser).atColumn(newColumn);
    } else {
        return BlockStart.of(listItemParser).atColumn(newColumn);
    }
}
 
Example #18
Source File: FencedCodeBlockParser.java    From commonmark-java with BSD 2-Clause "Simplified" License 5 votes vote down vote up
private boolean isClosing(CharSequence line, int index) {
    char fenceChar = block.getFenceChar();
    int fenceLength = block.getFenceLength();
    int fences = Parsing.skip(fenceChar, line, index, line.length()) - index;
    if (fences < fenceLength) {
        return false;
    }
    // spec: The closing code fence [...] may be followed only by spaces, which are ignored.
    int after = Parsing.skipSpaceTab(line, index + fences, line.length());
    return after == line.length();
}
 
Example #19
Source File: FencedCodeBlockParser.java    From commonmark-java with BSD 2-Clause "Simplified" License 5 votes vote down vote up
private static FencedCodeBlockParser checkOpener(CharSequence line, int index, int indent) {
    int backticks = 0;
    int tildes = 0;
    int length = line.length();
    loop:
    for (int i = index; i < length; i++) {
        switch (line.charAt(i)) {
            case '`':
                backticks++;
                break;
            case '~':
                tildes++;
                break;
            default:
                break loop;
        }
    }
    if (backticks >= 3 && tildes == 0) {
        // spec: If the info string comes after a backtick fence, it may not contain any backtick characters.
        if (Parsing.find('`', line, index + backticks) != -1) {
            return null;
        }
        return new FencedCodeBlockParser('`', backticks, indent);
    } else if (tildes >= 3 && backticks == 0) {
        // spec: Info strings for tilde code blocks can contain backticks and tildes
        return new FencedCodeBlockParser('~', tildes, indent);
    } else {
        return null;
    }
}
 
Example #20
Source File: FencedCodeBlockParser.java    From commonmark-java with BSD 2-Clause "Simplified" License 5 votes vote down vote up
@Override
public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) {
    int indent = state.getIndent();
    if (indent >= Parsing.CODE_BLOCK_INDENT) {
        return BlockStart.none();
    }

    int nextNonSpace = state.getNextNonSpaceIndex();
    FencedCodeBlockParser blockParser = checkOpener(state.getLine(), nextNonSpace, indent);
    if (blockParser != null) {
        return BlockStart.of(blockParser).atIndex(nextNonSpace + blockParser.block.getFenceLength());
    } else {
        return BlockStart.none();
    }
}
 
Example #21
Source File: LinkReferenceDefinitionParser.java    From commonmark-java with BSD 2-Clause "Simplified" License 5 votes vote down vote up
private int startTitle(CharSequence line, int i) {
    i = Parsing.skipSpaceTab(line, i, line.length());
    if (i >= line.length()) {
        state = State.START_DEFINITION;
        return i;
    }

    titleDelimiter = '\0';
    char c = line.charAt(i);
    switch (c) {
        case '"':
        case '\'':
            titleDelimiter = c;
            break;
        case '(':
            titleDelimiter = ')';
            break;
    }

    if (titleDelimiter != '\0') {
        state = State.TITLE;
        title = new StringBuilder();
        i++;
        if (i == line.length()) {
            title.append('\n');
        }
    } else {
        finishReference();
        // There might be another reference instead, try that for the same character.
        state = State.START_DEFINITION;
    }
    return i;
}
 
Example #22
Source File: HeadingParser.java    From commonmark-java with BSD 2-Clause "Simplified" License 5 votes vote down vote up
private static HeadingParser getAtxHeading(CharSequence line, int index) {
    int level = Parsing.skip('#', line, index, line.length()) - index;

    if (level == 0 || level > 6) {
        return null;
    }

    int start = index + level;
    if (start >= line.length()) {
        // End of line after markers is an empty heading
        return new HeadingParser(level, "");
    }

    char next = line.charAt(start);
    if (!(next == ' ' || next == '\t')) {
        return null;
    }

    int beforeSpace = Parsing.skipSpaceTabBackwards(line, line.length() - 1, start);
    int beforeHash = Parsing.skipBackwards('#', line, beforeSpace, start);
    int beforeTrailer = Parsing.skipSpaceTabBackwards(line, beforeHash, start);
    if (beforeTrailer != beforeHash) {
        return new HeadingParser(level, line.subSequence(start, beforeTrailer + 1).toString());
    } else {
        return new HeadingParser(level, line.subSequence(start, beforeSpace + 1).toString());
    }
}
 
Example #23
Source File: LinkReferenceDefinitionParser.java    From commonmark-java with BSD 2-Clause "Simplified" License 5 votes vote down vote up
private int title(CharSequence line, int i) {
    int afterTitle = LinkScanner.scanLinkTitleContent(line, i, titleDelimiter);
    if (afterTitle == -1) {
        // Invalid title, stop
        return -1;
    }

    title.append(line.subSequence(i, afterTitle));

    if (afterTitle >= line.length()) {
        // Title still going, continue on next line
        title.append('\n');
        return afterTitle;
    }

    int afterTitleDelimiter = afterTitle + 1;
    int afterSpace = Parsing.skipSpaceTab(line, afterTitleDelimiter, line.length());
    if (afterSpace != line.length()) {
        // spec: No further non-whitespace characters may occur on the line.
        return -1;
    }
    referenceValid = true;
    finishReference();
    paragraph.setLength(0);

    // See if there's another definition.
    state = State.START_DEFINITION;
    return afterSpace;
}
 
Example #24
Source File: JLatexMathBlockParser.java    From Markwon with Apache License 2.0 5 votes vote down vote up
@Override
public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) {

    // let's define the spec:
    //  * 0-3 spaces before are allowed (Parsing.CODE_BLOCK_INDENT = 4)
    //  * 2+ subsequent `$` signs
    //  * any optional amount of spaces
    //  * new line
    //  * block is closed when the same amount of opening signs is met

    final int indent = state.getIndent();

    // check if it's an indented code block
    if (indent >= Parsing.CODE_BLOCK_INDENT) {
        return BlockStart.none();
    }

    final int nextNonSpaceIndex = state.getNextNonSpaceIndex();
    final CharSequence line = state.getLine();
    final int length = line.length();

    final int signs = consume(DOLLAR, line, nextNonSpaceIndex, length);

    // 2 is minimum
    if (signs < 2) {
        return BlockStart.none();
    }

    // consume spaces until the end of the line, if any other content is found -> NONE
    if (Parsing.skip(SPACE, line, nextNonSpaceIndex + signs, length) != length) {
        return BlockStart.none();
    }

    return BlockStart.of(new JLatexMathBlockParser(signs))
            .atIndex(length + 1);
}
 
Example #25
Source File: IndentedCodeBlockParser.java    From 1Rramp-Android with MIT License 5 votes vote down vote up
@Override
public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) {
    // An indented code block cannot interrupt a paragraph.
    if (state.getIndent() >= Parsing.CODE_BLOCK_INDENT && !state.isBlank() && !(state.getActiveBlockParser().getBlock() instanceof Paragraph)) {
        return BlockStart.of(new IndentedCodeBlockParser()).atColumn(state.getColumn() + Parsing.CODE_BLOCK_INDENT);
    } else {
        return BlockStart.none();
    }
}
 
Example #26
Source File: ListBlockParser.java    From 1Rramp-Android with MIT License 5 votes vote down vote up
@Override
public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) {
    BlockParser matched = matchedBlockParser.getMatchedBlockParser();

    if (state.getIndent() >= Parsing.CODE_BLOCK_INDENT && !(matched instanceof ListBlockParser)) {
        return BlockStart.none();
    }
    int markerIndex = state.getNextNonSpaceIndex();
    int markerColumn = state.getColumn() + state.getIndent();
    boolean inParagraph = matchedBlockParser.getParagraphContent() != null;
    ListData listData = parseListMarker(state.getLine(), markerIndex, markerColumn, inParagraph);
    if (listData == null) {
        return BlockStart.none();
    }

    int newColumn = listData.contentColumn;
    ListItemParser listItemParser = new ListItemParser(newColumn - state.getColumn());

    // prepend the list block if needed
    if (!(matched instanceof ListBlockParser) ||
            !(listsMatch((ListBlock) matched.getBlock(), listData.listBlock))) {

        ListBlockParser listBlockParser = new ListBlockParser(listData.listBlock);
        listBlockParser.setTight(true);

        return BlockStart.of(listBlockParser, listItemParser).atColumn(newColumn);
    } else {
        return BlockStart.of(listItemParser).atColumn(newColumn);
    }
}
 
Example #27
Source File: DocumentParser.java    From 1Rramp-Android with MIT License 5 votes vote down vote up
private void advance() {
    char c = line.charAt(index);
    if (c == '\t') {
        index++;
        column += Parsing.columnsToNextTabStop(column);
    } else {
        index++;
        column++;
    }
}
 
Example #28
Source File: BlockQuoteParser.java    From 1Rramp-Android with MIT License 5 votes vote down vote up
@Override
public BlockContinue tryContinue(ParserState state) {
    int nextNonSpace = state.getNextNonSpaceIndex();
    if (isMarker(state, nextNonSpace)) {
        int newColumn = state.getColumn() + state.getIndent() + 1;
        // optional following space or tab
        if (Parsing.isSpaceOrTab(state.getLine(), nextNonSpace + 1)) {
            newColumn++;
        }
        return BlockContinue.atColumn(newColumn);
    } else {
        return BlockContinue.none();
    }
}
 
Example #29
Source File: BlockQuoteParser.java    From 1Rramp-Android with MIT License 5 votes vote down vote up
public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) {
    int nextNonSpace = state.getNextNonSpaceIndex();
    if (isMarker(state, nextNonSpace)) {
        int newColumn = state.getColumn() + state.getIndent() + 1;
        // optional following space or tab
        if (Parsing.isSpaceOrTab(state.getLine(), nextNonSpace + 1)) {
            newColumn++;
        }
        return BlockStart.of(new BlockQuoteParser()).atColumn(newColumn);
    } else {
        return BlockStart.none();
    }
}
 
Example #30
Source File: BackticksInlineProcessor.java    From Markwon with Apache License 2.0 5 votes vote down vote up
@Override
protected Node parse() {
    String ticks = match(TICKS_HERE);
    if (ticks == null) {
        return null;
    }
    int afterOpenTicks = index;
    String matched;
    while ((matched = match(TICKS)) != null) {
        if (matched.equals(ticks)) {
            Code node = new Code();
            String content = input.substring(afterOpenTicks, index - ticks.length());
            content = content.replace('\n', ' ');

            // spec: If the resulting string both begins and ends with a space character, but does not consist
            // entirely of space characters, a single space character is removed from the front and back.
            if (content.length() >= 3 &&
                    content.charAt(0) == ' ' &&
                    content.charAt(content.length() - 1) == ' ' &&
                    Parsing.hasNonSpace(content)) {
                content = content.substring(1, content.length() - 1);
            }

            node.setLiteral(content);
            return node;
        }
    }
    // If we got here, we didn't match a closing backtick sequence.
    index = afterOpenTicks;
    return text(ticks);
}