//
// Copyright (c) 2024, Brian Frank and Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
// 08 Oct 2024 Matthew Giannini Creation
//
**
** A block node.
**
** We can think of a document as a sequence of blocks - structural
** elements like paragraphs, block quotations, lists, headings, rules,
** and code blocks. Some blocks contain other blocks; others contain
** inline content (text, images, code spans, etc.).
**
@Js
abstract class Block : Node
{
override Block? parent() { super.parent }
protected override Void setParent(Node? p)
{
if (p isnot Block)
throw ArgErr("Parent of block must also be a block")
super.setParent(p)
}
}
**************************************************************************
** Document
**************************************************************************
** Document is the root node of the AST
@Js
final class Document : Block { }
**************************************************************************
** Heading
**************************************************************************
** Heading block
@Js
class Heading : Block
{
new make(Int level := 0) { this.level = level }
** The heading "level"
const Int level
}
**************************************************************************
** BlockQuote
**************************************************************************
** Block quote block
@Js
class BlockQuote : Block { }
**************************************************************************
** FencedCode
**************************************************************************
** Fenced code block
@Js
class FencedCode : Block
{
new make(Str? fenceChar := null)
{
this.fenceChar = fenceChar
}
** The fence character that was used, e.g. '`', or '~', if available, or null otherwise
Str? fenceChar
Int fenceIndent := 0
** The length of the opening fence (how many of the `fenceChar` were used to start
** the code block) if available, or null otherwise
Int? openingFenceLen
{
set {
if (it != null && it < 3) throw ArgErr("openingFenceLen needs to be >= 3")
checkFenceLens(it, closingFenceLen)
&openingFenceLen = it
}
}
** The length of the closing fence (how many of the `fenceChar` were used to end
** the code block) if available, or null otherwise
Int? closingFenceLen
{
set {
if (it != null && it < 3) throw ArgErr("closingFenceLen needs to be >= 3")
checkFenceLens(openingFenceLen, it)
&closingFenceLen = it
}
}
** Optional info string (see spec), e.g. 'fantom' in '```fantom'
Str? info
Str? literal
private static Void checkFenceLens(Int? openingFenceLen, Int? closingFenceLen)
{
if (openingFenceLen != null && closingFenceLen != null)
{
if (closingFenceLen < openingFenceLen)
throw ArgErr("fence lengths required to be: closingFenceLen >= openingFenceLen")
}
}
}
**************************************************************************
** HtmlBlock
**************************************************************************
** HTML block
@Js
class HtmlBlock : Block
{
new make(Str? literal := null) { this.literal = literal }
Str? literal
}
**************************************************************************
** ThematicBreak
**************************************************************************
** Thematic break block
@Js
class ThematicBreak : Block
{
new make(Str? literal := null) { this.literal = literal }
** source literal that represents this break, if available
Str? literal
}
**************************************************************************
** IndentedCode
**************************************************************************
** Indented code block
@Js
class IndentedCode : Block
{
new make(Str? literal := null) { this.literal = literal }
** Indented code literal
Str? literal
}
** Abstract base class for list blocks
@Js
abstract class ListBlock : Block
{
** Whether this list is tight or loose
**
** spec: A list is loose if any of its constituent list items are separated by blank
** lines, or if any of its constituent list items directly contain two block-level
** elements with a blank line between them. Otherwise, a list is tight.
** (The difference in HTML output is that paragraphs in a loose list are
** wrapped in <p> tags, while paragraphs in a tight list are not.)
Bool tight := false
}
**************************************************************************
** BulletList
**************************************************************************
** Bullet list block
@Js
class BulletList : ListBlock
{
new make(Str? marker := null) { this.marker = marker }
** The bullet list marker that was used, e.g. '-', '*', or '+', if available,
** or null otherwise.
Str? marker
}
**************************************************************************
** OrderedList
**************************************************************************
** Ordered list block
@Js
class OrderedList : ListBlock
{
new make(Int? startNumber, Str? markerDelim)
{
this.startNumber = startNumber
this.markerDelim = markerDelim
}
** The start number used in the marker, e.g. '1', if available, or null otherwise
Int? startNumber
** The delimiter used in the marker, e.g. '.' or ')', if available, or null otherwise
Str? markerDelim
}
**************************************************************************
** ListItem
**************************************************************************
** List item
@Js
class ListItem : Block
{
new make(Int? markerIndent, Int? contentIndent)
{
this.markerIndent = markerIndent
this.contentIndent = contentIndent
}
** The indent of the marker such as '-' or '1.' in columns (spaces or tab stop of 4)
** if available, or null otherwise.
**
** - '- Foo' (marker indent: 0)
** - ' - Foo' (marker indent: 1)
** - ' 1. Foo' (marker indent: 2)
Int? markerIndent
** The indent of the content in columns (spaces or tab stop of 4) if available
** or null otherwise. The content indent is counted from the beginning of the line
** and includes the marker on the first line
**
** - '- Foo' (content indent: 2)
** - ' - Foo' (content indent: 3)
** - ' 1. Foo' (content indent: 5)
**
** Note that subsequent lines in the same list item need to be indented by at least
** the content indent to be counted as part of the list item.
Int? contentIndent
}
**************************************************************************
** Paragraph
**************************************************************************
** Paragraph
@Js
class Paragraph : Block { }
**************************************************************************
** CustomBlock
**************************************************************************
** Custom Block
@Js
abstract class CustomBlock : Block { }