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.45.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>