docs/src/internals/parse.md
This document goes through the internals of how we take a markdown file and finish rendering it into the terminal screen.
Markdown file parsing is done via the comrak crate. This crate parses the markdown file and gives you back an AST that contains the contents and structure of the input file.
ASTs are a logical way of representing the markdown file but this structure makes it a bit hard to process. Given our
ultimate goal is to render this input, we want it to be represented in a way that facilitates that. Because of this we
first do a pass on this AST and construct a list of MarkdownElements. This enum represents each of the markdown
elements in a flattened, non-recursive, way:
[Bold(hello), ItalicsBold(my name is), ItalicsBoldStrikethrough(bob)] (names
are completely not what they are in the code, this is just to illustrate flattening). This makes it much easier to
render text because we don't need to walk the tree and keep the state between levels.MarkdownElement::List element that contains a list of items that contain their
text, prefix ("*" for bullet lists), and nesting depth. This also simplifies processing as list elements can also
contain formatted text so we would otherwise have the same problem as above.This first step then produces a list of elements that can easily be processed.
The format above is nicer than an AST but it's still not great to be used as the input to the code that renders the presentation for various reasons:
Because of this, we introduce a step in between parsing and rendering where we build a presentation. A presentation is made up of a list of slides and each slide is made up of render operations. Render operations are the primitives that the render code understands to print text on the screen. These can be the following, among others:
This allows us to have a simple model where the logic that takes markdown elements and a theme and chooses how it will be rendered is in one place, and the logic that takes those instructions and executes them is elsewhere. So for example, this step will take a bullet point and concatenate is suffix ("*" for bullet points for example), turn that into a single string and generate a "render text" operation.
This has the nice added bonus that the rendering code doesn't have to be fiddling around with string concatenation or other operations that could take up CPU cycles: it just takes these render operations and executes them. Not that performance matters here but it's nice to get better performance for free.
The rendering code is straightforward and simply takes the current slide, iterates all of its rendering operations, and executes those one by one. This is done via the crossterm crate.
The only really complicated part is fitting text into the screen. Because we apply our own margins, we perform word splitting and wrapping around manually, so there's some logic that takes the text to be printed and the width of the terminal and splits it accordingly.
Note that this piece of code is the only one aware of the current screen size. This lets us forget in previous steps about how large the screen is and simply delegate that responsibility to this piece.