# core/matchers/markdown.matcher.ts

> 88 lines of code and 22 definitions.

Tree: Bane's Lab Site
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/tree#file-core-matchers-markdown-matcher-ts
Source text: https://banes-lab.com/source/tree/core/matchers/markdown.matcher.ts.txt

Listed in [core/matchers](https://banes-lab.com/api/source/tree/core/matchers.md), before [core/matchers/search.matcher.ts](https://banes-lab.com/source/tree/core/matchers/search.matcher.ts.md).

## Definitions

- `orderedText` (lexical_declaration, line 50, exported)
- `isTableRule` (lexical_declaration, line 67, exported)
- `charsOf` (lexical_declaration, line 18)
- `isRule` (lexical_declaration, line 26, exported)
- `bodyLines` (lexical_declaration, line 90, exported)
- `chars` (lexical_declaration, line 27, exported)
- `[first]` (lexical_declaration, line 28, exported)
- `headingLevel` (lexical_declaration, line 37, exported)
- `level` (lexical_declaration, line 38, exported)
- `bulletText` (lexical_declaration, line 45, exported)
- `digits` (lexical_declaration, line 52, exported)
- `closes` (lexical_declaration, line 56, exported)
- `cellsOf` (lexical_declaration, line 60, exported)
- `inner` (lexical_declaration, line 62, exported)
- `body` (lexical_declaration, line 63, exported)
- `trimmed` (lexical_declaration, line 68, exported)
- `uncommented` (lexical_declaration, line 75)
- `out` (lexical_declaration, line 76)
- `at` (lexical_declaration, line 77)
- `open` (lexical_declaration, line 79)
- `lines` (lexical_declaration, line 91, exported)
- `close` (lexical_declaration, line 95, exported)

## Contained in

- [core/matchers](https://banes-lab.com/anatomy/tree/folder-core-matchers.md)

## Used by

- [core/converters/markdown.converter.ts](https://banes-lab.com/source/tree/core/converters/markdown.converter.ts.md)

## Linked from

- [core/converters](https://banes-lab.com/anatomy/tree/folder-core-converters.md)
- [core/matchers](https://banes-lab.com/anatomy/tree/folder-core-matchers.md)

## Source

```typescript
import {
    BULLET_MARKS,
    COMMENT_CLOSE,
    COMMENT_OPEN,
    FRONTMATTER_FENCE,
    HEADING_MARK,
    HEADING_MAX_LEVEL,
    ORDERED_CLOSE,
    RULE_CHARS,
    RULE_MIN_LENGTH,
    TABLE_PIPE,
    TABLE_RULE_CHARS,
} from "#configuration/constants/markdown.constants";
import { DIGITS } from "#configuration/constants/syntax.constants";
import { LINE_BREAK } from "#configuration/constants/code.constants";
import { SPACE } from "#configuration/constants/document.constants";

const charsOf = function charsOf(text: string): string[] {
    const chars: string[] = [];
    for (const char of text) {
        chars.push(char);
    }
    return chars;
};

export const isRule = function isRule(line: string): boolean {
    const chars = charsOf(line.trim()).filter((char) => char !== SPACE);
    const [first] = chars;
    return (
        chars.length >= RULE_MIN_LENGTH &&
        first !== undefined &&
        RULE_CHARS.has(first) &&
        chars.every((char) => char === first)
    );
};

export const headingLevel = function headingLevel(line: string): number {
    let level = 0;
    while (level < line.length && line.charAt(level) === HEADING_MARK) {
        level += 1;
    }
    return level > 0 && level <= HEADING_MAX_LEVEL && line.charAt(level) === SPACE ? level : 0;
};

export const bulletText = function bulletText(line: string): string | null {
    const trimmed = line.trim();
    return BULLET_MARKS.has(trimmed.charAt(0)) && trimmed.charAt(1) === SPACE ? trimmed.slice(2) : null;
};

export const orderedText = function orderedText(line: string): string | null {
    const trimmed = line.trim();
    let digits = 0;
    while (digits < trimmed.length && DIGITS.includes(trimmed.charAt(digits))) {
        digits += 1;
    }
    const closes = trimmed.charAt(digits) === ORDERED_CLOSE && trimmed.charAt(digits + 1) === SPACE;
    return digits > 0 && closes ? trimmed.slice(digits + 2) : null;
};

export const cellsOf = function cellsOf(line: string): string[] {
    const trimmed = line.trim();
    const inner = trimmed.startsWith(TABLE_PIPE) ? trimmed.slice(1) : trimmed;
    const body = inner.endsWith(TABLE_PIPE) ? inner.slice(0, -1) : inner;
    return body.split(TABLE_PIPE).map((cell) => cell.trim());
};

export const isTableRule = function isTableRule(line: string): boolean {
    const trimmed = line.trim();
    return (
        trimmed.includes(TABLE_PIPE) &&
        charsOf(trimmed).every((char) => char === TABLE_PIPE || TABLE_RULE_CHARS.has(char))
    );
};

const uncommented = function uncommented(text: string): string {
    let out = "";
    let at = 0;
    while (at < text.length) {
        const open = text.indexOf(COMMENT_OPEN, at);
        if (open === -1) {
            return out + text.slice(at);
        }
        const close = text.indexOf(COMMENT_CLOSE, open + COMMENT_OPEN.length);
        out += text.slice(at, open);
        at = close === -1 ? text.length : close + COMMENT_CLOSE.length;
    }
    return out;
};

export const bodyLines = function bodyLines(text: string): string[] {
    const lines = uncommented(text).split(LINE_BREAK);
    if (lines[0]?.trim() !== FRONTMATTER_FENCE) {
        return lines;
    }
    const close = lines.findIndex((line, index) => index > 0 && line.trim() === FRONTMATTER_FENCE);
    return close === -1 ? lines : lines.slice(close + 1);
};
```
