All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dotty.tools.scaladoc.parsers.WikiCodeBlockParser.scala Maven / Gradle / Ivy

The newest version!
package dotty.tools.scaladoc.parsers

import com.vladsch.flexmark.ast._
import com.vladsch.flexmark.parser.Parser
import com.vladsch.flexmark.parser.core._
import com.vladsch.flexmark.parser.block._
import com.vladsch.flexmark.util.ast.Block
import com.vladsch.flexmark.util.ast.BlockContent
import com.vladsch.flexmark.util.data.DataHolder
import com.vladsch.flexmark.util.sequence.BasedSequence
import com.vladsch.flexmark.util.sequence.SegmentedSequence

import java.{util => ju}
import ju.regex.Matcher
import ju.regex.Pattern
import scala.jdk.CollectionConverters._


/** Copied from FencedCodeBlockParser. */
object WikiCodeBlockParser {
  private val OPENING_FENCE = Pattern.compile("^\\{{3}")
  private val CLOSING_FENCE = Pattern.compile("^(\\}{3})(?=[ \t]*$)$")

  class Factory extends CustomBlockParserFactory {
    override def apply(options: DataHolder): BlockParserFactory =
      new WikiCodeBlockParser.BlockFactory(options)

    override def getAfterDependents =
      new ju.HashSet[Class[?]](ju.Arrays.asList(
        classOf[BlockQuoteParser.Factory],
        classOf[HeadingParser.Factory],
        //FencedCodeBlockParser.Factory.class,
        //HtmlBlockParser.Factory.class,
        //ThematicBreakParser.Factory.class,
        //ListBlockParser.Factory.class,
        //IndentedCodeBlockParser.Factory.class
      ))

    override def getBeforeDependents =
      new ju.HashSet[Class[?]](ju.Arrays.asList(
        //BlockQuoteParser.Factory.class,
        //HeadingParser.Factory.class,
        //FencedCodeBlockParser.Factory.class,
        classOf[HtmlBlockParser.Factory],
        classOf[ThematicBreakParser.Factory],
        classOf[ListBlockParser.Factory],
        classOf[IndentedCodeBlockParser.Factory],
      ))

    override def affectsGlobalScope = false
  }

  private[WikiCodeBlockParser] class BlockFactory (val options: DataHolder)
  extends AbstractBlockParserFactory(options) {
    def tryStart(state: ParserState, matchedBlockParser: MatchedBlockParser): BlockStart = {
      val nextNonSpace = state.getNextNonSpaceIndex
      val line = state.getLine
      var matcher: Matcher = null
      if state.getIndent < 4 then {
        val trySequence = line.subSequence(nextNonSpace, line.length)
        matcher = OPENING_FENCE.matcher(trySequence)
        if matcher.find then {
          val fenceLength = matcher.group(0).length
          val blockParser =
            new WikiCodeBlockParser(state.getProperties, fenceLength, state.getIndent, nextNonSpace)
          blockParser.block.setOpeningMarker(trySequence.subSequence(0, fenceLength))
          return BlockStart.of(blockParser).atIndex(nextNonSpace + fenceLength)
        }
      }
      BlockStart.none
    }
  }
}

/** Copied from FencedCodeBlockParser. */
class WikiCodeBlockParser(
  options: DataHolder,
  var fenceLength: Int,
  private var fenceIndent: Int,
  private var fenceMarkerIndent: Int
) extends AbstractBlockParser {

  this.fenceMarkerIndent = fenceIndent + fenceMarkerIndent

  final private val block = new FencedCodeBlock()
  private var content = new BlockContent
  private val codeContentBlock = Parser.FENCED_CODE_CONTENT_BLOCK.get(options)

  def getBlock: Block = block
  def getFenceIndent: Int = fenceIndent
  def getFenceMarkerIndent: Int = fenceMarkerIndent
  def tryContinue(state: ParserState): BlockContinue = {
    val nextNonSpace = state.getNextNonSpaceIndex
    var newIndex = state.getIndex
    val line = state.getLine
    var matcher: Matcher = null
    val matches =
      state.getIndent <= 3
      && nextNonSpace < line.length

    if matches then {
      val trySequence = line.subSequence(nextNonSpace, line.length)
      matcher = WikiCodeBlockParser.CLOSING_FENCE.matcher(trySequence)
      if matcher.find then {
        val foundFenceLength = matcher.group(0).length
        if (foundFenceLength >= fenceLength) { // closing fence - we're at end of line, so we can finalize now
          block.setClosingMarker(trySequence.subSequence(0, foundFenceLength))
          return BlockContinue.finished
        }
      }
    }
    // skip optional spaces of fence indent
    var i = fenceIndent
    while ({
      i > 0 && newIndex < line.length && line.charAt(newIndex) == ' '
    }) do {
      newIndex += 1
      i -= 1
    }
    BlockContinue.atIndex(newIndex)
  }

  override def addLine(state: ParserState, line: BasedSequence): Unit = {
    content.add(line, state.getIndent)
  }

  override def isPropagatingLastBlankLine(lastMatchedBlockParser: BlockParser) = false

  override def closeBlock(state: ParserState): Unit = { // first line, if not blank, has the info string
    val lines = content.getLines
    if (lines.size > 0) {
      val info = lines.get(0)
      if (!info.isBlank) block.setInfo(info.trim)
      val chars = content.getSpanningChars
      val spanningChars = chars.baseSubSequence(chars.getStartOffset, lines.get(0).getEndOffset)
      if (lines.size > 1) { // have more lines
        val segments = lines.subList(1, lines.size)
        block.setContent(spanningChars, segments)
        if this.codeContentBlock then {
          val codeBlock = new CodeBlock()
          codeBlock.setContent(segments)
          codeBlock.setCharsFromContent
          block.appendChild(codeBlock)
        } else {
          val codeBlock = new Text(SegmentedSequence.create(segments.asScala.toSeq*))
          block.appendChild(codeBlock)
        }
      }
      else block.setContent(spanningChars, BasedSequence.EMPTY_LIST)
    }
    else block.setContent(content)
    block.setCharsFromContent
    content = null
  }
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy