Skip to content

Rulers: Overview

Alteras1 edited this page Jun 24, 2020 · 6 revisions

Rulers are what dictate the html output of a tag. The parser will automatically tokenize any content with an opening tag [tag] and a closing tag [/tag], before passing it to the ruler.

Rulers in the markdown-it extension are split into two general types: Inline and Block level. The order of parsing is Block Level > Inline Level. This leaves the engine with the annoying quirks of not being able to put block level tags inside inline level. Overall, these rulers function the same way.

Example of a Ruler

Here is an example of a tag and its ruler, specifically a block level ruler with replace:

[print=line]
text
[/print]
  md.block.bbcode.ruler.push("print", {
    tag: "print",
    replace: function (state, tagInfo, content) {
      let printOption = tagInfo.attrs['_default'];

      let token = state.push("div_open", "div", 1);
      if (!printOption) {
        token.attrs = [["class", "bbcode-print"]];
      } else {
        token.attrs = [["class", "bbcode-print-" + printOption]];
      }

      token = state.push("inline", "", 0);
      token.content = content;
      token.children = [];

      state.push("div_close", "div", -1);

      return true;
    }
  });

It defines the name of the tag, in this case [print], and picks up it's components, the tagInfo and the content.
tagInfo is the arguments given inside the tag, creating a dictionary. In this case [print=line] -> {_default: "line"}.
It passes this, along with content, into a function that determines the html output by pushing attributes into the token.

This is a brief example. The following sections will go into further detail. But first, let us start with the tag itself

Dissection of a Tag

As briefly touched upon above, a tag is composed of three parts: tag, tagInfo, content. This comes together like so:

[tag tagInfo]content[/tag]

tag and content are self-explanatory strings (Note: content can be manipulated inside the ruler). tagInfo on the other hand is more interesting, as it is completely optional. tagInfo comes in two types, a single argument, and a multi-argument.

Single Argument tagInfo

[tag=value]

Here, the value is stored as a string in the key _default, accessible via tagInfo.attrs. If no value is provided, tagInfo.attrs['_default'] returns NULL. This happens when the tag is [tag] or [tag arg1=value1 ...].

Multi-Argument tagInfo

[tag arg1=value1 arg2=value2 ...]

Any number of arguments can be provided. Similar to the single argument, every value is optional, and if missing, returns a NULL. Each one creates a key/value pair in tagInfo.attrs under their respective names.
{arg1: "value1", arg2: "value2", ...}
It is up to the ruler's code to determine how to process a tag when given its arguments. The best example of this is the [font] tag.

It should be noted that it is illegal to create a combined tag structure [tag=value1 arg=value2], as _default will have the value "value1 arg=value2"

Structure of a Ruler

Let us discuss the structure of a ruler. Here is the basic outline for creating a ruler:

function setupMarkdownIt(md) {

  md.inline.bbcode.ruler.push("tagname", {
    tag: "tagname",
    method: "method function/value"
  });

  md.block.bbcode.ruler.push("secondtagname", {
    tag: "secondtagname",
    method: "method function/value"
  });
}

All rulers are created inside the setupMarkdownIt(md) function, where an array is pushed into either the block or inline level, containing the name of the tag and the method for handling it. Block and Inline levels will be discussed in a later section. For simplicity in the rest of this discussion, we'll refer to both md.inline.bbcode.ruler and md.block.bbcode.ruler as ruler.

ruler is made up of the tag and the method. tag defines the name of the tag and seems to be case-insensitive, but as a standard, we keep it lowercase. method determines the html to be outputted.