如何构建具备Intellisense的声明式模板引擎?替代方案探讨
解决方案思路
基于Roslyn构建自定义语言服务(首推)
- 核心逻辑:借助Roslyn的代码分析能力,将模板内的
{{code ...}}块与周围静态文本拼接成完整的C#语法树,让编辑器(VS/VS Code)能够识别并提供Intellisense。- 先为模板文件定义专属后缀(比如
.tplcs),识别{{model TypeName}}指令绑定模型类型,提取所有{{code ...}}和{{endcode}}之间的代码片段。 - 在语言服务中将模板转换为一个“虚拟”C#类:静态文本作为字符串常量,代码块编写为循环/逻辑代码,同时注入模型类型引用,让Roslyn可以解析
Model的成员。 - 实现编辑器的语法高亮与补全功能,利用Roslyn的
Workspace和SemanticModel获取代码符号信息,实时提供提示。
- 先为模板文件定义专属后缀(比如
- 优势:模板语法完全自定义,支持任意项目类型,Intellisense体验与原生C#一致,还能跨编辑器兼容(只要支持LSP,比如VS Code可通过Language Server Protocol实现)。
复用Razor核心能力,摆脱ASP.NET依赖
- 改造方向:Razor的核心库
Microsoft.AspNetCore.Razor.Language并不依赖ASP.NET运行时,可以单独引用。- 自定义Razor项目系统:创建非ASP.NET的项目模板,注册自定义Razor文件处理器,让VS/VS Code识别
.cshtml文件并启用Razor语言服务。 - 替换模板宿主:默认Razor绑定Web模型,你可以自定义
IRazorTemplateEngine的宿主,让模板绑定自身的模型类型,而非PageModel或ViewData。
- 自定义Razor项目系统:创建非ASP.NET的项目模板,注册自定义Razor文件处理器,让VS/VS Code识别
- 优势:无需从零实现语法分析和Intellisense,直接复用成熟的Razor生态,仅需少量适配即可支持任意项目。
基于现有模板引擎扩展Intellisense
- 可选工具:比如使用Scriban或Liquid这类现成模板引擎,再为其编写自定义语言服务插件。
- 以Scriban为例:它本身支持C#模型绑定,你可以基于Roslyn为Scriban模板中的代码块生成虚拟C#代码,再通过LSP提供Intellisense。
- 优势:模板语法成熟,社区支持充足,仅需补充Intellisense能力即可。
关键实现细节
- 模型类型绑定:在模板开头通过
{{model Your.Namespace.ModelType}}指令,让语言服务自动引用对应程序集,解析模型成员。 - 代码块语法合并:将模板中的循环代码块转换为C#的
foreach循环,静态文本编写为Write方法调用,生成完整的虚拟代码供Roslyn分析。 - 跨编辑器支持:通过Language Server Protocol(LSP)编写通用语言服务,同时支持VS和VS Code,避免重复开发。
内容的提问来源于stack exchange,提问作者Joshua Frank
相关产品推荐
相关产品推荐

