Windows Terminal 默认配置文件设计:profiles 的 defaults 与 list 结构是如何确定的
Windows Terminal 默认配置文件设计profiles 的 defaults 与 list 结构是如何确定的【免费下载链接】terminalThe new Windows Terminal and the original Windows console host, all in the same place!项目地址: https://gitcode.com/GitHub_Trending/term/terminal本篇围绕 Windows Terminal仓库中的 Cascadia / 新一代终端设置模型的设计规格文档 #2325 - Default Profile Settings.md 展开它记录了终端设置模型中“如何为所有配置文件Profile提供公共默认值”这一需求的完整设计过程——从四个候选方案defaultSettings全局对象、__default__魔法 GUID、把profiles改为含defaults与list的对象、inheritFrom配置继承的利弊权衡到最终被采纳的方案 3以及该方案在当前仓库源码中的真实落地方式向后兼容的运行时类型判别、baseLayerProfile基础层配置、IInheritable继承树。读完本文你可以理解终端 JSON 设置中profiles对象结构的由来、四个方案被否决/采纳的具体原因并能在自己的终端设置文件中正确使用defaults公共配置块。背景为什么需要“默认 Profile 设置”规格文档的摘要部分点明了核心诉求用户经常有一些希望在所有Profile 上统一生效的公共设置例如亚克力背景、字体、字号而不想逐个 Profile 手工重复配置。文档作者为 Mike Griesezadjii-msft创建于 2019-11-13对应 issue 编号 #2325。文档还交代了这个设计讨论的由来在实现该功能的原始 PR#3369评审过程中团队对“如何把这个能力暴露给用户”产生了分歧于是先写规格、穷举方案、再做决策——这也是本文值得阅读的地方它完整保留了四个候选方案的 JSON 形态、各自的优势与顾虑而不只是给出结论。方案一全局设置中的defaultSettingsProfile 对象第一个提案是在根设置对象里增加一个defaultSettings属性用一个“漂浮”在全局层的 Profile 对象承载所有默认值。完整示例如下{ $schema: https://aka.ms/terminal-profiles-schema, defaultProfile: {61c54bbd-c2c6-5271-96e7-009a87ff44bf}, defaultSettings: { useAcrylic: true, acrylicOpacity: 0.1, fontFace: Cascadia Code, fontSize: 10 }, requestedTheme : dark, showTabsInTitlebar : true, profiles: [ { guid: {61c54bbd-c2c6-5271-96e7-009a87ff44bf}, name: Windows PowerShell, commandline: powershell.exe, hidden: false }, { guid: {0caa0dad-35be-5f56-a8ff-afceeeaa6101}, name: cmd, commandline: cmd.exe, hidden: false } ], schemes: [], keybindings: [] }优势封装清晰所有默认 Profile 设置集中在一个对象里扫一眼设置文件就能定位默认值在哪里。易于理解只有一个对象作用于其后所有 Profile语义直白。顾虑命名困难团队对属性名反复权衡却始终没有满意答案defaultSettings容易与defaults.json概念混淆defaultProfileSettings会被理解为“默认那个 Profile 的设置”defaults同样与defaults.json冲突baseProfileSettings不够直观profiles.defaults、inheritedSettings、rootSettings、globalSettings、profileSettings、profilePrototype等候选也都不被看好。全局层里为何多出一个“悬浮 Profile”用户可能会困惑这个 Profile 对象为什么会出现在全局设置里它难道就是默认 Profile 吗方案二用户profiles列表中的__default__Profile 对象第二个提案是不新增全局属性而是在profiles数组里放一个 GUID 为__default__的特殊 Profile它承载所有公共默认值{ $schema: https://aka.ms/terminal-profiles-schema, defaultProfile: {61c54bbd-c2c6-5271-96e7-009a87ff44bf}, requestedTheme : dark, showTabsInTitlebar : true, profiles: [ { guid: __default__, useAcrylic: true, acrylicOpacity: 0.1, fontFace: Cascadia Code, fontSize: 10 }, { guid: {61c54bbd-c2c6-5271-96e7-009a87ff44bf}, name: Windows PowerShell, commandline: powershell.exe, hidden: false }, { guid: {0caa0dad-35be-5f56-a8ff-afceeeaa6101}, name: cmd, commandline: cmd.exe, hidden: false } ], schemes: [], keybindings: [] }优势同样把默认设置封装在一个对象里不过因为该对象可以位于列表任意位置清晰度略逊于方案一。默认 Profile 与其他 Profile 归入同一个profiles列表结构上“都在一个地方”。顾虑神秘的__default__GUID认定该 Profile 特殊的唯一手段是给它一个常量字符串而这个字符串并非合法 GUID本身就值得怀疑。反直觉靠添加一个带神秘guid的 Profile 来充当全局默认这一机制没有任何文档之外的提示能告诉用户“加这个魔法配置块就能影响所有 Profile”。一个 Profile 凭什么作用于其他所有 Profile列表中某一项影响其余各项这在直觉上很难自洽。方案三把profiles改为含list与defaults的对象最终采纳第三个提案改变了profiles的类型从数组变为对象对象内含defaults默认配置块和listProfile 数组两个键{ $schema: https://aka.ms/terminal-profiles-schema, defaultProfile: {61c54bbd-c2c6-5271-96e7-009a87ff44bf}, requestedTheme : dark, showTabsInTitlebar : true, profiles: { defaults: { useAcrylic: true, acrylicOpacity: 0.1, fontFace: Cascadia Code, fontSize: 10 }, list:[ { guid: {61c54bbd-c2c6-5271-96e7-009a87ff44bf}, name: Windows PowerShell, commandline: powershell.exe, hidden: false }, { guid: {0caa0dad-35be-5f56-a8ff-afceeeaa6101}, name: cmd, commandline: cmd.exe, hidden: false } ] }, schemes: [], keybindings: [] }优势与 Profile 归组默认值与 Profile 列表同处一个profiles对象之下语义关系一目了然。向后兼容文档特别指出借助 Jsoncpp 可以在运行时判断profiles是数组还是对象——是数组就回退到原有行为必然没有defaults是对象就进入新结构取defaults与list。用户既有设置文件因此不会被破坏。顾虑Schema 变更幅度不小profiles从 Profile 对象列表变成了内嵌列表的对象。但如上所述可以通过运行时类型判别平滑升级因此文档认为“并非重大问题”。所有 Profile 多一层缩进四层缩进让一些人不太舒服——这是纯粹的体验层面顾虑。方案四Profile 中的inheritFrom继承机制第四个提案引入了通用的配置继承给Profile增加inheritFrom属性指向父 Profile 的 GUID支持多层继承链{ $schema: https://aka.ms/terminal-profiles-schema, defaultProfile: {61c54bbd-c2c6-5271-96e7-009a87ff44bf}, requestedTheme : dark, showTabsInTitlebar : true, profiles: [ { guid: {11111111-1111-1111-1111-111111111111}, hidden: true, useAcrylic: true, acrylicOpacity: 0.1, fontFace: Cascadia Code, fontSize: 10 }, { guid: {61c54bbd-c2c6-5271-96e7-009a87ff44bf}, inheritFrom: {11111111-1111-1111-1111-111111111111}, name: Windows PowerShell, commandline: powershell.exe, hidden: false }, { guid: {0caa0dad-35be-5f56-a8ff-afceeeaa6101}, inheritFrom: {11111111-1111-1111-1111-111111111111}, name: cmd, commandline: cmd.exe, hidden: false }, { guid: {0caa0dad-ffff-5f56-a8ff-afceeeaa6101}, inheritFrom: {0caa0dad-35be-5f56-a8ff-afceeeaa6101}, name: This is another CMD, commandline: cmd.exe /c myCoolScript.bat, hidden: false } ], schemes: [], keybindings: [] }优势无需大改既有设置模型只是给Profile加一个新属性文件结构基本不变。属性名唯一inheritFrom相对已有键名非常独特不易冲突。表达力强允许任意多层配置分组。用户可以把公共设置拆成多套“准默认”分组而不被单一的“default”Profile 绑死例如一套给所有 WSL ProfilestartingDirectory设为~、fontFace设为 Ubuntu Mono一套给所有 PowerShell Profile等等。顾虑GUID 对人不友好inheritFrom只能引用 GUID 才能保证唯一标识而示例中手写的{11111111-1111-1111-1111-111111111111}尚且易读继承自“真实”GUID 的 Profile 时如“This is another CMD”继承自 “cmd”其inheritFrom值一眼看上去完全不传达“cmd”的含义。必须防循环引用多层叠加时要确保继承链不成环实现上更棘手。与设置 UI 如何协同用户在 UI 中编辑某个继承 Profile 时改动应该只写到最上层 Profile 吗UI 又该如何向用户传达“此 Profile 正在从其他 Profile 继承设置”心智负担更重用户需要自己在脑中构建一棵继承树才能理解某个 Profile 的最终设置从何而来。结论采纳方案三方案四进入特性积压规格文档给出的最终结论是团队选择了方案 3主要卖点为——把新的“默认 Profile 设置”与其余 Profile 设置归组在一起虽然是 Schema 变更但不是破坏性变更运行时类型判别保证旧文件可用查看设置时容易理解两者的关系。同时团队表示方案四的理念也不错但对这样一个相对简单的需求而言过于“重”了于是将其放入终端特性积压清单issue #3818。文档的 Resources 部分还列出了三个关联事项原始 issue #2325Default Profile for Common Profile Settings、原始 PR #3369Add support for User Default settings以及 #3818Add support for inheriting and overriding another profiles settings。源码印证defaults/list结构在当前实现中的落地以上设计并非纸面方案当前仓库的终端设置模型src/cascadia/TerminalSettingsModel/已经按方案 3 落地并且向后兼容逻辑与文档描述一致。关键解析入口_parseJson的数组/对象判别在 CascadiaSettingsSerialization.cpp 中两个键名常量正是文档方案 3 的产物static constexpr std::string_view DefaultSettingsKey{ defaults }; static constexpr std::string_view ProfilesListKey{ list };见 CascadiaSettingsSerialization.cpp#L38-L39。而_parseJson的实现精确对应了文档中“如果是数组就回退到旧行为”的兼容策略const auto profilesObject _getJSONValue(root, ProfilesKey); const auto profileDefaults _getJSONValue(profilesObject, DefaultSettingsKey); const auto profilesList profilesObject.isArray() ? profilesObject : _getJSONValue(profilesObject, ProfilesListKey);见 CascadiaSettingsSerialization.cpp#L1041-L1044当profiles是数组isArray()为真时整个对象被直接当作 Profile 列表defaults取不到即为空当它是对象时才读取list键。这与规格文档 Proposal 3 中 “With Jsoncpp, we can determine at runtime if an object is an array or an object” 的表述完全吻合。defaults被解析为“基础层 Profile”解析出的defaults块并不只是简单合并而是被构造成一个基础层 Profilesettings.baseLayerProfile Profile::FromJson(json.profileDefaults);见 CascadiaSettingsSerialization.cpp#L873。也就是说profiles.defaults中的每个属性都走与具体 Profile 相同的反序列化路径字体、颜色、外观等全部有效再作为所有 Profile 的“基座”参与继承。官方 Schema 与实际设置文件官方 JSON Schema profiles.schema.json 中profiles被定义为同时包含list与defaults两个属性的对象见该文件第 3258、3261 行附近确认了方案 3 的结构已成为正式契约。仓库自带的用户默认设置 userDefaults.json 本身就采用新结构profiles: { list: [ { guid: {61c54bbd-c2c6-5271-96e7-009a87ff44bf}, name: Windows PowerShell, commandline: %SystemRoot%\\System32\\WindowsPowerShell\\v1.0\\powershell.exe, hidden: false }, ... ] }系统默认 defaults.json 中的defaultProfile: {61c54bbd-c2c6-5271-96e7-009a87ff44bf}Windows PowerShell 的 GUID与规格文档所有示例中使用的 GUID 一致便于对照阅读。继承机制在模型层的一般化defaults块之所以能“作用于所有 Profile”底层依赖的是设置模型中更通用的父/子继承体系。IInheritable.h 定义了让设置对象从父对象继承值的接口其核心属性宏按“用户显式设置的值 → 继承值 → 系统默认值”的优先级回退Profile.h 的文件头注释还直接画出了 Profile 继承树的形态Profile 的 defaults 层在树根各 Profile 作为其子节点。此外 CascadiaSettingsSerialization.cpp#L1003 附近 “Merge profiles, color schemes, and globals into the user settings (aka inheritance)” 的代码段展示了内置/动态 Profile 如何以父节点身份挂到用户 Profile 上——方案 4 所描述的“多层继承”在实现层已有基础设施只是对用户暴露的 JSON 层面当时只采纳了方案 3 的单一defaults层。实战使用要点与适用前提结合文档结论与当前源码实际使用上有几点值得注意在终端设置 JSON 中写公共默认值把希望统一生效的 Profile 级属性如fontFace、fontSize、useAcrylic、acrylicOpacity、startingDirectory等写入profiles.defaults对象各具体 Profile 中显式写出的属性会覆盖默认值未写出的属性则继承默认值。profiles同时兼容两种形态从实现看写成数组旧格式仍被支持此时没有默认值可用写成{ defaults: {...}, list: [...] }对象新格式才能启用本特性。迁移时旧文件不会失效这正对应文档中 “gracefully upgrade” 的设计承诺。defaults中不要写guid它是配置块而非真实 Profile示例中所有示例均未为其指定guiddefaultProfile仍应指向list中某个真实 Profile 的 GUID。继承的局限当前对用户暴露的是单层“defaults → profile”继承方案 4 讨论过的多层 Profile 间继承inheritFrom在当时被明确推迟到 backlog#3818不要假设该能力随本文档落地。适用前提以上内容基于本仓库Windows 新一代终端及其原始控制台宿主共存的代码库中src/cascadia设置模型的当前实现与doc/cascadia下的官方 Schemadefaults/list结构是终端 JSON 设置文件的契约与 conhost原控制台宿主的注册表配置路径无关。相关文档与延伸阅读设计规格正文#2325 - Default Profile Settings.md设置模型总体设计#885 - Terminal Settings Model.md级联默认设置规格defaults.json分层的后续演进#754 - Cascading Default Settings.md外观配置对象Profile 级外观属性的结构化扩展#3062 - Appearance configuration object for profiles.md官方设置 Schemaprofiles.schema.json设置模型实现CascadiaSettingsSerialization.cpp、IInheritable.h、Profile.h默认配置文件defaults.json、userDefaults.json【免费下载链接】terminalThe new Windows Terminal and the original Windows console host, all in the same place!项目地址: https://gitcode.com/GitHub_Trending/term/terminal创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考