JSFiddle - React, Tailwind, and code Playground

by Sineos

HTML

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Klipper Config Syntax Highlighting</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
    <script src="./dev_test.js"></script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css">
    <script>
        document.addEventListener('DOMContentLoaded', (event) => {
            hljs.highlightAll();
        });
    </script>
</head>
<body>
    <h1>Klipper Config Example</h1>
    <pre><code class="language-klipper">

[idle_timeout]
gcode:
    {% if printer.pause_resume.is_paused %}
        M104 S0 ;ooo
        G1 X0
    {% else %}

[stepper_x] # iii
step_pin: 10 ; kkk
; bla
step_pin: PC2

[board_pins] ; kkk
aliases:
    EXP1_5=PE12, EXP1_6=PE13,    # hhh
    EXP1_5=PE12, EXP1_6=PE13,    ; jjj


    </code></pre>
</body>
</html>

JavaScript

hljs.registerLanguage("klipper", function (hljs) {
  const COMMENT = {
    scope: "comment",
    variants: [
      { begin: /^\s*[#;].*$/, end: /$/ }, // Standalone comments
      { begin: /[#;].*/, end: /$/ },      // Inline/trailing comments
    ],
    relevance: 10,
  };

  const KEY_PART = {
    scope: "symbol",
    begin: /^[^=\s][^:=]*/,  // More flexible key matching
    end: /(?=\s*[:=])/,
  };

  const VALUE_PART = {
    scope: "addition",
    begin: /[:=]/,
    end: /$/,
    excludeBegin: true,
    excludeEnd: true,
    contains: [
      COMMENT,
      hljs.inherit(hljs.NUMBER_MODE, { scope: "number" }),
      hljs.QUOTE_STRING_MODE
    ],
  };

  const SECTION = {
    scope: "punctuation",
    begin: /^\s*\[/,
    end: /\]/,
    contains: [
      {
        scope: "section",
        match: /[^\]]+/,
      },
    ],
  };

  const JINJA_BLOCK = {
    scope: "name",
    begin: /(?:^{%|{%|{{|{#)/,  // All Jinja delimiters
    end: /(?:%}|%}|}}|#})/,
    contains: [
      {
        scope: "built_in",
        match: /\b(if|elif|else|for|endfor|while|endwhile|set|in|params|not|and|trim|round)\b/,
      },
      hljs.QUOTE_STRING_MODE,
      hljs.inherit(hljs.NUMBER_MODE, { scope: "number" }),
    ],
  };

  const GCODE_COMMAND = {
    scope: "deletion",
    match: /\b([GMT]\d+(?:\.\d+)?|D[0-9.]+|I[-0-9.]+|J[-0-9.]+|K[-0-9.]+)\b/,
    contains: [
      {
        scope: "addition",
        match: /\b[XZYEFSPQRIJK]\d*\.?\d*\b/,
      },
    ],
  };

  const INDENTED_BLOCK = {
    scope: "deletion",
    begin: /^\s+/,
    end: /$/,
    contains: [
      COMMENT,
      hljs.inherit(hljs.NUMBER_MODE, { scope: "number" }),
      hljs.QUOTE_STRING_MODE,
      GCODE_COMMAND,
      JINJA_BLOCK,  // Allow Jinja in indented blocks
    ],
    relevance: 10,
  };

  return {
    name: "Klipper Config",
    aliases: ["klipper", "cfg"],
    disableAutodetect: true,
    contains: [
      COMMENT,
      SECTION,
      KEY_PART,
      VALUE_PART,
      JINJA_BLOCK,
     ...