SnippetForm 产品功能文档
1. 概述
SnippetForm 是一个代码片段管理器,以多标签页 WinForms 窗体的形式,集中管理和快速调用分散在各配置文件、文件夹、甚至数据库中的文本片段。用户可通过关键字模糊检索,一键将目标片段粘贴到外部编辑器,或直接打开源文件。
第 1–8 节保留原 WinForms 设计与移植背景;第 9 节记录当前 Electron 项目在
sf..HEAD中实现的行为。
2. 核心功能
2.1 多标签页片段集合管理
- 每个标签页对应一个独立的片段集合(由一个
.cfg配置文件驱动)。 - 支持新增、打开、关闭集合。
- 双击标签页可关闭当前集合(前台固定页除外)。
2.2 片段检索与展示
- 列表视图展示片段的文本内容、来源路径、所在行号。
- 顶部过滤框支持实时关键字过滤。
- 支持显示行号(
ucListView.LineNumberType.small)。
2.3 一键调用(打开 / 粘贴)
- 双击或按 Enter:以默认模式执行当前选中项。
- 数字键 1-9, 0:以默认模式直接执行对应序号的片段,无需先选中(1 → 第 1 项,9 → 第 9 项,0 → 第 10 项,内部映射为
(num + 9) % 10)。 - Ctrl + 数字键:以 Ctrl 模式执行对应序号项。
- Alt + 数字键:在过滤框中插入该数字字符(不触发执行)。
- 行为模式由
Ctrl for paste复选框(配置项Snippet.CtrlForPaste,默认勾选)控制,逻辑为bOpen = bCtrl ^ CtrlForPaste:- 勾选时(默认):不按 Ctrl 执行打开(打开源文件/URL);按住 Ctrl 执行粘贴(最小化窗体 → 片段文本写入剪贴板 → 模拟
Ctrl+V粘贴到外部窗口)。 - 不勾选时:行为相反,默认粘贴,Ctrl 打开。
- 勾选时(默认):不按 Ctrl 执行打开(打开源文件/URL);按住 Ctrl 执行粘贴(最小化窗体 → 片段文本写入剪贴板 → 模拟
2.4 右键菜单(上下文操作)
| 菜单项 | 快捷键 | 说明 |
|---|---|---|
| Reload cfg | F5 | 重新读取当前集合的配置文件 |
| Edit cfg | Alt+E | 用外部编辑器打开当前 .cfg 文件 |
| Edit template | Alt+T | 打开 SQL 模板配置文件 |
| Open as file/dir | Alt+Shift+O | 将选中项的路径作为文件/目录打开 |
2.5 多数据源配置驱动
一个 .cfg 文件可混合配置以下数据源:
| 配置行格式 | 说明 |
|---|---|
# 注释 |
以 # 开头的行为注释 |
sql_raw=:<SQL语句> |
直接执行原生 SQL,结果按行拆分为片段 |
sql_模板名=:<参数1> <参数2> ... |
调用 sqlCfgPath 中预定义的 SQL 模板,支持 ${1}、${2} ... 占位符替换;参数可用空格或 " 包裹 |
filter_path=:<过滤文件路径> |
指定文件/目录过滤规则文件 |
<单个文件路径> |
读取该文本文件,按行拆分为片段 |
<文件夹路径> |
递归读取文件夹下所有 *.* 文件 |
<文件夹路径>\*.txt |
递归读取文件夹下匹配通配符的文件 |
片段文本规则:
- 文件首行非分类符(
@@)时,自动在前面加上@@作为标题。 - 空行会被跳过。
2.6 智能打开行为
根据选中项的路径和按键状态,Open 方法会执行不同动作:
- Shift + 打开:若片段文本包含 URL,直接打开该 URL。
- Alt + 打开:执行"Open as file/dir",在资源管理器中定位。
.zrtf文件:调用主窗体的AddUCEditor方法,在内部编辑器中打开。src_txt_sep目录下的文本:解析为内部笔记格式,打开对应.zrtf并跳转到指定行。- 普通文件:调用外部编辑器(路径由
Snippet.EditorPath配置),并自动模拟键盘输入Ctrl+G→ 粘贴行号 → 回车,实现跳转到指定行。 - 粘贴模式:将片段文本写入剪贴板,模拟
Ctrl+V粘贴到外部窗口。
3. 架构与组件
3.1 组件关系图
SnippetForm (Form)
├── TCManager (管理 TabControl)
│ └── TabPage ── SnippetTPClient (TabPageClient)
│ └── ucSnippet (UserControl)
│ └── ucListView (列表+过滤框)
├── btnGo : 执行当前选中项
├── btnCancel : 隐藏窗体
├── btnAdd : 新建并打开一个空白 .cfg
├── btnOpen : 打开已有 .cfg(支持多选)
└── chkCtrlForPaste : 切换 Ctrl 行为模式
3.2 关键类说明
| 类/接口 | 职责 |
|---|---|
SnippetForm |
主窗体。实现 IControlSearch 接口,可被外部搜索框驱动。窗体关闭时执行 Hide() 而非销毁。 |
TCManager |
TabControl 辅助类。管理 TabPageClient 与 TabPage 的绑定;支持固定页、双击关闭。 |
TCManager.TabPageClient (接口) |
每个标签页内容必须实现的接口:GetControl()、Close()、GetTitle()。 |
SnippetTPClient |
TabPageClient 的实现。负责 ucSnippet 的生命周期管理和工厂方法(Create / Open / Opens / CreateByConfig)。 |
ucSnippet |
核心用户控件。负责读取配置、加载片段、渲染列表、处理用户交互(双击/按键/右键菜单)。 |
ucListView |
通用列表视图控件。提供列表展示、过滤框、状态栏、行号显示。 |
4. 关键接口
4.1 TCManager.TabPageClient
public interface TabPageClient
{
Control GetControl(); // 返回要嵌入 TabPage 的用户控件
bool Close(); // 关闭前调用,返回 true 允许关闭
string GetTitle(); // 标签页标题
}
4.2 IControlSearch(SnippetForm 实现)
public interface IControlSearch
{
string GetName(); // 返回 "snippet"
void DoSearch(string text); // 将 text 写入当前 ucSnippet 的过滤框
int GetProgress(); // 返回 0
int GetResultCount(); // 返回 0
}
5. 配置文件格式详解
5.1 集合配置(.cfg)
# 这是一个片段集合配置示例
# 1. 直接执行 SQL
sql_raw=:SELECT title FROM notes WHERE tag = 'daily'
# 2. 使用 SQL 模板(模板定义在 sql.ucfg 中)
sql_normal=:ide
sql_test=:bash "a bc"
# 3. 文件/目录数据源
code\csharp\snippets.txt
code\python\
code\js\*.txt
# 4. 过滤规则
filter_path=:my_filter.txt
5.2 SQL 模板配置(sql.ucfg)
sql_normal=:SELECT content FROM snippets WHERE lang = '${1}'
sql_test=:SELECT * FROM logs WHERE level = '${1}' AND msg LIKE '%${2}%'
5.3 过滤文件(my_filter.txt)
通过 FileDirFilter 定义要排除的文件或目录,格式由 FileDirFilter 实现决定。
6. 移植指南
6.1 必须移植的文件
| 文件 | 说明 |
|---|---|
SnippetForm.cs / .Designer.cs |
主窗体 |
SnippetTPClient.cs |
TabPageClient 适配器 |
ucSnippet.cs / .Designer.cs |
核心用户控件 |
TCManager.cs |
TabControl 管理器 |
ucListView 相关文件 |
列表视图控件(若无可替换为标准 ListView + TextBox) |
6.2 外部依赖(cyb 库)
以下工具类来自 cyb 命名空间,移植时需替换或自行实现:
| 依赖项 | 用途 | 移植建议 |
|---|---|---|
cyb.TCManager |
已包含在 TCManager.cs |
直接复制 |
cyb.Config |
读取 sql.ucfg 键值对 |
可用 Dictionary<string,string> 或 INI 解析器替代 |
cyb.LVData / LVItem |
列表数据模型 | 自定义简单数据类即可 |
cyb.FileMy |
文件读写、递归搜索 | 用 System.IO.File、Directory.GetFiles 替代 |
cyb.ProcessMy |
启动外部进程 | 用 System.Diagnostics.Process 替代 |
cyb.Keybd_Event |
模拟键盘输入(粘贴、Ctrl+G、回车) | 可用 SendKeys.Send 或 P/Invoke keybd_event 替代 |
cyb.Utilities.IsControlDown / IsAltDown / IsShiftDown |
检测修饰键 | P/Invoke GetKeyState 或 Control.ModifierKeys |
cyb.PathMy.NormalizePath / ConvertToRelPath |
路径规范化 | 用 Path.GetFullPath 自行封装 |
cyb.StringMy.SplitSub |
按分隔符切分字符串 | 用 string.Split 替代 |
cyb.MyRegex.MatchOne / MatchOnes |
正则匹配辅助 | 用 System.Text.RegularExpressions.Regex 替代 |
cyb.DeleHelper.DlgtStringSInvoke |
跨线程 UI 调用 | 用 Control.Invoke 替代 |
cyb.ErrorHandler.Show |
异常弹窗 | 用 MessageBox.Show 替代 |
cyb.MenuMy.MakeItem |
创建菜单项 | 直接 new ToolStripMenuItem |
cyb.Classifier.ClassifyNodeEx |
数据库节点模型 | 自定义类,包含 tid, pid, txt, start, zrtfPath, nid 字段 |
TinyNodeHelper.GetTinyNodes |
执行 SQL 查询 | 用 System.Data.SQLite 或 Dapper 替代 |
FileDirFilter |
文件/目录过滤 | 自行实现白名单/黑名单过滤逻辑 |
AppCfg |
全局配置对象 | 替换为项目自身的配置管理类 |
FhUtil.OpenUrl |
打开浏览器 | 用 Process.Start 替代 |
6.3 最小化移植步骤
- 复制核心文件:将
SnippetForm、ucSnippet、SnippetTPClient、TCManager复制到目标项目。 - 替换 cyb 依赖:将上述 cyb 工具调用替换为 .NET 标准库或项目已有工具类。
- 替换配置系统:将
AppCfg相关调用(如AppCfg.aCfg[]、AppCfg.CfgRoot、AppCfg.mainForm等)替换为目标项目的配置/主窗体引用。 - 调整数据源:若不需要 SQL 数据源,可删除
GetSnippetsThread中的 SQL 分支;若需要,接入目标项目的数据库访问层。 - 调整打开行为:根据目标项目需求,修改
ucSnippet.Open()中的文件打开/编辑器调用逻辑。 - UI 适配:调整
SnippetForm和ucSnippet的控件布局以匹配目标项目的 UI 风格。
7. 注意事项
- 线程安全:
GetSnippets()在后台线程读取数据,完成后通过DeleHelper.DlgtStringSInvoke回刷 UI。移植时务必保证跨线程 UI 操作正确。 - 窗体生命周期:
SnippetForm关闭时调用e.Cancel = true; this.Hide();,属于单例常驻窗体。主程序通常通过懒加载属性(SpForm)持有实例,并通过快捷键(如hotKeyShow_SPF)反复显示/隐藏。 - 剪贴板与键盘模拟:
Open()方法大量使用SetClipBoardText+Keybd_Event.Paste()+Keybd_Event.KeyWitchCtrl('G')。若目标环境有安全限制或不需要自动跳转行号,可移除这部分逻辑。 - 路径硬编码:
Open()中硬编码了src_txt_sep和.zrtf的内部笔记格式处理。若目标项目无此概念,可直接删除该分支。
8. 扩展建议
- 热键自定义:将
hotKeyShow_SPF等注册逻辑从主窗体抽出,改为可配置。 - 插件化数据源:将文件/目录/SQL 三种数据源抽象为
IDataSource接口,便于新增数据源类型(如 Web API、Git 仓库等)。 - 搜索结果持久化:将
ucListView的过滤结果缓存,避免每次切换标签页重新加载。
9. 当前项目迁移实现
9.1 入口
- 片段管理器是独立 Electron 窗口,可从 View 菜单或命令面板打开。
- 默认系统级热键为
Ctrl+Alt+N,可在“设置 → 快捷键 → 热键”修改。热键被占用时,仍可从菜单打开。 - 可从 File 菜单或命令面板将当前项目注册为片段来源。
- 首次执行时,在应用数据目录的
snippet-projects中以项目名创建.cfg,递归读取项目中的 Markdown 文件。 - 同名配置已存在时追加短哈希,旧版哈希命名的配置仍会复用。
- 再次执行时复用已有集合,以项目名显示,移到首位后选中,并使片段窗口保持置顶。
- 首次执行时,在应用数据目录的
9.2 窗口管理
- 关闭窗口或按
Esc时隐藏而不销毁,系统焦点返回先前的窗口;编辑集合配置时,Esc仅关闭配置对话框。 - “窗口 → 切换窗口”和命令面板的同名命令仅在笔记主窗口之间轮换,不切换到片段窗口。
- 首次加载集合和刷新结果时显示加载动画;读取结束或失败后隐藏。查询完成前不显示“没有匹配的片段”。
9.3 Tab 管理
- 每个
.cfg集合占用一个 Tab。切换 Tab 时保留各自的查询条件、结果、选中项和滚动位置;刷新只更新当前集合。 - 按
Ctrl/Cmd+PageUp或Ctrl/Cmd+PageDown按顺序切换 Tab;按Ctrl/Cmd+Tab(Shift反向)从最近使用的集合中选择,选择器打开时可用↑、↓继续选择。 - 按
Ctrl/Cmd+W关闭当前 Tab。右键 Tab 可关闭当前、其他、左侧、右侧或全部集合,也可在文件管理器中显示对应.cfg。 - 默认按
Shift+Alt+R在文件管理器中显示当前集合;Tab 右键菜单打开时则显示菜单所指集合。快捷键提示跟随设置更新。 - 已打开的集合和当前集合保存在应用数据目录的
snippets.json中,不随笔记库切换。
9.4 配置
- 按
Ctrl/Cmd+N、Ctrl/Cmd+O、Ctrl/Cmd+E分别新建集合、打开集合、编辑当前配置;按F5刷新当前集合。打开集合支持多选,不复制原.cfg文件。 - “新建”和“打开”共用的对话框起始路径可在“编辑配置”中设置为现有文件夹的绝对路径。留空或目录已不可用时,由系统决定打开位置。
- 没有打开集合时仍可编辑全局配置。
- 单个来源文件大小上限默认为 2 MiB。
- 片段结果上限默认为 20000 条。
- 两者适用于所有集合。点击对应超限诊断可直接打开配置并聚焦该字段,保存后刷新当前集合。
- 集合配置或文件过滤配置在编辑期间被外部修改时,保存会提示冲突。两个配置字段统一以 UTF-8 写入。
9.5 集合数据源
.cfg每行可写一个文件、目录或文件名通配符,如notes\\*.txt;目录及通配符均递归读取。相对路径以.cfg所在目录为基准,空行和#注释行跳过。- 数据源保存在
.cfg,文件过滤配置独立保存在同名.cfg.ignore,因此过滤区注释和空行不会混入数据源。.cfg.ignore每行可写一个ignore:=过滤文件路径,可同时引用多个文件;相对路径以.cfg所在目录为基准。过滤文件沿用仓库.mynoteignore的 Gitignore 规则,命中的文件和目录不会加载。新建项目集合时默认填入仓库根目录的.mynoteignore。 - 每个非空文本行形成一条片段,保留来源路径和原文件行号。首个不以
@@开头的内容行在展示及复制时加上@@。 - 读取
.cfg、.cfg.ignore和片段来源文件时,先按 BOM 识别 UTF-8、UTF-16 LE/BE;无 BOM 时优先严格按 UTF-8 解码,失败则按 GBK 解码。 - 已加载集合的配置文件、来源文件和目录变化后,对应集合会标记为待更新;下次刷新、修改查询或切回 Tab 时重读。
- 可写
relay:=test.cfg或relay:=/abs/path/to/cfg.cfg引用上游集合当前查询的全部命中项;未打开的上游集合会在后台打开,不切换当前 Tab。上游未设查询时取全部条目。 - 接力和普通来源按配置顺序合并,以来源路径和行号去重,再由当前 Tab 查询。
- 上游变化后,下游会在下次查询或切回时刷新。
- 仅支持一级接力。上游再含
relay:=时,该条引用显示诊断,其余来源继续加载。
- 读取错误、超限和不支持的配置行显示在诊断区。
9.6 搜索
- 搜索复用 Quick Open 的子串、跳字模糊、多关键词、无声调拼音全拼和首字母匹配及排序。
f:仅筛选片段文本,p:仅筛选来源路径;命中字符在文本和路径中高亮。 - 搜索框旁的“拼音”按钮可切换全局拼音搜索并立即更新结果;“设置 → 快捷键”中配置的“切换拼音搜索”快捷键也可在此使用。
- 按
Ctrl/Cmd+F聚焦搜索框。成功使用片段时保存非空搜索词,最近 50 条去重历史保存在本机 Local Storage,并可用面板历史快捷键回看。 - 结果显示片段文本、来源路径和行号。空查询保留来源顺序;界面显示命中总数,列表渲染前 500 条。
9.7 片段调用和导出
- “ctrl for paste”默认勾选并保存在本机。勾选时,双击、按 Enter 或按
Alt+数字键打开来源,按住 Ctrl 则粘贴;取消勾选时两者互换。 Alt+1–Alt+9、Alt+0分别执行当前结果前十项的双击动作;单独输入数字可继续搜索。- 粘贴会将片段写入剪贴板、隐藏片段窗口,并向先前的外部应用发送
Ctrl+V(macOS 为Cmd+V)。行内“插入”执行相同操作,不依赖当前笔记。 - 跨应用粘贴依赖平台能力。
- Windows 会记住片段窗口打开或失焦时的外部窗口。
- macOS 需要授权系统辅助功能。
- Linux X11 需要
xdotool,Wayland 需要已运行的ydotool服务。 - 找不到外部窗口或系统工具不可用时,显示错误并重新打开片段窗口。
- “打开来源”将 Markdown 文件在应用内打开并跳转到来源行,其他文件交给系统默认程序;含 HTTP/HTTPS 网址的片段还可“打开链接”。
- “查询结果另存为”将当前查询命中的全部片段正文按结果顺序保存。默认文件名为
查询结果.md,可指定其他名称或扩展名;没有查询结果时不可用。
9.8 未迁移功能
- 当前不支持旧版
filter_path=:、sql_配置行、tnd、SQLite 和.zrtf内部跳转。
