Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ Inline Elements
Helper Classes
--------------

.. autoclass:: marko.element.Element
:members:

.. autoclass:: marko.source.Source
:members:

Expand Down
42 changes: 42 additions & 0 deletions docs/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,48 @@ The parsed document is an Abstract Syntax Tree (AST) that you can traverse and m

For more details about element types, see :ref:`elements`.

Source Positions
----------------

Each element in the AST carries the positions of its corresponding text in the
source, which is useful for implementing features like syntax highlighters.

Every element has a :attr:`~marko.element.Element.source_span` attribute, a
``(start, end)`` tuple of indices into the source text, and the convenience
properties :attr:`~marko.element.Element.start_pos` and
:attr:`~marko.element.Element.end_pos`. The positions are ``None`` when not
available (e.g. for elements produced by extensions that don't record them)::

from marko import Markdown

text = "[Google](https://google.com)"
doc = Markdown().parse(text + "\n")
paragraph = doc.children[0]
link = paragraph.children[0]
print(link.source_span) # (0, 28)
print(text[link.start_pos:link.end_pos]) # [Google](https://google.com)

For inline elements, the parts of the span that are pure syntax (e.g. the
markers of emphasis, the backticks of a code span, the brackets of a link) are
exposed via :attr:`~marko.element.Element.syntax_spans`, while the content
spans are covered by the element's children::

print(link.syntax_spans) # [(0, 1), (7, 28)] -> "[" and "](https://google.com)"
print(link.children[0].source_span) # (1, 7) -> "Google"

For :class:`~marko.inline.Link` and :class:`~marko.inline.Image` elements, the
destination and title are also exposed separately via the ``dest_span`` and
``title_span`` attributes::

print(link.dest_span) # (9, 27) -> "https://google.com"

.. note::

Positions are indices into the *normalized* source text: line terminators
(``\r\n``, ``\r``) are normalized to ``\n`` before parsing, and U+0000 is
replaced with U+FFFD. For plain LF text the positions map directly to the
original string.

Custom Rendering
----------------

Expand Down
259 changes: 259 additions & 0 deletions examples/demo.html

Large diffs are not rendered by default.

88 changes: 88 additions & 0 deletions examples/demo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Marko Syntax Highlighting Demo

This document demonstrates how Marko's source mapping info can be used to
build a **syntax highlighter**. Every token below is colored by its element
type, using the `source_span` and `syntax_spans` attributes.

## Headings & emphasis

### Level three *with emphasis*

#### Level four with `inline code`

Setext heading
==============

Second level
------------

Text with *emphasis*, **strong emphasis**, ***both***, and ~~strikethrough~~
(GFM). Escaped characters like \*stars\* and \# hashes stay literal.

## Links, images & autolinks

Here is [a link](https://example.com), one [with a title](https://example.com "The title"),
and a [reference link][ref]. An image: ![Marko logo](https://example.com/logo.png "Logo").
Autolinks like <https://example.org> and email <me@example.com> work too.

[ref]: https://example.com/ref "Reference"

## Code

Inline code: use `marko.parse(text)` to get the AST, then check
`element.source_span`.

```python
import marko

doc = marko.parse("# Hello")
heading = doc.children[0]
print(heading.source_span) # (0, 10)
```

An indented code block looks like this one,
and keeps its own color.

## Block quotes

> A block quote with *styled* content.
> It spans multiple lines.
>
> > And can be nested.

## Lists

- unordered item with `code`
- another item
- nested item one
- nested item two
- [x] a completed task
- [ ] a pending task

1. first ordered item
2. second ordered item
1. nested ordered

## Tables (GFM)

| Name | Price | Quantity |
| ------ | ----- | -------- |
| Apple | $1 | 10 |
| Banana | $0.5 | 3 |

## Other blocks

---

Some <span style="color: red">raw HTML</span> inline, and a block below:

<div class="note">
This is an HTML block.
</div>

> [!NOTE]
> GFM alert boxes are supported too.

A paragraph with a hard line break:
and a soft line break
right here.
Loading
Loading