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

laika.ast.templates.scala Maven / Gradle / Ivy

/*
 * Copyright 2012-2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package laika.ast

import laika.internal.rewrite.ReferenceResolver.CursorKeys
import laika.parse.SourceCursor

/** The base type for all inline elements that
  *  can be found in a template.
  */
trait TemplateSpan extends Span

/** A container of other TemplateSpan elements.
  */
trait TemplateSpanContainer extends ElementContainer[TemplateSpan] with RewritableContainer {

  type Self <: TemplateSpanContainer

  /** Rewrites all template span children of this container based on the specified rules.
    *
    * Concrete types are expected to support rewriting at least for all standard block, span and template span
    * elements they contain, plus optionally for any other elements that have custom support for rewriting.
    */
  def rewriteTemplateSpans(rules: RewriteRule[TemplateSpan]): Self = rewriteChildren(
    RewriteRules.forTemplates(rules)
  )

  def rewriteChildren(rules: RewriteRules): Self = withContent(rules.rewriteTemplateSpans(content))

  /** Creates a copy of this instance with the specified new content.
    *
    * Implementation note: This method exists to deal with the fact that there is no polymorphic copy method
    * and trades a small bit of boilerplate for avoiding the compile time hit of using shapeless for this.
    */
  def withContent(newContent: Seq[TemplateSpan]): Self

}

/** Common methods for simple template span containers (without additional parameters). */
trait TemplateSpanContainerCompanion {

  type ContainerType

  /** Creates an empty instance */
  def empty: ContainerType = createSpanContainer(Nil)

  /** Create an instance only containing a single TemplateString span */
  def apply(text: String, texts: String*): ContainerType = createSpanContainer(
    (text +: texts).map(TemplateString(_))
  )

  /** Create an instance containing a one or more spans */
  def apply(span: TemplateSpan, spans: TemplateSpan*): ContainerType = createSpanContainer(
    span +: spans.toList
  )

  protected def createSpanContainer(spans: Seq[TemplateSpan]): ContainerType

}

/** Wraps a generic element that otherwise could not be placed directly into
  *  a template document tree. Useful when custom tags which are placed inside
  *  a template produce non-template tree elements.
  */
case class TemplateElement(element: Element, indent: Int = 0, options: Options = Options.empty)
    extends TemplateSpan with ElementTraversal
    with RewritableContainer {
  type Self = TemplateElement

  def rewriteChildren(rules: RewriteRules): TemplateElement =
    copy(element = rules.rewriteElement(element))

  def withOptions(options: Options): TemplateElement = copy(options = options)
}

/** A generic container element containing a list of template spans. Can be used where a sequence
  *  of spans must be inserted in a place where a single element is required by the API.
  *  Usually renderers do not treat the container as a special element and render its children
  *  as s sub flow of the parent container.
  */
case class TemplateSpanSequence(content: Seq[TemplateSpan], options: Options = Options.empty)
    extends TemplateSpan with TemplateSpanContainer {
  type Self = TemplateSpanSequence
  def withContent(newContent: Seq[TemplateSpan]): TemplateSpanSequence = copy(content = newContent)
  def withOptions(options: Options): TemplateSpanSequence              = copy(options = options)
}

object TemplateSpanSequence extends TemplateSpanContainerCompanion {
  type ContainerType = TemplateSpanSequence

  /** Create an instance containing one or more spans, translating them to TemplateSpans */
  def adapt(spans: Seq[Span]): ContainerType = {
    val templateSpans = spans.map {
      case ts: TemplateSpan => ts
      case span             => TemplateElement(span)
    }
    createSpanContainer(templateSpans)
  }

  /** Create an instance containing one or more spans, translating them to TemplateSpans */
  def adapt(span: Span, spans: Span*): ContainerType = adapt(span +: spans.toList)

  protected def createSpanContainer(spans: Seq[TemplateSpan]): ContainerType = TemplateSpanSequence(
    spans
  )

}

/** A simple string element, representing the parts of a template
  *  that are not detected as special markup constructs and treated as raw text.
  */
case class TemplateString(content: String, options: Options = Options.empty) extends TemplateSpan
    with TextContainer {
  type Self = TemplateString
  def withOptions(options: Options): TemplateString = copy(options = options)
}

/** The root element of a template document tree.
  */
case class TemplateRoot(content: Seq[TemplateSpan], options: Options = Options.empty) extends Block
    with TemplateSpan with TemplateSpanContainer {
  type Self = TemplateRoot
  def withContent(newContent: Seq[TemplateSpan]): TemplateRoot = copy(content = newContent)
  def withOptions(options: Options): TemplateRoot              = copy(options = options)
}

/** Companion with a fallback instance for setups without a default template */
object TemplateRoot extends TemplateSpanContainerCompanion {

  type ContainerType = TemplateRoot
  protected def createSpanContainer(spans: Seq[TemplateSpan]): ContainerType = TemplateRoot(spans)

  /** A fallback instance that can be used when no user-specified template
    * is available. It simply inserts the content of the parsed markup document
    * without any surrounding decoration.
    */
  val fallback: TemplateRoot = TemplateRoot(
    TemplateContextReference(CursorKeys.documentContent, required = true, SourceCursor.Generated)
  )

}

/** The root element of a document tree (originating from text markup) inside a template.
  *  Usually created by a template reference like `\${cursor.currentDocument.content}`.
  */
case class EmbeddedRoot(content: Seq[Block], indent: Int = 0, options: Options = Options.empty)
    extends TemplateSpan with BlockContainer {
  type Self = EmbeddedRoot
  def withContent(newContent: Seq[Block]): EmbeddedRoot = copy(content = newContent)
  def withOptions(options: Options): EmbeddedRoot       = copy(options = options)
}

object EmbeddedRoot extends BlockContainerCompanion {
  type ContainerType = EmbeddedRoot
  override protected def createBlockContainer(blocks: Seq[Block]) = EmbeddedRoot(blocks)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy