Skip to content

MkCode

Show source on GitHub

Class representing a Code block.

Example: Default

Jinja

{{ "a = 1 + 2" | MkCode }}

Python

MkCode('a = 1 + 2')
a = 1 + 2
```` {.python }
a = 1 + 2
````
<div class="language-python highlight"><pre><span></span><code><span id="__span-0-1"><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="n">a</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">+</span> <span class="mi">2</span>
</span></code></pre></div>
.. sourcecode:: python
    a = 1 + 2
MkCode
╰── MkText('a = 1 + 2')

Example: Syntax highlighting

Jinja

{{ "var z = x + y;" | MkCode(language="js") }}

Python

MkCode('var z = x + y;', language='js')
var z = x + y;
```` {.js }
var z = x + y;
````
<div class="language-js highlight"><pre><span></span><code><span id="__span-0-1"><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="kd">var</span><span class="w"> </span><span class="nx">z</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">x</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">y</span><span class="p">;</span>
</span></code></pre></div>
.. sourcecode:: js
    var z = x + y;
MkCode
╰── MkText('var z = x + y;')

Example: Highlighting lines

Jinja

{{ "1
2
3
4" | MkCode(highlight_lines=[1, 3]) }}

Python

MkCode('1\n2\n3\n4', highlight_lines=[1, 3])
1
2
3
4
```` {.python  hl_lines='1 3'}
1
2
3
4
````
<div class="language-python highlight"><pre><span></span><code><span id="__span-0-1"><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="hll"><span class="mi">1</span>
</span></span><span id="__span-0-2"><a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a><span class="mi">2</span>
</span><span id="__span-0-3"><a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a><span class="hll"><span class="mi">3</span>
</span></span><span id="__span-0-4"><a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a><span class="mi">4</span>
</span></code></pre></div>
.. sourcecode:: python
    1
    2
    3
    4
MkCode
╰── MkText('1\n2\n3\n4')

Example: Line numbers

Jinja

{{ "1
2
3
4" | MkCode(linenums=10) }}

Python

MkCode('1\n2\n3\n4', linenums=10)
1
2
3
4
```` {.python  linenums='10'}
1
2
3
4
````
<div class="language-python highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-0-10">10</a></span>
<span class="normal"><a href="#__codelineno-0-11">11</a></span>
<span class="normal"><a href="#__codelineno-0-12">12</a></span>
<span class="normal"><a href="#__codelineno-0-13">13</a></span></pre></div></td><td class="code"><div><pre><span></span><code><span id="__span-0-10"><a id="__codelineno-0-10" name="__codelineno-0-10"></a><span class="mi">1</span>
</span><span id="__span-0-11"><a id="__codelineno-0-11" name="__codelineno-0-11"></a><span class="mi">2</span>
</span><span id="__span-0-12"><a id="__codelineno-0-12" name="__codelineno-0-12"></a><span class="mi">3</span>
</span><span id="__span-0-13"><a id="__codelineno-0-13" name="__codelineno-0-13"></a><span class="mi">4</span>
</span></code></pre></div></td></tr></table></div>
.. sourcecode:: python
    1
    2
    3
    4
MkCode
╰── MkText('1\n2\n3\n4')

Bases: MkContainer

fence_boundary property

fence_boundary: str

fence_title property

fence_title: str

text property

text: str

__init__

__init__(
    content: str | mk.MkNode | list = "",
    *,
    language: str = "python",
    title: str = "",
    linenums: int | None = None,
    highlight_lines: list[int] | None = None,
    fence_level: int | None = None,
    **kwargs: Any
)

Parameters:

Name Type Description Default
content str | MkNode | list

Content to show inside code box

''
language str

language for syntax highlighting

'python'
title str

Code block title

''
linenums int | None

If set, use as start linenumber

None
highlight_lines list[int] | None

Optionally highlight lines

None
fence_level int | None

Determines amount of ticks used for fence. If None, auto-determine based on nesting depth.

None
kwargs Any

Keyword arguments passed to parent

{}

for_file classmethod

for_file(
    path: str | os.PathLike,
    *,
    linenums: bool = True,
    highlight_caller: bool = True,
    title: str | None = None,
    language: str | None = None,
    **kwargs: Any
)

Parameters:

Name Type Description Default
path str | PathLike

Path to the code file (also supports fsspec-protocol URLs)

required
linenums bool

Whether to show line numbers

True
highlight_caller bool

Whether we want to try to highlight the line which called this method.

True
title str | None

title to use for the code box. If None is set, filename will be used.

None
language str | None

Syntax highlighting language. If None, try to infer from extension.

None
kwargs Any

Keyword arguments passed to MkCode ctor

{}

for_object classmethod

for_object(
    obj: datatypes.HasCodeType,
    *,
    dedent: bool = True,
    extract_body: bool = False,
    title: str | None = None,
    linenums: bool = True,
    highlight_caller: bool = True,
    **kwargs: Any
) -> Self

Parameters:

Name Type Description Default
obj HasCodeType

Python object to show code from

required
dedent bool

Whether to dedent the code

True
extract_body bool

if True, Function / Class signatures are stripped from the code

False
title str | None

Title to use for code block. If None, it will use the object path.

None
linenums bool

Whether to show line numbers

True
highlight_caller bool

Whether we want to try to highlight the line which called this method.

True
kwargs Any

Keyword arguments passed to MkCode ctor

{}
Name Children Inherits
MkDiagram
mknodes.basenodes.mkdiagram
Class representing a mermaid diagram.
MkTreeView
mknodes.templatenodes.mktreeview
Node to display tree structures.
    Name Children Inherits
    MkContainer
    mknodes.basenodes.mkcontainer
    A node containing other MkNodes.
    graph TD
      94854582908560["mkcode.MkCode"]
      94854582919984["mkcontainer.MkContainer"]
      94854582916880["mknode.MkNode"]
      94854582838576["node.Node"]
      140544995341632["builtins.object"]
      94854582919984 --> 94854582908560
      94854582916880 --> 94854582919984
      94854582838576 --> 94854582916880
      140544995341632 --> 94854582838576
    /home/runner/work/mknodes/mknodes/mknodes/basenodes/mkcode/metadata.toml
    [metadata]
    icon = "mdi:code-json"
    name = "MkCode"
    
    [requirements.extension."pymdownx.superfences"]
    [requirements.extension."pymdownx.highlight"]
    anchor_linenums = true
    line_spans="__span"
    pygments_lang_class = true
    
    
    [examples.title]
    title = "Default"
    description = "By default, python syntax highlighting is used."
    jinja = """
    {{ "a = 1 + 2" | MkCode }}
    """
    
    [examples.syntax_highlight]
    title = "Syntax highlighting"
    jinja = """
    {{ "var z = x + y;" | MkCode(language="js") }}
    """
    
    [examples.hl_lines]
    title = "Highlighting lines"
    jinja = """
    {{ "1\n2\n3\n4" | MkCode(highlight_lines=[1, 3]) }}
    """
    
    [examples.line_numbers]
    title = "Line numbers"
    jinja = """
    {{ "1\n2\n3\n4" | MkCode(linenums=10) }}
    
    """
    
    [output.markdown]
    template = """
    {{ node.fence_boundary }} {{ node.fence_title }}
    {{ node.text }}
    {{ node.fence_boundary }}
    """
    
    [output.rst]
    template = """
    .. sourcecode:: {{ node.language }}
    {{ node.text | indent(first=True) }}
    """
    
    mknodes.basenodes.mkcode.MkCode
    class MkCode(mkcontainer.MkContainer):
        """Class representing a Code block."""
    
        ICON = "material/code-json"
        ATTR_LIST_SEPARATOR = "\n"
        REQUIRED_EXTENSIONS = [
            resources.Extension(
                "pymdownx.highlight",
                anchor_linenums=True,
                line_spans="__span",
                pygments_lang_class=True,
            ),
            resources.Extension("pymdownx.superfences"),
        ]
    
        def __init__(
            self,
            content: str | mk.MkNode | list = "",
            *,
            language: str = "python",
            title: str = "",
            linenums: int | None = None,
            highlight_lines: list[int] | None = None,
            fence_level: int | None = None,
            **kwargs: Any,
        ):
            """Constructor.
    
            Arguments:
                content: Content to show inside code box
                language: language for syntax highlighting
                title: Code block title
                linenums: If set, use as start linenumber
                highlight_lines: Optionally highlight lines
                fence_level: Determines amount of ticks used for fence.
                             If None, auto-determine based on nesting depth.
                kwargs: Keyword arguments passed to parent
            """
            self.language = language
            self.title = title
            self.linenums = linenums
            self.highlight_lines = highlight_lines
            self._fence_level = fence_level
            super().__init__(content=content, **kwargs)
    
        @property
        def text(self) -> str:
            """Text content."""
            return "\n".join(str(i) for i in self.items)
    
        @property
        def fence_boundary(self) -> str:
            """Return fence boundary, based on nesting level. Default is ```."""
            if self._fence_level:
                return "`" * (self._fence_level + 3)
            block_level = sum(isinstance(i, MkCode) for i in self.ancestors)
            return "`" * (block_level + 3)
    
        @property
        def fence_title(self) -> str:
            """Title in first line."""
            classes = " ".join(f".{i}" for i in self.mods.css_classes)
            attrs = {}
            if self.title:
                attrs["title"] = self.title
            if self.language:
                classes = f".{self.language} {classes}"
            if self.linenums is not None:
                attrs["linenums"] = str(self.linenums)
            if self.highlight_lines:
                sub = " ".join(str(i) for i in self.highlight_lines)
                attrs["hl_lines"] = sub
            if not classes and not attrs:
                return ""
            attr_str = " ".join(f"{k}={v!r}" for k, v in attrs.items())
            if attr_str:
                attr_str = " " + attr_str
            return f"{{{classes}{attr_str}}}"
    
        def _to_markdown(self) -> str:
            first_line = f"{self.fence_boundary} {self.fence_title}"
            return f"{first_line}\n{self.text}\n{self.fence_boundary}"
    
        @classmethod
        def for_file(
            cls,
            path: str | os.PathLike,
            *,
            linenums: bool = True,
            highlight_caller: bool = True,
            title: str | None = None,
            language: str | None = None,
            **kwargs: Any,
        ):
            """Create a MkCode node based on a code file.
    
            Line numbers will be shown by default. If `highlight_caller` is `True`,
            it will try to detect whether the calling method is inside the code block are
            creating and if yes, it will highlight that line.
    
            Arguments:
                path: Path to the code file (also supports fsspec-protocol URLs)
                linenums: Whether to show line numbers
                highlight_caller: Whether we want to try to highlight the line which called
                                  this method.
                title: title to use for the code box. If None is set, filename will be used.
                language: Syntax highlighting language. If None, try to infer from extension.
                kwargs: Keyword arguments passed to MkCode ctor
            """
            file_path = upath.UPath(path)
            content = file_path.read_text()
            hl_lines = None
            if highlight_caller and (frame := inspect.currentframe()) and frame.f_back:
                call_file = frame.f_back.f_code.co_filename
                if call_file == str(file_path.absolute()):
                    line_count = content.count("\n")
                    line = frame.f_back.f_lineno
                    hl_lines = [line] if 0 <= line <= line_count else None
            start_line = 1 if linenums else None
            title = file_path.name if title is None else title
            if language is None:
                language = datatypes.EXT_TO_PYGMENTS_STYLE.get(file_path.suffix, "")
            return cls(
                content,
                linenums=start_line,
                title=title,
                highlight_lines=hl_lines,
                language=language,
                **kwargs,
            )
    
        @classmethod
        def for_object(
            cls,
            obj: datatypes.HasCodeType,
            *,
            dedent: bool = True,
            extract_body: bool = False,
            title: str | None = None,
            linenums: bool = True,
            highlight_caller: bool = True,
            **kwargs: Any,
        ) -> Self:
            """Create a MkCode node based on a python object.
    
            Fetches code by using the inspect module.
            Line numbers will be shown by default. If highlight_caller is True,
            it will try to detect whether the calling method is inside the code block are
            displaying and if yes, it will highlight that line.
    
            Arguments:
                obj: Python object to show code from
                dedent: Whether to dedent the code
                extract_body: if True, Function / Class signatures are stripped from the code
                title: Title to use for code block. If None, it will use the object path.
                linenums: Whether to show line numbers
                highlight_caller: Whether we want to try to highlight the line which called
                                  this method.
                kwargs: Keyword arguments passed to MkCode ctor
            """
            code = inspecthelpers.get_source(obj)
            if extract_body:
                if not callable(obj):
                    msg = "Can only extract body from Functions, Methods and classes"
                    raise TypeError(msg)
                code = textfilters.extract_body(code)
            code = textwrap.dedent(code) if dedent else code
            code_title = title if title is not None else classhelpers.get_code_name(obj)
            hl_lines = None
            lines, start_line = inspecthelpers.get_source_lines(obj)
            if isinstance(obj, types.ModuleType):
                start_line += 1
            if highlight_caller and (frame := inspect.currentframe()) and frame.f_back:
                call_file = frame.f_back.f_code.co_filename
                obj_file = inspecthelpers.get_file(obj)
                if call_file == obj_file:
                    line_no = frame.f_back.f_lineno
                    line = line_no - start_line + 1
                    hl_lines = [line] if 0 <= line <= start_line + len(lines) else None
            return cls(
                code,
                title=code_title,
                linenums=start_line if linenums else None,
                highlight_lines=hl_lines,
                **kwargs,
            )