简介:这款插件名为 Geany-JSON-Prettifier,专门面向 Linux 环境下使用 Geany 的开发者,核心用途是把凌乱或压缩成单行的 JSON 数据重新排版,并附带格式校验与缩小功能。针对实际编辑中常见的格式混乱问题,它允许用户只格式化当前选中部分,也可一次处理同一文件里的多个独立 JSON 实体;缩进方式、正斜杠转义等选项均可按需调整,能够较好适配不同团队的代码风格,处理第三方接口返回或日志片段时尤为实用。压缩包以 zip 方式提供,共收录一百七十六个文件,大小约一百六十三 KB,从文件构成看以 C 语言源码和头文件为主,同时包含多组 JSON 输入样例、对应的 gold 预期输出,以及 CMake、configure 等构建脚本和说明文档,便于查看插件实现与编译部署。该资源在平台已有四百零二人学习下载,对于想改造 Geany 编辑体验的开发者和研究 C 语言插件开发的读者都具备参考价值。通过结合源码与测试用例,可以观察到插件如何借助 yajl 库完成解析、格式化、压缩与验证流程,为理解具体实现或二次扩展提供比较完整的线索。
1. 为什么我不再用外部工具,直接在 Geany 里格式化 JSON
以前我改 JSON 配置文件的习惯是先打开一个小网站,右键粘贴、格式化、再粘回 Geany。直到某天我意识到,这个流程有个致命问题,而它恰好是 Geany-JSON-Prettifier 解决的:文件在编辑器这边,格式化在网页那边,两边没有对照,改到一半想验证 JSON 只能整段复制粘贴。这个插件直接在当前文档上做格式化、美化、缩小和验证,全程不离开编辑器。适合每天和 JSON 格式打交道的人:嵌入式开发、自动化测试、写配置文件的运维,以及所有不想为了 JSON 格式化再开一个 GUI 工具的人。
2. JSON 格式化不是字符串替换:解析器选型与 Geany 插件机制
2.1 Geany 插件本质上是什么
Geany 的插件不是一个独立进程,而是一个共享库 .so。Geany 启动时扫描插件目录,dlopen 每一个 .so,然后从里面找 plugin_set_info 函数注册插件。写插件的人唯一要做的是把库编译出来、丢到插件目录、在插件管理器里打勾。对新手来说最迷惑的是 geany_data、geany_functions 这几个全局变量哪来的:它们是 Geany 在 dlopen 插件之后填充的,插件只需要声明,不要在代码里自己初始化。声明之后,插件里的任何一个函数都能拿到主窗口、当前文档、Scintilla 控件引用,这是 Geany 给插件开的后门。
一个最小插件骨架长这样:
#include <geanyplugin.h> GeanyPlugin *geany_plugin; GeanyData *geany_data; GeanyFunctions *geany_functions; static void json_format_document(GeanyDocument *doc, gboolean minify) { g_return_if_fail(doc != NULL); /* 取当前文档全文,交给 JSON 核心处理,再写回 */ gsize len = 0; const gchar *src = sci_get_contents(doc->editor->sci, &len); /* 调用 transform_json 得到新文本,再用 set_text 写回 */ } static void on_menu_format(GtkMenuItem *item, gpointer user_data) { GeanyDocument *doc = document_get_current(); json_format_document(doc, FALSE); } void plugin_init(GeanyData *data) { /* 往 Tools 菜单里挂一个菜单项 */ } void plugin_cleanup(void) { /* 释放自己申请的资源,Geany 不帮你兜底 */ } void plugin_set_info(GeanyPlugin *plugin) { plugin->funcs->init = plugin_init; plugin->funcs->cleanup = plugin_cleanup; plugin->info->name = "Geany-JSON-Prettifier"; plugin->info->description = _("JSON prettify, minify and validate"); plugin->info->version = "0.1"; plugin->info->author = "..."; }代码后的关键点:plugin_init 在插件被启用时调用一次,做菜单和状态栏的注册;plugin_cleanup 在插件被禁用或 Geany 退出时调用,负责释放资源;plugin_set_info 是 Geany 识别一个 .so 是不是合法插件的关键,name、description、version 都会显示在插件管理器的列表里。注意 Geany 大版本变换时这个结构体的字段会增删,所以不能拿旧头文件编新插件,否则插件管理器里会灰显。
2.2 字符串替换式格式化为什么是玄学
很多人第一次写 JSON 美化器会走捷径:扫描字符串,数花括号深度,遇到{或[就加换行和缩进。文件小看不出问题,一旦碰到 JSON 里的字符串值,就会翻车。看这个例子:
{ "msg": "{\"code\":0}", "url": "https://example.com/a?b={c}" }如果用「数花括号」的方式处理,字符串里的{}也被当成结构符号,缩进立刻错乱;更麻烦的是转义引号\"和普通引号混在一起时,字符串边界的判断很容易写错——你以为遇到双引号就结束了,实际那是字符串内容的一部分。哪怕勉强处理对了,还要自己实现转义字符保留、Unicode 处理、键值之间空格的规则,等于重写一遍 JSON 规范。正确做法是先解析成语法树,再从语法树重新输出文本,解析器负责处理所有合法格式,输出端只负责排版。
2.3 json-c 和 cJSON 怎么选
我一般选 json-c,理由很直接。json_tokener_parse_ex 能拿到解析失败的错误类型和出错位置,验证功能要靠它;原生提供 JSON_C_TO_STRING_PRETTY,美化输出不用自己拼缩进;Debian/Ubuntu/Fedora 都有 libjson-c-dev 包,apt 或 dnf 直接装,不用往项目里扔第三方源码。cJSON 更轻,适合嵌入式这种几 KB 内存的场景,但它的解析错误定位能力弱一截,对「验证并报错」这个需求不够。这个插件项目里用的是 json-c 路线,我跟着这个方向走。
有人会问:不是有 jq 吗?jq 确实能把 JSON 格式化得很好,但它是外部进程,在编辑器场景里用起来是另一套交互:切到终端、跑命令、读输出、再贴回编辑器。插件和 jq 不是竞争关系,插件解决的是「不离开编辑器的即时反馈」,jq 解决的是「脚本管道里的文本处理」。我两个都用,日常改配置用插件,流水线脚本里用 jq。核心处理函数长这样:
#include <json-c/json.h> #include <string.h> /* 返回值:TRUE 转换成功,*out 里是格式化或缩小后的文本 */ static gboolean transform_json(const gchar *src, gchar **out, gboolean minify, gchar **error_msg) { struct json_object *root = json_tokener_parse(src); if (root == NULL) { *error_msg = g_strdup("json_tokener_parse failed: not valid JSON"); return FALSE; } if (minify) { *out = g_strdup(json_object_to_json_string(root)); } else { *out = g_strdup(json_object_to_json_string_ext( root, JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED)); } json_object_put(root); /* 释放解析出的对象树 */ return TRUE; }逻辑说明:第一步 json_tokener_parse 把整段文本解析成 json_object 树,这一步同时就是验证,解析失败返回 NULL;第二步根据 minify 参数选择输出形式,缩小用紧凑模式,美化用 PRETTY 标志;最后 json_object_put 释放对象树,避免内存泄漏。两个输出函数的区别在于:json_object_to_json_string 单行输出、无多余空白;加上 JSON_C_TO_STRING_PRETTY 之后会按两空格缩进输出多行文本,数组和对象的结构层次一眼能看出。JSON_C_TO_STRING_SPACED 控制键值冒号后是否加空格,一般和 PRETTY 一起用。
这段值得多说一句:解析成功但序列化后内容和原文不逐字节一致是正常的,JSON 对象本身没有「原样」概念,{}之间的空白、缩进、换行在解析后全部丢弃,重新输出时按我们的排版规则生成。这一点正是格式化器区别于文本替换器的价值。
2.4 验证路径要单独实现
格式化时 parse 返回 NULL 只告诉我们「这段 JSON 坏了」,用户体验上还差一步:坏在哪一行。json-c 的 tokener 能做更细的错误定位:
struct json_tokener *tok = json_tokener_new(); struct json_object *obj = json_tokener_parse_ex(tok, src, strlen(src)); enum json_tokener_error err = json_tokener_get_error(tok); if (err != json_tokener_success) { int pos = json_tokener_get_parse_end(tok); /* 用 pos 算行号列号,拼成 "line 3 column 12: unexpected character" */ } json_tokener_free(tok);参数说明:json_tokener_new 创建解析器上下文,parse_ex 比 parse 多返回错误状态;json_tokener_get_parse_end 返回的是错误发生处的字节偏移 pos,注意是字节不是字符,中文等多字节字符需要按 UTF-8 边界处理;拿到 pos 后从文本开头数换行符就能算出第几行第几列,这也是后面做「点击错误自动跳转」的基础。验证路径建议独立于格式化走:先验证、再格式化,避免两者耦合成一个按钮,用户想看错误详情时反而被格式化结果干扰。顺便说一句,选中区域格式化也是这类插件最常见的需求,实现上只需要把 sci_get_contents 换成 sci_get_selected_text,处理后用 sci_replace_sel 替换选中区,核心 transform_json 不用改动,入口层换两个函数就够了。
3. 从源码编译成 .so 并装上 Geany:依赖、路径与版本匹配
3.1 编译需要哪三样东西
编译这个插件要三类依赖。第一是 Geany 的开发头文件,Debian/Ubuntu 系叫 libgeany-dev,CentOS/Fedora 系可能是 geany-devel,Arch 上直接装 geany 本体就带;它提供 geanyplugin.h 和 geany.pc,没有它就没有 GeanyPlugin 结构体和 sci_get_contents 这些封装。第二是 json-c 开发包,Debian/Ubuntu 叫 libjson-c-dev,提供 json.h 和 pkg-config 元数据。第三是 pkg-config 本身,用来把上面两个库的编译参数展开到命令行。装完之后先验证一步,能同时查到 geany 和 json-c 才算齐了:
sudo apt install geany libgeany-dev libjson-c-dev pkg-config pkg-config --modversion geany json-c第二行命令会输出两个版本号,如果某个报 not found,说明对应开发包装的位置不在 pkg-config 搜索路径里。常见做法是把 PKG_CONFIG_PATH 指到 .pc 文件所在目录,比如/usr/lib/x86_64-linux-gnu/pkgconfig。
3.2 编译命令与 makefile 模板
整个插件可以只从一个 .c 文件编出来:
gcc -std=c11 -fPIC -shared \ -o json_prettifier.so json_prettifier.c \ $(pkg-config --cflags --libs geany json-c)参数含义:-std=c11 指定 C 标准;-fPIC 是生成共享库必需的位置无关代码;-shared 告诉编译器产物是 .so 而不是可执行文件;pkg-config --cflags 展开出 -I 头文件路径,--libs 展开出 -l 链接库和 -L 库路径。这里不用 -Wl,-soname 之类的花活,Geany 按文件名识别插件。最容易翻车的地方是 geanyplugin.h 找不到,现象是编译直接报 fatal error,先确认 libgeany-dev 装了、pkg-config 能查到 geany。
我一般建议把编译命令沉淀成 makefile,方便改一个参数就重编:
CC ?= gcc CFLAGS ?= -std=c11 -Wall -Wextra -O2 PLUGIN := json_prettifier.so SRCS := json_prettifier.c $(PLUGIN): $(SRCS) $(CC) -fPIC -shared $(CFLAGS) \ -o $@ $(SRCS) \ $(shell pkg-config --cflags --libs geany json-c) clean: rm -f $(PLUGIN) install: $(PLUGIN) install -Dm755 $(PLUGIN) $(DESTDIR)$(PLUGIN_DIR)说明:CFLAGS 里加 -Wextra 是为了把 json-c 返回值忽略这类警告露出来;install 目标用 DESTDIR 支持打包,PLUGIN_DIR 在命令行传,下面讲路径时用得上。实际编译时最常遇到的错误是 geany.pc 不在 pkg-config 搜索路径里,发行版把 .pc 装到了 /usr/lib/pkgconfig 而系统走了 /usr/lib/x86_64-linux-gnu/pkgconfig,这时把 PKG_CONFIG_PATH 指过去即可。
3.3 .so 该放哪,插件管理器从哪读
Geany 查插件目录靠编译时的前缀和用户数据目录,不同版本路径不完全一样,常见做法是这两条:
- 系统级:/usr/lib/geany/ 或者 /usr/lib/x86_64-linux-gnu/geany/
- 用户级:新版 Geany 习惯放 ~/.local/share/geany/plugins/,老教程里常见的 ~/.config/geany/plugins/ 在部分发行版已经不被扫描
最稳的确认方式不是猜路径,而是跑一下:
geany --print-prefix这个命令输出 Geany 自己认为的安装前缀,插件目录一般就是$prefix/lib/geany。把编译好的 .so 复制过去之后,先做一次文件类型确认,防止编译器因为参数问题产出 0 字节文件:
file json_prettifier.so readelf -d json_prettifier.so | grep -i neededreadelf 的 needed 段列出它链接的共享库,如果缺 libjson-c.so 或者 libgeany 的版本号对不上,加载时会直接报错,这一步能提前暴露问题。Windows 和 macOS 上路径逻辑类似,但 Geany 的插件机制这两个平台支持较弱,如果主力是 Windows,我更建议直接用 Linux 环境或者 WSL 编译,省掉一半折腾。
3.4 启用与验证
Geany 菜单「工具 -> 插件管理器」,找到 Geany-JSON-Prettifier 打勾。这里有个习惯:改插件状态后重启一次编辑器再确认菜单项,因为部分版本启停只做了一半,菜单没有即时刷新。验证成功的标志是 Tools 菜单下出现 Format JSON、Minify JSON、Validate JSON 三个动作。如果列表里这个名字灰显,或者干脆看不到,回头查 3.3 的放置路径和 readelf 结果,多数是路径或依赖问题。启用之后第一件事是打开一个已知损坏的 JSON 文件跑 Validate,确认消息窗口输出的错误行号和列号是对的,再打开一个正常文件跑 Format,确认缩进写回正常。这两步过了,插件才算真正装好。
4. 格式化、缩小、验证三合一:快捷键绑定与参数调优
4.1 三个动作的分工
- Format JSON:美化整个文档,处理缩进换行,最常用。
- Minify JSON:压成单行紧凑文本,适合塞进命令行参数或者减少日志体积。
- Validate JSON:只验证不重写,出错时在状态栏和消息窗口给出错误行号列号。
三个动作共用同一套核心,只是输出端参数不同。我倾向于验证单独做,因为验证失败时你不想失去原文件的任何信息。格式化是写回文档,验证是只读操作,两者语义要分清。有人会想做成一个按钮「坏了就格式化,没坏就报错」,实际体验很别扭:你想确认文件有没有问题时,按钮一旦顺手点成格式化,缩进就被改了,git diff 一片噪音。分开之后,「我就想看看这个文件坏没坏」这个高频动作才足够安全。
4.2 菜单与快捷键怎么绑
Geany 主菜单挂菜单项有现成封装,也可以在 plugin_init 里用 Gtk 容器直接加:
static void on_menu_minify(GtkMenuItem *item, gpointer user_data) { GeanyDocument *doc = document_get_current(); json_format_document(doc, TRUE); } void plugin_init(GeanyData *data) { GtkWidget *tools_menu = geany->main_widgets->tools_menu; GtkWidget *item = gtk_menu_item_new_with_mnemonic(_("Minify JSON")); g_signal_connect(item, "activate", G_CALLBACK(on_menu_minify), NULL); gtk_container_add(GTK_CONTAINER(tools_menu), item); gtk_widget_show(item); }说明:geany->main_widgets->tools_menu 是 Geany 主窗口的 Tools 菜单容器,把 GtkMenuItem 挂进去就出现在菜单里;g_signal_connect 绑定 activate 信号,点击菜单触发回调;回调里 document_get_current() 拿当前文档,传给核心处理函数。菜单项文字里的下划线表示助记符,比如 _("Format JSON") 在菜单里显示为 F 带下划线,Alt+F 触发。
真正的自定义快捷键一般交给用户做:Geany 的 keybindings 配置写在 ~/.config/geany/keybindings.conf,插件注册的 keybinding 组会出现在「首选项 -> 键位绑定」里,用户可以改成任何组合。插件自己别硬编码全局快捷键,容易和系统热键撞车。我一般设默认 Ctrl+Alt+J(Format)、Ctrl+Alt+M(Minify)、Ctrl+Alt+V(Validate),真冲突了就让用户在键位绑定面板里改。
4.3 参数怎么调:缩进、数组换行、键排序
json-c 的 PRETTY 输出默认两空格缩进,这对很多人不够:有人习惯四空格、有人要 Tab。常见做法是自己遍历 json_object 树做输出控制,而不是吃 json_object_to_json_string_ext 的默认输出。下面这段是我常用的一种方式,利用 json_object_object_foreach 遍历对象并手工控制缩进:
static void append_indent(GString *out, int depth, int width) { g_string_append_len(out, " ", depth * width); } static void dump_json_object(const struct json_object *obj, GString *out, int depth, int width) { enum json_type type = json_object_get_type(obj); if (type == json_type_object) { g_string_append_c(out, '{'); json_object_object_foreach((struct json_object *)obj, key, val) { g_string_append_c(out, '\n'); append_indent(out, depth + 1, width); g_string_append_printf(out, "\"%s\": ", key); dump_json_object(val, out, depth + 1, width); } if (json_object_object_length(obj) > 0) g_string_append_c(out, '\n'); append_indent(out, depth, width); g_string_append_c(out, '}'); } else if (type == json_type_array) { /* 数组分支:每个元素一行,或按配置决定 */ } else { g_string_append(out, json_object_to_json_string(obj)); } }参数说明:depth 当前嵌套深度,width 是每级缩进的空格数,运行时通过配置项传入;json_object_object_foreach 是 json-c 的宏,按插入顺序遍历对象键值对,这正是「格式化后键不重排」的关键;叶子节点直接复用 json_object_to_json_string 让 json-c 处理字符串转义和数字格式。这段输出逻辑和 json-c 内置 PRETTY 最大的区别是 width 可配置,同时保留了字符串内的转义处理。数组每个元素占一行的开关也可以在这个框架里做:判断数组元素是对象还是标量,对象就每个占一行,标量数组保持一行,视觉上更紧凑,这是我在实际用过一段时间后觉得最舒服的折中。
4.4 配置项清单与持久化
暴露在设置对话框里的参数我用这些:
| 配置项 | 可选值 | 说明 |
|---|---|---|
| indent_width | 2 / 4 / 8 / tab | 每级缩进宽度,走 4.3 的自定义输出函数 |
| array_per_line | on / off | 数组元素强制每个占一行,长数组更清晰 |
| sort_keys | on / off | 对象键按字典序排序,默认 off 保持原顺序 |
| allow_trailing_comma | on / off | 容忍尾逗号,默认 off,JSONC 场景可开 |
| jump_to_error | on / off | 验证失败时把光标跳到错误行,默认 on |
sort_keys 实现时注意:json_object_object_foreach 是插入序,要排序得先取出所有 key 放 g_strcmp0 排序再遍历。allow_trailing_comma 用纯 json-c 标准模式做不了,常见做法是在源码里先剥掉最后一个逗号再解析,副作用是错误行号偏移,所以打开这个开关后错误定位要重新计算。配置持久化我用 Geany 的 prefs 机制,本质是写 ~/.config/geany/geany.conf,插件启动时读一次,设置对话框里改完立即写回。
提示:改了缩进参数后,建议先用 Validate 跑一遍样本文件再格式化正式文件,防止自定义输出函数在数组嵌套边界出 bug。
参数改完后用一个小测试集回归:空对象、嵌套数组、带转义字符串、Unicode 中文、数字边界。我一般会准备五六个样本文件,跑一遍格式化再跑一遍验证,确认输出和 jq 的结果一致。回归这个动作不花多少时间,但能挡住「改排序把转义弄坏」这类翻车。
5. 避坑与排查:版本不匹配、编码错乱、注释误报
5.1 插件管理器里 .so 灰显
现象:把编译好的 json_prettifier.so 放进目录,插件管理器列表里能看见名字,但复选框是灰的,点不动。
原因:用旧版本的 geanyplugin.h 编出的 .so 拿到新版本 Geany 上加载,GeanyPlugin 结构体字段对不上,插件机制认为它不是合法插件;也可能是缺符号,json-c 或 libgeany 的 soname 版本和编译时不匹配,dlopen 直接失败。
解决:先跑 geany --version 确认当前版本,用相同版本的 libgeany-dev 重编;然后看启动日志,Geany 用 --debug 参数启动时会打印 dlopen 失败的具体原因,undefined symbol 会明确写出缺哪个函数。重编时顺手跑一下 readelf -d json_prettifier.so,看 NEEDED 里的 libgeany 版本号,和 /usr/lib 下实际存在的对比。这个坑我踩过两次,每次都是升级系统后 libgeany-dev 变了、旧 .so 忘了重编。
5.2 格式化后中文全变乱码
现象:原本正常的配置文件,格式化后中文注释或字符串值变乱码,英文数字正常,有时还出现方框字符。
原因:文件不是 UTF-8 编码,比如 Windows 传过来的 GBK 文件。Geany 用 UTF-8 读,格式化后按 UTF-8 写回,字节被二次解释,自然乱码。
解决:动手前先看编辑器窗口右下角的编码提示;如果不是 UTF-8,先「文档 -> 设置编码 -> 转换为 UTF-8」,再格式化。这是老玩家的条件反射:换编码永远先转换再操作,而不是直接保存。还要注意 BOM,文件头有 BOM 时 json-c 会把 \xEF\xBB\xBF 当成非法字符,报错位置在第一个字符,看起来很像文件坏了,实际只是 BOM 没剥。
5.3 合法 JSON 被报错,原来是注释和尾逗号
现象:前端工程里常见的带 // 注释、对象尾逗号的 json 文件,插件报 parse failed,错误位置指向注释或逗号附近。
原因:JSON 标准和 JavaScript 对象字面量不是一个东西,标准 JSON 不允许注释也不允许尾逗号,解析器严格按 RFC 8259 来。这类文件严格说叫 JSONC,很多工程工具默认容忍,但 json-c 的标准 tokener 不认。
解决:两个选择,一是先把注释和尾逗号清掉再用,二是打开配置里的 allow_trailing_comma。尾逗号可以用预处理剥掉,注释只能靠逐行分析摘除,但要注意字符串值里的 // 不能被误伤,判断逻辑必须跟踪当前是否在字符串内部,逃不开 2.2 节说的字符串边界问题。工程上如果是自己的项目,我一般建议顺手把这类文件改成标准 JSON,格式化器也好、其他工具也好,兼容成本最低。
5.4 大文件格式化把 Geany 卡死
现象:打开一个十几 MB 的 JSON 日志点格式化,编辑器整个无响应几十秒,期间 CPU 100%,风扇狂转。
原因:json-c 把整段文本解析成完整对象树,再序列化成另一个字符串,内存和 CPU 都随文件规模二次放大;Geany 是单进程编辑器,主线程被占用,界面就冻结。中途没有任何取消入口,只能等它跑完。
解决:先用 Validate 确认文件是合法 JSON 再格式化,避免「解析失败还要浪费一次完整遍历」;如果文件确实大,先 Minify 一次,文本体积降下来再跑 PRETTY,内存峰值能小不少。真正几十 MB 的日志型 JSON 不适合在编辑器内处理,我一般的做法是先用脚本按顶层 key 切出问题片段,格式化片段而不是整个文件,这样既不卡,git diff 也干净。
5.5 格式化后 git diff 满屏飘红
现象:格式化完提交,git diff 显示整个文件每个行都变了,reviewer 根本没法看语义改动。
原因:原文件用的 CRLF 换行被写成了 LF,或者缩进宽度从 4 变 2,diff 按行比对时全部命中。不是格式化逻辑错了,是换行符和缩进策略动了全局。
解决:提交前用 git diff --ignore-all-space -w 对比,肉眼确认只有缩进变化;如果不想污染历史,把格式化限制在改动区块,只格式化你正在改的对象。从此我每次动完 JSON 配置都会看一眼 diff 的统计行数,如果改动行数远超实际修改范围,优先怀疑换行符被整体改写,而不是内容变了。
6. 进阶技巧:错误行跳转与命令行批量格式化
6.1 验证失败自动跳到出错行
json_tokener_get_parse_end 拿到的是字节偏移,把偏移换成行号是验证体验的关键。行号换算用普通循环数换行符即可,然后通过 Geany 的 Scintilla 封装把光标移过去:
static void jump_to_pos(GeanyDocument *doc, int byte_pos) { int line = 0; const gchar *text = sci_get_contents(doc->editor->sci, NULL); for (int i = 0; i < byte_pos && text[i]; i++) if (text[i] == '\n') line++; sci_send_message(doc->editor->sci, SCI_GOTOLINE, line, 0); }说明:sci_send_message 把 SCI_GOTOLINE 指令发给当前文档的 Scintilla 控件,光标跳到出错行;参数 line 是从 0 计数的行号,所以不用加一。这个函数配合验证流程,点一下 Validate 就能看到错误位置,比状态栏里的一行文字直观太多。
6.2 把核心函数复用成命令行工具
插件里的 transform_json 完全可以在命令行场景复用。抽出相同逻辑编译一个独立小工具,配合 find 批量格式化配置文件:
find . -name "*.json" -not -path "*/node_modules/*" -print0 | \ xargs -0 -n 1 -P 4 jsonfmt --mode pretty --indent 4说明:-P 4 表示四个并发进程,处理几十个 json 文件时比逐个跑快得多;--indent 4 对应插件里的 indent_width 配置,命令行工具的缩进参数和 GUI 参数保持同一个默认值。用这种方式,CI 里也能顺手加一步 jsonfmt --mode validate,格式不合规直接失败。要从插件工程里长出一个 CLI,只需要把 transform_json 放进单独 .c 文件,main 里读文件、调 transform_json、写回,整个改动不到一百行。这个便利是我后来体会到的最大的隐性收益:插件和 CLI 共用同一套解析和输出逻辑,GUI 里验证过的规则,命令行批量跑一遍结果完全一致,不会有两边行为漂移。从那以后我每次动 JSON 配置都强制走一遍验证、格式化、git diff -w 三步,再批量交给 CLI 收尾,这套流程用了很久都没翻过车,希望帮到你。
本文还有配套的精品资源,点击获取