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

laika.io.api.TreeParser.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.io.api

import cats.data.NonEmptyList
import cats.effect.{Async, Resource}
import laika.api.MarkupParser
import laika.api.builder.{OperationConfig, ParserBuilder}
import laika.ast.{DocumentType, StyleDeclarationSet, TemplateDocument, TextDocumentType}
import laika.io.descriptor.ParserDescriptor
import laika.io.model.{InputTreeBuilder, ParsedTree}
import laika.io.ops.InputOps
import laika.io.runtime.{Batch, ParserRuntime}
import laika.parse.markup.DocumentParser
import laika.parse.markup.DocumentParser.{DocumentInput, ParserError}
import laika.theme.{Theme, ThemeProvider}

/** Parser for a tree of input documents.
  *
  * @author Jens Halm
  */
class TreeParser[F[_]: Async: Batch] (parsers: NonEmptyList[MarkupParser], val theme: Theme[F]) extends InputOps[F] {

  type Result = TreeParser.Op[F]

  val F: Async[F] = Async[F]

  val docType: TextDocumentType = DocumentType.Markup

  lazy val config: OperationConfig = parsers
    .map(_.config)
    .reduceLeft[OperationConfig](_ merge _)
    .withBundles(theme.extensions)
  
  private[laika] def modifyConfig (f: OperationConfig => OperationConfig): TreeParser[F] = {
    val modifiedParsers = parsers.map(p => new MarkupParser(p.format, f(p.config)))
    new TreeParser(modifiedParsers, theme)
  }

  def fromInput (input: InputTreeBuilder[F]): TreeParser.Op[F] = TreeParser.Op(parsers, theme, input)

}

/** Builder API for constructing a parsing operation for a tree of input documents.
  */
object TreeParser {

  /** Builder step that allows to specify the execution context
    * for blocking IO and CPU-bound tasks.
    */
  case class Builder[F[_]: Async: Batch] (parsers: NonEmptyList[MarkupParser], theme: ThemeProvider) {

    /** Specifies an additional parser for text markup.
      * 
      * When multiple parsers exist for an operation, the target parser
      * will be determined by the suffix of the input document, e.g.
      * `.md` for Markdown and `.rst` for reStructuredText.
      */
    def withAlternativeParser (parser: MarkupParser): Builder[F] = copy(parsers = parsers.append(parser))

    /** Specifies an additional parser for text markup.
      *
      * When multiple parsers exist for an operation, the target parser
      * will be determined by the suffix of the input document, e.g.
      * `.md` for Markdown and `.rst` for reStructuredText.
      */
    def withAlternativeParser (parser: ParserBuilder): Builder[F] = copy(parsers = parsers.append(parser.build))

    /** Applies the specified theme to this parser, overriding any previously specified themes.
      */
    def withTheme (theme: ThemeProvider): Builder[F] = copy(theme = theme)

    /** Final builder step that creates a parallel parser.
      */
    def build: Resource[F, TreeParser[F]] = theme.build.map(new TreeParser[F](parsers, _))

  }

  /** Represents a parsing operation for a tree of input documents.
    *
    * It can be run by invoking the `parse` method which delegates to the library's
    * default runtime implementation or by developing a custom runner that performs
    * the parsing based on this operation's properties.
    */
  case class Op[F[_]: Async: Batch] (parsers: NonEmptyList[MarkupParser], theme: Theme[F], input: InputTreeBuilder[F]) {

    /** The merged configuration of all markup parsers of this operation, including the theme extensions.
      */
    lazy val config: OperationConfig = parsers
      .map(_.config)
      .reduceLeft[OperationConfig](_ merge _)
      .withBundles(theme.extensions)

    /** The template parser for this operation. If this property is empty
      * templating is not supported for this operation.
      */
    lazy val templateParser: Option[DocumentInput => Either[ParserError, TemplateDocument]] = config.templateParser map { rootParser =>
      DocumentParser.forTemplate(rootParser, config.configProvider)
    }

    /** The parser for CSS documents for this operation. Currently CSS input
      * will only be parsed for PDF output, in case of HTML or EPUB formats
      * CSS documents will merely copied to the target format.
      */
    lazy val styleSheetParser: DocumentInput => Either[ParserError, StyleDeclarationSet] =
      DocumentParser.forStyleSheets(config.styleSheetParser)

    /** Performs the parsing operation based on the library's
      * default runtime implementation, suspended in the effect F.
      */
    def parse: F[ParsedTree[F]] = ParserRuntime.run(this)

    /** Provides a description of this operation, the parsers
      * and extension bundles used, as well as the input sources.
      * This functionality is mostly intended for tooling support.
      */
    def describe: F[ParserDescriptor] = ParserDescriptor.create(this)

  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy