这是你进入项目时最先需要知道的信息。根据任务类型,跳转到对应章节。
| 你想做什么 | 入口文件/目录 |
|---|---|
| 添加新的输入法格式支持 | src/ImeWlConverter.Formats/{Format}/ → 创建 Importer + Exporter |
| 修改码表/字典数据(拼音、五笔、注音等) | src/ImeWlConverter.CodeData/(接口 + Resources/ 嵌入码表) |
| 修改转换管道逻辑 | src/ImeWlConverter.Core/Pipeline/ConversionPipeline.cs |
| 修改 CLI 参数/行为 | src/ImeWlConverter.Application/Cli/(命令定义+执行链)与 Mapping/(参数解析) |
| 查询 CLI 机器可读契约(退出码/JSON) | src/ImeWlConverter.Application/Cli/ExitCodes.cs、docs/MIGRATION.md |
| 修改编码生成(拼音/五笔等) | src/ImeWlConverter.Core/CodeGeneration/Generators/ |
| 修改过滤器 | src/ImeWlConverter.Core/Filters/ |
| 修改过滤配置 DTO | src/ImeWlConverter.Abstractions/Options/FilterConfig.cs |
| 修改 macOS GUI | src/ImeWlConverterMac/ViewModels/MainWindowViewModel.cs |
| 修改 Windows GUI | src/IME WL Converter Win/Forms/MainForm.cs |
| 添加/修改单元测试 | src/ImeWlConverterCoreTest/ |
| 修改 CI 流程 | .github/workflows/ci.yml |
| 发布新版本 | docs/RELEASING.md |
深蓝词库转换(IME WL Converter)是一个跨平台的输入法词库格式转换工具,支持 50+ 种输入法格式之间的相互转换。
- 语言: C# / .NET 10.0
- 测试框架: xUnit 2.9.3
- 构建系统: Makefile + dotnet CLI
- 版本管理: MinVer(从 Git tag 自动生成)
- 平台: Windows / Linux / macOS
make build # 构建所有项目
make test # 运行单元测试 (318 个,实际以 dotnet test 输出为准)
make integration-test # 运行集成测试 (29 个用例,需先 make build-cmd)
make lint # 检查代码格式
make format # 自动格式化代码
make run-cmd # 运行 CLI 工具
make run-mac # 运行 macOS GUIsrc/
├── ImeWlConverter.Abstractions/ # 接口层(零依赖)
├── ImeWlConverter.CodeData/ # 码表数据叶子(零依赖,只读线程安全表服务)
├── ImeWlConverter.Core/ # 业务服务层(转换管道、编码生成、过滤、简繁转换)
├── ImeWlConverter.Formats/ # 格式实现层(107个文件,50+种格式)
├── ImeWlConverter.Application/ # 三端共享应用层(参数映射、请求工厂、CLI 前端、引导)
├── ImeWlConverter.SourceGenerators/ # Source Generator(编译时格式注册)
├── ImeWlConverterCmd/ # CLI 薄入口(逻辑在 Application/Cli)
├── ImeWlConverterMac/ # macOS GUI (Avalonia 11.2.3)
├── IME WL Converter Win/ # Windows GUI (WinForms)
└── ImeWlConverterCoreTest/ # xUnit 单元测试
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ CLI │ │ WinForms │ │ macOS GUI │
│ CommandBuild│ │ MainForm │ │ ViewModel │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
└────共享 Application 层────┬─────────┘
▼
┌───────────────────────┐
│ IConversionPipeline │ (Abstractions 层接口)
└───────────┬───────────┘
▼
┌───────────────────────┐
│ ConversionPipeline │ (Core 层统一实现)
│ │
│ Import → Filter → │
│ ChineseConvert → │
│ WordRank → CodeGen → │
│ RemoveEmpty → Export │
└───────────────────────┘
设计原则:三端(CLI、WinForms、macOS GUI)共用同一个 ConversionPipeline 底层转换引擎,只在用户交互层不同。
- CLI 通过
Application/Cli与Application/Requests解析参数构建ConversionRequest - WinForms 通过
MainForm用户操作构建ConversionRequest - macOS GUI 通过
MainWindowViewModel构建ConversionRequest - 三端共享
FilterConfig(Abstractions/Options/)、ConversionOptions、IProgress<ProgressInfo>
以下按从外到内的顺序描述各层,你只需要读到与任务相关的层即可。
| 文件 | 职责 |
|---|---|
ImeWlConverterCmd/Program.cs |
薄入口:注册编码 Provider,调用 CliApp.Run |
Application/Cli/CliApp.cs |
CLI 统一入口(旧参数检测 → 命令调用);Win 内嵌 CLI 共用 |
Application/Cli/CliOptions.cs |
System.CommandLine 选项定义 |
Application/Cli/CliCommandFactory.cs |
根命令装配与转换执行链(校验→组装→执行→输出) |
Application/Cli/CliValidator.cs |
必填项/文件存在性/格式 ID 校验(结构化 CliError) |
Application/Cli/Output/*.cs |
人类输出 / --json 输出 / 格式清单输出 |
Application/Cli/ExitCodes.cs |
退出码契约(0 成功 / 1 用法 / 2 输入 / 3 部分失败 / 4 内部) |
Application/Mapping/*.cs |
filter/编码类型/自定义格式 spec 解析(Try 模式,可单测) |
Application/Requests/ConversionRequestFactory.cs |
ConversionRequest 组装单一事实来源 |
Application/Bootstrap/ImeWlConverterBootstrapper.cs |
三端共用 DI 组装 |
| 文件 | 职责 |
|---|---|
App.axaml.cs |
应用入口,创建 DI 容器,注入 ViewModel |
ViewModels/MainWindowViewModel.cs |
MVVM ViewModel,构建 ConversionRequest,调用 IConversionPipeline |
Views/FilterConfigWindow.axaml.cs |
过滤配置 UI,使用共享 FilterConfig DTO |
| 文件 | 职责 |
|---|---|
Program.cs |
应用入口,创建 DI 容器 |
Forms/MainForm.cs |
主窗口,构建 ConversionRequest,调用 IConversionPipeline |
Forms/FilterConfigForm.cs |
过滤配置 UI,使用共享 FilterConfig DTO |
| 文件 | 职责 |
|---|---|
ConversionPipeline.cs |
薄编排层:导入 → 委托 EntryTransformationService → 导出;合并/逐文件两种模式 |
EntryTransformationService.cs |
词条五阶段处理(Filter→ChineseConvert→WordRank→CodeGen→RemoveEmpty),两条导出路径共用 |
FilterPipelineFactory.cs + FilterModules/ |
过滤器装配(模块注册制,开闭原则) |
FilterPipeline.cs |
过滤执行器(单条过滤 → 变换 → 批量过滤) |
CodePredicates.cs |
空编码判定谓词(两条路径共用) |
SelfDefiningCodeSource.cs |
自定义码表加载(ISelfDefiningCodeSource 实现) |
ConversionPipeline 支持的能力:
- 合并导出 / 逐文件导出(
MergeToOneFile选项) - 文件输出 / Stream 输出(GUI 先预览再保存)
- 从 FilterConfig 经 FilterPipelineFactory 模块化构建 FilterPipeline
- IProgress 细粒度进度报告
- CancellationToken 取消支持
- 逐文件错误捕获与结构化累积(
ConversionResult.Errors)
每个格式拆分为独立的 Importer 和 Exporter,通过 [FormatPlugin] 属性标记,Source Generator 自动注册到 DI。
| 基类 | 用途 |
|---|---|
TextFormatImporter |
文本格式导入(逐行解析) |
TextFormatExporter |
文本格式导出(逐行生成) |
BinaryFormatImporter |
二进制格式导入 |
| 目录 | 内容 |
|---|---|
CodeGeneration/Generators/ |
编码生成器(拼音、五笔86/98/新世纪、郑码、仓颉、注音、超音、二笔x4) |
Filters/ |
IWordFilter / IWordTransform / IBatchFilter 实现(12 种) |
Helpers/ |
文件操作、编码检测、拼音字典、HTTP |
Language/ |
纯 .NET 简繁转换(基于 OpenCC 映射表) |
WordRank/ |
默认词频生成器 |
Resources/ |
嵌入式码表 + 简繁映射文件 |
纯接口和 DTO,零依赖。供所有层引用。
| 子目录 | 关键类型 |
|---|---|
Contracts/ |
IFormatImporter, IFormatExporter, IConversionPipeline, ICodeGenerator, IWordFilter, IWordTransform, IBatchFilter |
Models/ |
WordEntry (sealed record), WordCode, FormatMetadata, ProgressInfo |
Options/ |
ConversionOptions, FilterConfig, CodeGenerationOptions, ImportOptions, ExportOptions |
Results/ |
Result<T>, ImportResult, ExportResult, ConversionRequest, ConversionResult |
Enums/ |
CodeType, SortType, PinyinType, ChineseConversionMode |
# 1. 在 Formats 项目中创建目录和文件
mkdir src/ImeWlConverter.Formats/MyFormat/// 2. 创建 Importer(注意:必须是 partial 类,且不要手写 Metadata 属性——由 Source Generator 生成)
[FormatPlugin("myf", "我的格式", 500)]
public sealed partial class MyFormatImporter : TextFormatImporter
{
protected override Encoding FileEncoding => new UTF8Encoding(false);
protected override bool IsContentLine(string line) =>
!string.IsNullOrWhiteSpace(line) && !line.StartsWith("#"); // 可选:跳过注释/空行
protected override IEnumerable<WordEntry> ParseLine(string line) { /* 解析逻辑 */ }
}// 3. 创建 Exporter(同样不手写 Metadata)
[FormatPlugin("myf", "我的格式", 500)]
public sealed partial class MyFormatExporter : TextFormatExporter
{
protected override Encoding FileEncoding => new UTF8Encoding(false);
protected override string? FormatEntry(WordEntry entry) { /* 导出逻辑,返回 null 跳过该词条 */ }
}Source Generator 会自动完成两件事:
- 将带
[FormatPlugin]的类注册到 DI 容器(无需手动注册) - 为每个类生成
Metadata属性(从[FormatPlugin]参数与基类链自动推断IsBinary等,手写 Metadata 会与生成代码冲突)
完整真实示例见 src/ImeWlConverter.Formats/Rime/RimeImporter.cs。
过滤器位于 src/ImeWlConverter.Core/Filters/,实现接口:
public interface IWordFilter { bool ShouldKeep(WordEntry entry); }
public interface IWordTransform { WordEntry? Transform(WordEntry entry); }
public interface IBatchFilter { IReadOnlyList<WordEntry> Filter(IReadOnlyList<WordEntry> entries); }FilterConfig(Abstractions/Options/FilterConfig.cs)定义了所有过滤选项,ConversionPipeline 内部的 BuildFilterPipeline() 方法将其转换为 FilterPipeline 实例。
CLI 通过 --filter 参数启用过滤:-f "len:2-10|rm:eng|rm:num"
编码生成器位于 src/ImeWlConverter.Core/CodeGeneration/Generators/,实现 ICodeGenerator 接口。通过 DI 注册,由 CodeGenerationService 根据 CodeType 选择对应生成器。
- 编排与导出:
src/ImeWlConverter.Core/Pipeline/ConversionPipeline.cs(薄层) - 词条阶段处理:
EntryTransformationService.cs,7 步流程:
- Import — 逐文件导入,累积所有 WordEntry(ConversionPipeline)
- Filter → ChineseConvert → WordRank → CodeGen → RemoveEmpty — EntryTransformationService.ApplyAsync(合并模式调一次,逐文件模式每文件一次)
- Export — 导出到文件或 Stream(ConversionPipeline)
- 过滤器实现位于
src/ImeWlConverter.Core/Filters/,装配位于Pipeline/FilterPipelineFactory.cs+Pipeline/FilterModules/ - 新增过滤器:实现
IFilterModule(或往现有模块加一行)→ 加入DefaultFilterModules.Create()清单即可,无需修改管道
- 禁止手动修改版本号(由 MinVer 从 Git tag 生成,配置在
src/Directory.Build.props) - 禁止使用运行时反射注册格式(Source Generator 自动注册)
- 禁止硬编码路径分隔符(用
Path.Combine()) - 禁止在 GUI 项目中重复实现转换逻辑(统一使用
IConversionPipeline) - 禁止 GUI 内联格式识别/请求组装/预览截断(统一用
ImeWlConverter.Application的 FormatDetectionService/ConversionRequestFactory/PreviewService——历史上三端各写一份已导致 macOS 识别 Bug) - 禁止 CLI 输出契约随意变更(退出码 0-4 与 --json schema 见
Application/Cli/ExitCodes.cs与docs/MIGRATION.md;变更必须同步基线测试tests/integration/cli-baseline.sh)
三端使用相同的 DI 注册方式:
var services = new ServiceCollection();
services.AddAllFormats(); // Source Generator 生成的格式注册
services.AddImeWlConverterCore(); // 管道、编码生成器、简繁转换、词频生成器
var sp = services.BuildServiceProvider();
// 获取管道
var pipeline = sp.GetRequiredService<IConversionPipeline>();项目处理多种字符编码(UTF-8、GBK、GB2312、Big5、Unicode)。.NET 10 已内置 CodePages 支持,但部分旧代码仍调用:
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);- CLI 工具为 framework-dependent(需 .NET 运行时)
- macOS app bundle 为 self-contained(包含运行时)
- Windows GUI 为 WinForms(仅 Windows)
- 集成测试支持 Linux、macOS、Windows (Git Bash)
- 单元测试: xUnit 2.9.3,位于
src/ImeWlConverterCoreTest/ - 集成测试: shell 脚本框架,位于
tests/integration/ - 测试并行执行(
xunit.runner.json中parallelizeTestCollections: true;Phase 2 已消灭静态可变状态,码表全部走ImeWlConverter.CodeData的线程安全只读表) - 禁新增 static 可变字典/集合——需要码表数据时注入
ICodeTableLibrary/IPinyinTable等接口(见src/ImeWlConverter.CodeData/) [Fact(Skip = "...")]标记按需运行的慢速测试
GitHub Actions (.github/workflows/ci.yml):
- Lint(格式检查,快速失败)
- Build + 单元测试 (Ubuntu)
- 多平台构建 (Win x64/x86, Linux x64/arm64, macOS x64/arm64)
- 集成测试 (Linux + macOS)
| 决策 | 原因 |
|---|---|
| 三端统一使用 ConversionPipeline | 消除重复转换逻辑,保证行为一致 |
| FilterConfig 放在 Abstractions 层 | 三端共享过滤配置 DTO,避免各端重复定义 |
| ConversionPipeline 支持 Stream 输出 | GUI 需要先预览内容再决定是否保存 |
| CLI 用 FormatRegistrar 显式注册 | 消除反射,支持 AOT/Trimming |
| Source Generator 注册新格式 | 添加格式只需加 [FormatPlugin] 属性 |
| 测试并行执行 | Phase 2 建立码表数据层(CodeData),静态可变字典全部改为 DI 只读表,竞态根因消除 |
| sealed record 实体 | 不可变性保证,不会意外修改中间状态 |