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

mdoc.internal.markdown.FenceInput.scala Maven / Gradle / Ivy

The newest version!
package mdoc.internal.markdown

import com.vladsch.flexmark.ast.FencedCodeBlock
import scala.meta.inputs.Input
import scala.meta.inputs.Position
import mdoc.internal.cli.Context
import mdoc.internal.markdown.Modifier.Str
import mdoc.internal.markdown.Modifier.Default
import mdoc.internal.markdown.Modifier.Post
import mdoc.internal.markdown.Modifier.Pre
import mdoc.parser.{CodeFence, Text}

case class PreFenceInput(block: CodeFence, input: Input, mod: Pre)
case class StringFenceInput(block: CodeFence, input: Input, mod: Str)
case class ScalaFenceInput(block: CodeFence, input: Input, mod: Modifier)

class FenceInput(ctx: Context, baseInput: Input) {
  def getModifier(block: CodeFence): Option[Modifier] = {
    block.getMdocMode.flatMap { mode =>
      if (mode.isEmpty) Some(Modifier.Default())
      else {
        Modifier(mode)
          .orElse {
            val (name, info) = mode.split(":", 2) match {
              case Array(a) => (a, "")
              case Array(a, b) => (a, b)
            }
            ctx.settings.stringModifiers
              .collectFirst[Modifier] {
                case mod if mod.name == name =>
                  Str(mod, info)
              }
              .orElse {
                ctx.settings.postModifiers.collectFirst {
                  case mod if mod.name == name =>
                    Post(mod, info)
                }
              }
              .orElse {
                ctx.settings.preModifiers.collectFirst {
                  case mod if mod.name == name =>
                    Pre(mod, info)
                }
              }
          }
          .orElse {
            invalid(block.info, s"Invalid mode '$mode'")
            None
          }
      }
    }
  }

  private def invalid(info: Text, message: String): Unit = {
    val offset = CodeFence.taglen + 1 // includes colon
    val start = info.posBeg + offset
    val end = info.posEnd - 1
    val pos = Position.Range(baseInput, start, end)
    ctx.reporter.error(pos, message)
  }
  private def invalidCombination(info: Text, mod1: String, mod2: String): Boolean = {
    invalid(info, s"invalid combination of modifiers '$mod1' and '$mod2'")
    false
  }

  private def isValid(info: Text, mod: Modifier): Boolean = {
    if (mod.isFailOrWarn && mod.isCrash) {
      invalidCombination(info, "crash", "fail")
    } else if (mod.isSilent && mod.isInvisible) {
      invalidCombination(info, "silent", "invisible")
    } else if (mod.isReset && mod.isNest) {
      invalid(
        info,
        "the modifier 'nest' is redundant when used in combination with 'reset'. " +
          "To fix this error, remove 'nest'"
      )
      false
    } else if (mod.isCompileOnly) {
      val others = mod.mods - Mod.CompileOnly
      if (others.isEmpty) {
        true
      } else {
        val all = others.map(_.toString.toLowerCase).mkString(", ")
        invalid(
          info,
          s"""compile-only cannot be used in combination with $all"""
        )
        false
      }
    } else {
      true
    }
  }
  def unapply(block: CodeFence): Option[ScalaFenceInput] = {
    getModifier(block) match {
      case Some(mod) =>
        if (isValid(block.info, mod)) {
          val input = Input.Slice(baseInput, block.body.posBeg, block.body.posEnd)
          Some(ScalaFenceInput(block, input, mod))
        } else {
          None
        }
      case _ => None
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy