Chunk Types

This is more of a reference to the typing of chunks.

// In com/tristanhunt/knockoff/Chunk.scala
package com.tristanhunt.knockoff

import scala.collection.mutable.ListBuffer
import scala.util.parsing.input.Position

trait Chunk {
  def content : String
  
  def isLinkDefinition = false

  /** Create the Block and append to the list. */
  def appendNewBlock( list : ListBuffer[ Block ],
                      remaining : List[ (Chunk, SpanSeq, Position) ],
                      spans : SpanSeq, position : Position )
                    ( elementFactory : ElementFactory, discounter : Discounter )
}

TextChunk

// In com/tristanhunt/knockoff/TextChunk.scala
package com.tristanhunt.knockoff

import scala.collection.mutable.ListBuffer
import scala.util.parsing.input.Position

/** Mostly, a Chunk that is not empty. */
case class TextChunk( val content : String ) extends Chunk {

  def appendNewBlock( list : ListBuffer[ Block ],
                      remaining : List[ (Chunk, SpanSeq, Position) ],
                      spans : SpanSeq,
                      position : Position )
                    ( elementFactory : ElementFactory, discounter : Discounter )
  : Unit = {
    list += elementFactory.para( elementFactory.toSpan( spans ), position )
  }
}

HorizontalRuleChunk

// In com/tristanhunt/knockoff/HorizontalRuleChunk.scala
package com.tristanhunt.knockoff

import scala.collection.mutable.ListBuffer
import scala.util.parsing.input.Position

case object HorizontalRuleChunk extends Chunk {
  val content = "* * *\n"
  
  def appendNewBlock( list : ListBuffer[ Block ],
                      remaining : List[ (Chunk, SpanSeq, Position) ],
                      spans : SpanSeq,
                      position : Position )
                    ( elementFactory : ElementFactory, discounter : Discounter )
  : Unit = {
    list += elementFactory.hr( position )
  }
}

EmptySpace

// In com/tristanhunt/knockoff/EmptySpace.scala
package com.tristanhunt.knockoff

import scala.collection.mutable.ListBuffer
import scala.util.parsing.input.Position

/** Note that this does not cover forced line breaks. */
case class EmptySpace( val content : String ) extends Chunk {

  /**
    Empty space only matters in cases where the lines are indented, which is a
    way of dealing with editors that like to do things like strip out whitespace
    at the end of a line.
  */
  def appendNewBlock( list : ListBuffer[ Block ],
                      remaining : List[ (Chunk, SpanSeq, Position) ],
                      spans : SpanSeq,
                      position : Position )
                    ( elementFactory : ElementFactory, discounter : Discounter )
  : Unit = {
    if ( remaining.isEmpty ) return
    if ( list.isEmpty ) return
    list.last match {
      case lastCB : CodeBlock => remaining.first._1 match {
        case ice : IndentedChunk =>
          list.update( list.length - 1,
                       elementFactory.codeBlock(
                         elementFactory.text( lastCB.text.content + "\n" ),
                         lastCB.position ) )
        case _ => {}
      }
      case _ => {}
    }
  }
}

BulletLineChunk

// In com/tristanhunt/knockoff/BulletLineChunk.scala
package com.tristanhunt.knockoff

import scala.collection.mutable.ListBuffer
import scala.util.parsing.input.Position

case class BulletLineChunk( val content : String ) extends Chunk {

  def appendNewBlock( list : ListBuffer[ Block ],
                      remaining : List[ (Chunk, SpanSeq, Position) ],
                      spans : SpanSeq,
                      position : Position )
                    ( elementFactory : ElementFactory, discounter : Discounter )
  : Unit = {
    val li = elementFactory.uli( elementFactory.para(spans, position),
                                 position )
    if ( list.isEmpty ) {
      list += elementFactory.ulist( li )
    } else {
      list.last match {
        case ul : UnorderedList => val appended = ul + li
                                   list.update( list.length - 1, appended )
        case _ => list += elementFactory.ulist( li )
      }
    }
  }
}

NumberedLineChunk

// In com/tristanhunt/knockoff/NumberedLineChunk.scala
package com.tristanhunt.knockoff

import scala.collection.mutable.ListBuffer
import scala.util.parsing.input.Position
    
case class NumberedLineChunk( val content : String ) extends Chunk {

  def appendNewBlock( list : ListBuffer[ Block ],
                      remaining : List[ (Chunk, SpanSeq, Position) ],
                      spans : SpanSeq,
                      position : Position )
                    ( elementFactory : ElementFactory, discounter : Discounter )
  : Unit = {
    val li = elementFactory.oli( elementFactory.para(spans, position),
                                 position )
    if ( list.isEmpty ) {
      list += elementFactory.olist( li )
    } else {
      list.last match {
        case ol : OrderedList => val appended = ol + li
                                 list.update( list.length - 1, appended )
        case _ => list += elementFactory.olist( li )
      }
    }
  }
}

HeaderChunk

// In com/tristanhunt/knockoff/HeaderChunk.scala
package com.tristanhunt.knockoff

import scala.collection.mutable.ListBuffer
import scala.util.parsing.input.Position

case class HeaderChunk( val level : Int, val content : String ) extends Chunk {

  def appendNewBlock( list : ListBuffer[ Block ],
                      remaining : List[ (Chunk, SpanSeq, Position) ],
                      spans : SpanSeq,
                      position : Position )
                    ( elementFactory : ElementFactory, discounter : Discounter )
  : Unit = {
    list += elementFactory.head( level, elementFactory.toSpan(spans), position )
  }
}

IndentedChunk

// In com/tristanhunt/knockoff/IndentedChunk.scala
package com.tristanhunt.knockoff

import scala.collection.mutable.ListBuffer
import scala.util.parsing.input.Position

/**
  This represents a group of lines that have at least 4 spaces/1 tab preceding
  the line.
*/
case class IndentedChunk( val content : String ) extends Chunk {
  /**
    If the block before is a list, we append this to the end of that list.
    Otherwise, append it as a new code block. Two code blocks will get combined
    here (because it's common to have an empty line not be indented in many
    editors). Appending to the end of a list means that we strip out the first
    indent and reparse things.
  */
  def appendNewBlock( list : ListBuffer[ Block ],
                      remaining : List[ (Chunk, SpanSeq, Position) ],
                      spans : SpanSeq,
                      position : Position )
                    ( elementFactory : ElementFactory, discounter : Discounter )
  : Unit = {
    if ( list.isEmpty ) {
      spans.first match {
        case text : Text => list += elementFactory.codeBlock( text, position )
      }
    } else {
      list.last match {
        case ml : MarkdownList =>
          val bs = discounter.knockoff( content )
          val updated = ( ml /: bs )( (ml, block) => ml + block )
          list.update( list.length - 1, updated )

        case cb : CodeBlock =>
          spans.first match {
            case text : Text =>
              list.update( list.length - 1,
                           elementFactory.codeBlock(
                             elementFactory.text(cb.text.content + text.content),
                             cb.position ) )

            case s : Span =>
              error( "Expected Text(code) for code block append, not " + s )
          }

        case _ =>
          spans.first match {
            case text : Text =>
              list += elementFactory.codeBlock( text, position )
            case s : Span =>
              error( "Expected Text(code) for code block addition, not " + s )
          }
      }
    }
  }
}

BlockquotedChunk

Represents a single level of blockquoted material. That means that it could also contain content, which is then reparsed, recursively.

// In com/tristanhunt/knockoff/BlockquotedChunk.scala
package com.tristanhunt.knockoff

import scala.collection.mutable.ListBuffer
import scala.util.parsing.input.Position

/**
  @param content The material, not parsed, but also not containing this level's
                 '>' characters.
*/
case class BlockquotedChunk( val content : String ) extends Chunk {

  def appendNewBlock( list : ListBuffer[ Block ],
                      remaining : List[ (Chunk, SpanSeq, Position) ],
                      spans : SpanSeq,
                      position : Position )
                    ( elementFactory : ElementFactory, discounter : Discounter )
  : Unit = {
    val blocks = discounter.knockoff( content )
    list += elementFactory.blockquote( blocks, position )
  }
}

LinkDefinitionChunk

// In com/tristanhunt/knockoff/LinkDefinitionChunk.scala
package com.tristanhunt.knockoff

import scala.collection.mutable.ListBuffer
import scala.util.parsing.input.Position

case class LinkDefinitionChunk( val id : String, val url : String,
                                val title : Option[ String ] )
extends Chunk {

  override def isLinkDefinition = true
  
  def content : String = "[" + id + "]: " + url + (
    title.map( " \"" + _ + "\"" ).getOrElse("")
  )
  
  def appendNewBlock( list : ListBuffer[ Block ],
                      remaining : List[ (Chunk, SpanSeq, Position) ],
                      spans : SpanSeq,
                      position : Position )
                    ( elementFactory : ElementFactory, discounter : Discounter )
  : Unit = {
    list += elementFactory.linkdef( id, url, title, position )
  }
}