Skip to content

模板语言

HTML 代码

html
<!doctype html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <title>Monaco Template Editor</title>
    <style>
      html,
      body {
        margin: 0;
        padding: 0;
        height: 100%;
        overflow: hidden;
      }

      #container {
        width: 100%;
        height: 100vh;
      }
    </style>
  </head>

  <body>
    <div id="container"></div>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.53.0/min/vs/loader.min.js"></script>

    <script>
      require.config({
        paths: { vs: "https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.45.0/min/vs" },
      });

      require(["vs/editor/editor.main"], function () {
        const availableVariables = [
          { label: "userName", detail: "用户姓名", insertText: "userName" },
          { label: "company.name", detail: "公司名称", insertText: "company.name" },
          { label: "company.address", detail: "公司地址", insertText: "company.address" },
          { label: "order.id", detail: "订单ID", insertText: "order.id" },
        ];

        const LANGUAGE_ID = "my-template-lang";

        monaco.languages.register({ id: LANGUAGE_ID });

        monaco.languages.setLanguageConfiguration(LANGUAGE_ID, {
          autoClosingPairs: [
            { open: "{{", close: "}}" }, // 输入 {{ 自动补全 }}
            { open: "{", close: "}" }, // 输入 { 自动补全 }
            { open: '"', close: '"' },
            { open: "'", close: "'" },
          ],
        });

        monaco.languages.setMonarchTokensProvider(LANGUAGE_ID, {
          tokenizer: {
            root: [
              [/\{\{/, { token: "delimiter.curly", next: "@expression" }],
              [/[^\{]+/, "string"],
            ],
            expression: [
              [/[\w\.]+/, "variable.custom"],
              [/\}\}/, { token: "delimiter.curly", next: "@pop" }],
              [/\s+/, ""],
            ],
          },
        });

        monaco.editor.defineTheme("my-template-theme", {
          base: "vs-dark",
          inherit: true,
          rules: [
            { token: "variable.custom", foreground: "9CDCFE", fontStyle: "bold" },
            { token: "delimiter.curly", foreground: "C586C0" },
          ],
          colors: {},
        });

        monaco.languages.registerCompletionItemProvider(LANGUAGE_ID, {
          triggerCharacters: ["{", " ", "."], // 触发字符包含点号

          provideCompletionItems: function (model, position) {
            const textUntilPosition = model.getValueInRange({
              startLineNumber: position.lineNumber,
              startColumn: 1,
              endLineNumber: position.lineNumber,
              endColumn: position.column,
            });

            // 检测是否在 {{ }} 内
            const match = textUntilPosition.match(/\{\{([^}]*)$/);
            if (!match) {
              return { suggestions: [] };
            }

            // 核心修复:手动计算当前正在输入的单词范围(包含点号)
            // 匹配光标前连续的 字母、数字、下划线、点号
            const wordMatch = textUntilPosition.match(/([a-zA-Z0-9_\.]+)$/);

            let startColumn = position.column;
            if (wordMatch) {
              startColumn = position.column - wordMatch[0].length;
            }

            const range = {
              startLineNumber: position.lineNumber,
              endLineNumber: position.lineNumber,
              startColumn: startColumn,
              endColumn: position.column,
            };

            const suggestions = availableVariables.map((vari) => ({
              label: vari.label,
              kind: monaco.languages.CompletionItemKind.Variable,
              detail: vari.detail,
              insertText: vari.insertText,
              range: range, // 使用手动计算的范围进行替换
            }));

            return { suggestions: suggestions };
          },
        });

        monaco.editor.create(document.getElementById("container"), {
          value: "输入 {{}}",
          language: LANGUAGE_ID,
          theme: "my-template-theme",
          fontSize: 14,
        });
      });
    </script>
  </body>
</html>