Uninote
Uninote
用户根目录
brdr
common
programming
docs
后端试题
问题讨论

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 打开。

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 最小化移植步骤

  1. 复制核心文件:将 SnippetForm、ucSnippet、SnippetTPClient、TCManager 复制到目标项目。
  2. 替换 cyb 依赖:将上述 cyb 工具调用替换为 .NET 标准库或项目已有工具类。
  3. 替换配置系统:将 AppCfg 相关调用(如 AppCfg.aCfg[]、AppCfg.CfgRoot、AppCfg.mainForm 等)替换为目标项目的配置/主窗体引用。
  4. 调整数据源:若不需要 SQL 数据源,可删除 GetSnippetsThread 中的 SQL 分支;若需要,接入目标项目的数据库访问层。
  5. 调整打开行为:根据目标项目需求,修改 ucSnippet.Open() 中的文件打开/编辑器调用逻辑。
  6. 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 内部跳转。

README

cross-ref

点赞(0) 阅读(3) 举报
目录
标题