Skip to content

Snippet 修改对 VSCode Extension 的影响分析

你的问题

"这样的修改,会影响通过vscode生成的文件,名字不会正常替换吗?"

简短回答

不会影响!

我已经修改了 renderSnippet 函数,让它同时支持两种语法:

  • - 旧语法(用于程序化生成)
  • ${1:Name} - 新语法(用于手动输入 snippet)

详细说明

两种使用场景

场景 1: 手动输入 Snippet (在编辑器中)

使用方式:

  1. .dart 文件中输入 stWidget
  2. Tab 键触发
  3. 交互式输入参数

使用的语法: ${1:Name} (VSCode 标准 tabstop)

处理方式: VSCode 自动处理,不经过我们的代码

我的修改: ✅ 已修复,现在支持参数输入


场景 2: 通过命令生成文件 (VSCode Extension)

使用方式:

  1. 右键点击文件夹
  2. 选择 Flu: Generate
  3. 选择类型和输入名称
  4. 自动生成文件

使用的语法: ${1:Name} (两种都支持)

处理方式: 通过 renderSnippet 函数替换

我的修改: ✅ 增强了 renderSnippet,同时支持两种语法

代码流程

用户执行命令: Flu: Generate

输入名称: "MyButton"

调用: generateWidget("MyButton", options)

读取 snippet: getSnippetContent(projectDir, 'flu.stWidget', { Name: 'MyButton' })

调用: renderSnippet(snippetBody, { Name: 'MyButton' })

替换占位符:
  - {{Name}} → MyButton  ✅
  - ${1:Name} → MyButton ✅

写入文件: my_button_widget.dart

renderSnippet 函数的增强

之前的代码:

typescript
export function renderSnippet (bodyLines: string[] | string, variables: Record<string, any>): string {
  const body = Array.isArray(bodyLines) ? bodyLines.join('\n') : String(bodyLines || '');
  // 只支持 {{Name}} 语法
  return body.replace(/\{\{(\w+)\}\}/g, (_: string, key: string) => {
    return Object.prototype.hasOwnProperty.call(variables, key) ? String(variables[key]) : `{{${key}}}`;
  });
}

现在的代码:

typescript
export function renderSnippet (bodyLines: string[] | string, variables: Record<string, any>): string {
  const body = Array.isArray(bodyLines) ? bodyLines.join('\n') : String(bodyLines || '');
  
  // 1. 替换 {{Name}} 格式的占位符(旧语法,保持兼容)
  let result = body.replace(/\{\{(\w+)\}\}/g, (_: string, key: string) => {
    return Object.prototype.hasOwnProperty.call(variables, key) ? String(variables[key]) : `{{${key}}}`;
  });
  
  // 2. 替换 ${1:Name} 格式的 VSCode tabstop(新语法)
  result = result.replace(/\$\{(\d+):([^}]+)\}/g, (_match: string, _tabstopNum: string, defaultValue: string) => {
    const varMatch = defaultValue.match(/^(\w+)$/);
    if (varMatch) {
      const varName = varMatch[1];
      return Object.prototype.hasOwnProperty.call(variables, varName) ? String(variables[varName]) : defaultValue;
    }
    
    // 处理复杂的 tabstop 转换(如 ${1/(.*)/${1:/downcase}/})
    if (defaultValue.includes('${')) {
      return '';
    }
    
    return defaultValue;
  });
  
  return result;
}

测试结果

=== 测试 renderSnippet 函数 ===

测试 1: 旧语法 {{Name}}
class MyButtonWidget extends StatelessWidget {
  const MyButtonWidget({super.key});
}
✅ 预期: MyButtonWidget

测试 2: 新语法 ${1:Name}
class MyButtonWidget extends StatelessWidget {
  const MyButtonWidget({super.key});
}
✅ 预期: MyButtonWidget

测试 3: 混合语法
class MyButtonWidget extends StatelessWidget {
  /// Title: My Button
  const MyButtonWidget({super.key});
}
✅ 预期: MyButtonWidget 和 My Button

测试 5: 默认值(没有提供变量)
class MyButtonWidget extends StatelessWidget {
  String get title => 'Title';
}
✅ 预期: Name 被替换,Title 保留默认值

=== 所有测试完成 ===

实际使用对比

通过命令生成 Widget

操作步骤:

  1. 右键点击 lib/widgets 文件夹
  2. 选择 Flu: Generate
  3. 选择 Widget
  4. 选择 Stateless
  5. 输入名称: my_button

生成的文件: lib/widgets/my_button_widget.dart

文件内容:

dart
import 'package:flutter/material.dart';

class MyButtonWidget extends StatelessWidget {
  // ------------------------------
  // 1. 外部配置参数(构造函数)
  // ------------------------------
  /// 构造函数
  const MyButtonWidget({super.key});

  // ------------------------------
  // 2. UI构建(核心:根据参数渲染)
  // ------------------------------
  @override
  Widget build(BuildContext context) {
    Widget w = const SizedBox.shrink();
    w = Padding(padding: const EdgeInsets.all(12), child: w);
    return w;
  }

  // ------------------------------
  // 3. 辅助方法(UI计算/转换)
  // ------------------------------
  String _format(String s) => s;
}

结果: ✅ 名称正确替换为 MyButton

手动输入 Snippet

操作步骤:

  1. 打开任意 .dart 文件
  2. 输入 lessWidget
  3. Tab
  4. 输入 MyButton
  5. Tab 跳过其他参数

结果: ✅ 同样生成正确的代码

兼容性保证

向后兼容

  • ✅ 旧的 语法继续工作
  • ✅ 现有的项目不受影响
  • ✅ 之前创建的 snippet 文件仍然有效

向前兼容

  • ✅ 新的 ${1:Name} 语法支持交互式输入
  • ✅ 通过命令生成文件时也能正确替换
  • ✅ 两种语法可以混用

总结

问题

你担心修改 snippet 语法后,通过 VSCode Extension 命令生成文件时,名字不能正常替换。

答案

不会有问题! 因为:

  1. 我修改了 renderSnippet 函数,让它同时支持两种语法
  2. 测试验证通过,两种语法都能正确替换
  3. 保持向后兼容,旧的 语法继续工作
  4. 新增交互式支持,手动输入 snippet 时可以填写参数

两种语法的用途

语法用途处理方式
程序化生成(命令)renderSnippet 函数替换
${1:Name}交互式输入(手动)VSCode 自动处理 + renderSnippet 函数替换

最终效果

无论是:

  • ✅ 通过命令生成文件 (Flu: Generate)
  • ✅ 手动输入 snippet (stWidget + Tab)

都能正确工作,名字都会被正确替换!


修改文件:

  • packages/core/src/utils/snippet_loader.ts - 增强 renderSnippet 函数
  • packages/vscode-extension/snippets/dart.code-snippets - 更新为新语法
  • packages/core/templates/snippets/dart.code-snippets - 同步更新
  • packages/v2/templates/snippets/dart.code-snippets - 同步更新

测试文件:

  • packages/core/test/test_snippet_render.js - 验证两种语法都能正确工作

Released under the MIT License.