You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Monaco Editor JS中为Handlebars模板配置层级智能提示

实现Monaco Editor模板变量的智能提示(支持JSON定义的嵌套对象)

我最近刚好在项目里实现过类似的需求——给Monaco Editor中的模板语法(比如{{ myobject.field1 }})添加智能提示,而且支持用JSON对象定义可提示的结构,包括深层嵌套的字段。下面是具体的实现步骤和代码示例:

核心思路

Monaco Editor本身支持自定义语言服务,我们可以通过注册自定义补全提供者(Completion Item Provider),来解析模板中的变量上下文,根据预先定义的JSON对象结构,动态生成对应的智能提示项。

具体实现步骤

1. 定义你的模板变量结构

首先,把需要提示的对象结构用JSON格式定义好,支持任意层级的嵌套:

// 示例:定义模板可用的变量结构,支持深层嵌套
const templateVariables = {
  myobject: {
    field1: '',
    field2: '',
    nestedObj: {
      innerField1: '',
      innerField2: {
        deepField: ''
      }
    }
  },
  anotherObject: {
    foo: '',
    bar: ''
  }
};

2. 初始化Monaco Editor并注册补全提供者

接下来,初始化编辑器,然后注册自定义的补全逻辑。关键是要处理两种触发场景:

  • 用户输入{{ 后,提示根级别的对象(比如myobject、anotherObject)
  • 用户输入对象后的.时,提示该对象的所有字段(包括嵌套字段)

完整代码示例:

<div id="container" style="width:800px;height:600px;border:1px solid gray"></div>

<script src="https://cdn.jsdelivr.net/npm/monaco-editor@0.44.0/min/vs/loader.js"></script>
<script>
require.config({ paths: { 'vs': 'https://cdn.jsdelivr.net/npm/monaco-editor@0.44.0/min/vs' }});
require(['vs/editor/editor.main'], function() {
  // 1. 定义模板变量结构
  const templateVariables = {
    myobject: {
      field1: '',
      field2: '',
      nestedObj: {
        innerField1: '',
        innerField2: {
          deepField: ''
        }
      }
    },
    anotherObject: {
      foo: '',
      bar: ''
    }
  };

  // 2. 初始化编辑器
  const editor = monaco.editor.create(document.getElementById('container'), {
    value: '{{ myobject. }}',
    language: 'html', // 可以用html或者自定义语言,这里用html作为基础
    theme: 'vs-dark'
  });

  // 3. 注册自定义补全提供者
  monaco.languages.registerCompletionItemProvider('html', {
    // 触发补全的字符:空格、.、以及{{ 后的输入
    triggerCharacters: [' ', '.', '{', 'm', 'a'], // 可以根据需要调整
    provideCompletionItems: function(model, position) {
      const textUntilPosition = model.getValueInRange({
        startLineNumber: position.lineNumber,
        startColumn: 1,
        endLineNumber: position.lineNumber,
        endColumn: position.column
      });

      // 判断当前是否在{{ }}模板内部
      const templateMatch = textUntilPosition.match(/\{\{(\s+)?([\w\.]*)$/);
      if (!templateMatch) {
        return { suggestions: [] };
      }

      const currentInput = templateMatch[2] || '';
      const suggestions = [];

      // 解析当前输入的路径,比如"myobject.nestedObj"会拆分成["myobject", "nestedObj"]
      const pathSegments = currentInput.split('.').filter(seg => seg);
      let currentObj = templateVariables;

      // 递归找到当前路径对应的对象
      for (const seg of pathSegments.slice(0, -1)) {
        if (currentObj[seg]) {
          currentObj = currentObj[seg];
        } else {
          // 如果路径不存在,返回空提示
          return { suggestions: [] };
        }
      }

      // 如果是根路径(没有输入.),提示所有根对象
      if (pathSegments.length === 0 || (pathSegments.length === 1 && currentInput.endsWith('.'))) {
        // 处理输入.后的场景,比如"myobject.",提示myobject的字段
        if (currentInput.endsWith('.')) {
          Object.keys(currentObj).forEach(key => {
            suggestions.push({
              label: key,
              kind: monaco.languages.CompletionItemKind.Property,
              insertText: key,
              detail: `字段:${key}`
            });
          });
        } else {
          // 提示根对象
          Object.keys(templateVariables).forEach(key => {
            suggestions.push({
              label: key,
              kind: monaco.languages.CompletionItemKind.Object,
              insertText: key,
              detail: `对象:${key}`
            });
          });
        }
      } else {
        // 处理中间路径的补全,比如输入"myobject.n"时提示nestedObj
        const lastSegment = pathSegments[pathSegments.length - 1];
        Object.keys(currentObj).forEach(key => {
          if (key.startsWith(lastSegment)) {
            suggestions.push({
              label: key,
              kind: typeof currentObj[key] === 'object' ? monaco.languages.CompletionItemKind.Object : monaco.languages.CompletionItemKind.Property,
              insertText: key,
              detail: typeof currentObj[key] === 'object' ? `对象:${key}` : `字段:${key}`
            });
          }
        });
      }

      return { suggestions: suggestions };
    }
  });
});
</script>

4. 关键逻辑说明

  • 上下文判断:通过正则匹配{{后的内容,确保只在模板内部触发提示,不会影响其他内容的编辑。
  • 路径解析:把用户输入的变量路径(比如myobject.nestedObj)拆分成数组,递归找到对应的对象节点,然后生成该节点的所有属性作为提示项。
  • 补全项类型区分:用CompletionItemKind区分对象和字段,让提示更直观。

扩展优化

  • 你可以把templateVariables换成动态加载的JSON配置,方便后续维护。
  • 如果模板语法有特殊规则(比如支持数组),可以扩展路径解析逻辑,支持数组下标提示。
  • 可以添加文档说明,在补全项的documentation字段里展示字段的描述信息。

内容的提问来源于stack exchange,提问作者Jonny

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 09:10:23