Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

設計配方定義文件的格式 #4

Open
lotem opened this issue May 9, 2018 · 14 comments
Open

設計配方定義文件的格式 #4

lotem opened this issue May 9, 2018 · 14 comments

Comments

@lotem
Copy link
Member

lotem commented May 9, 2018

  1. 「配方」用於定義一組配置及其他數據文件,指定其如何使用。

  2. 配方定義文件的格式有以下選擇:

  • bash 腳本,可定義(實現)一組變量、函數
  • 特定格式的 YAML 文件

無論哪種格式,都允許在代碼庫缺失配方定義文件的情況下,自動推導出默認的配方定義。
在 Windows 平臺 bash 不可用的情況下,只能忽略配方定義文件,做默認安裝。

  1. 在配方裏定義安裝動作,有以下選擇:
  • 安裝腳本(bash)
  • 指定要複製的文件 + 打補靪

無論哪種形式,都支持在未定義安裝動作的情況下,執行默認的安裝動作。
目前已實現的默認安裝動作爲:複製代碼庫中的 *.yaml, *.txt, opencc/*.{json,ocd,txt} 文件。

  1. 一份代碼庫可以提供多份配方。
    安裝時指定配方 user/repo:recipe 或安裝代碼庫默認的配方 user/repo

  2. 參數化配方,允許在安裝時定義一組變量以改變(具體化)安裝動作。
    例如爲某項用補靪實現的定製動作設定目標輸入方案。

若選用 bash 安裝腳本,則實現參數化比較容易;
若選用 YAML 格式的補靪,則需要模板機制。

@nameoverflow
Copy link
Member

听起来不错, 但是似乎破坏了现在的方案机制的简洁性,方案本身和包管理器耦合起来了。
感觉如果在 /plum/ 中对配方定义做集中管理可以减轻对方案制定的负担,就像 homebrew 做的一样。

@lotem
Copy link
Member Author

lotem commented May 9, 2018

我還考慮過一個加強版的無配方文件的簡易實現:
規範 *.custom.yaml 的格式,安裝時合併 *.custom.yaml 的內容,而非簡單地複製文件。
因爲有賴於打補靪實現的配方包之間,很容易發生同名補靪文件相互覆蓋的錯誤。

合併 *.custom.yaml 文件的具體方法是跳過文件開頭的註釋行,讀到 ^patch:$ 之後,把剩餘的縮進代碼追加到已有的補靪文件。如果考慮到 patch: 以外會有其他的一級節點,則需要一點點腳本功夫。
這些編輯操作用 bash, sed, awk 可以完成。Windows 的做法需要研究。

或者不用文本操作實現。升級一下 rime_patch 程序,妥善地合併 YAML 補靪文件。只不過這樣會依賴於 librime-tools

Node.js 也可以用來執行文本編輯操作;調 YAML 組件需要研究除了 exe 以外得安裝多大的 node_modules。

@lotem
Copy link
Member Author

lotem commented May 9, 2018

@nameoverflow 集中管理有他的好處,查詢下載都方便;也有潛在問題,就是不夠自由。一些用家可能只想整理歸納自用的方案和配置。
還有就是那個中心的維護成本、用戶更新代碼再提交 PR 更新配方的不便,幾乎超出了 Rime 用戶羣的承受力(homebrew 軟件包的維護者是與 Rime 開發者同級別的,更付得起辛苦成本)。

我認爲沒有中心,才能實現用戶僅通過維護一份配方列表實現終極配置。
去中心化輔以在「中心」以 user/repo 登記資源,就安穩了。

@LEOYoon-Tsaw
Copy link
Member

用bash吧,畢竟更靈活,要能自動改名,例如提供一個xxx.schema.custom.yaml,然後根據提供的參數來確定xxx

@lotem
Copy link
Member Author

lotem commented May 12, 2018

草稿:(大體實現 https://github.com/rime-aca/OpenCC_Emoji ,加入了演示一些其他功能的代碼)

# encoding: utf-8
---
recipe:
  Rx: emoji-suggestion
  args:
    - schema=luna_pinyin
    - opt_extra_argument
  description: >-
    Show emoji suggestion with OpenCC
  # excludes:
  # includes:
  install_files: >-
    *.schema.yaml
    *.dict.yaml
    *.txt
    opencc/*.*
  patch_files:
    - recipe.yaml: ${schema:-luna_pinyin}.custom.yaml
    - default.custom.yaml

patch:
  switches/+:
    - name: emoji_suggestion
      reset: 1
      states: [ "🈚️️\uFE0E", "🈶️️\uFE0F" ]
  engine/filters/+:
    - simplifier@emoji_suggestion
  emoji_suggestion:
    opencc_config: emoji.json
    option_name: emoji_suggestion
    tips: all
...

--- | # this is how to apply patch to multiple files
recipe:
  patch_files:
    - recipe.yaml: luna_pinyin.custom.yaml
    - recipe.yaml: luna_pinyin_fluency.custom.yaml
    - recipe.yaml: luna_quanpin.custom.yaml
...

① 一些解釋:

不想讓配方作者具有執行任意腳本的能力(儘管用 bash 實現解析仍會有漏洞可鑽)。
所以配方文件仍是基於 YAML 語法。
但是爲了 bash 腳本實現方便,會做若干格式限定。

基於 bash 的實現,不會解析 YAML,肯定是當文本文件做字符串解析的。因此規定

  1. 配方文件不支持配置編譯器語法(__include, __patch)等
  2. recipe 下面的 key 嚴格按順序寫
  3. recipe/args 其實是變量賦值(默認值),會用 bash 求值
  4. recipe/install_files 包含所有要複製的文件名或 glob 通配文件名,可以寫成一個字符串(可理解爲 ls 的參數)也可以是 block style YAML 列表,省略該字段則執行默認安裝
    recipe/install 或者叫這個名字?
  5. recipe/patch_files 合併補靪文件,格式爲 block style 列表或者有序映射表,每項將一個 YAML 源碼文件的 patch 節點合併到目標文件(如爲列表格式,則爲用戶文件夾的同名文件),目標文件可以使用變量替換語法,以支持參數化補靪
    recipe/customize 曾考慮用這個名字,但會破壞 key 的字母表順序
  6. patch 節點,就是待合併的補靪,也可以寫在單獨的文件裏,如果配方只打個補靪,和 recipe 寫到一個文件裏比較方便
  7. 合併到已有的補靪文件,只複製 patch 之下的縮進行,注意 patch 同級的其他節點如本例的 recipe 不會合併過去

② 再談 recipe 的用法

安裝口令語法爲

bash rime-install emoji-suggestion#schema=luna_pinyin

其中 # 之後的逗號分隔的賦值語句定義配方參數(bash 的賦值語句用分號分隔,但分號用在一個命令行參數中間需要加引號,容易出錯)

這裏省略了配方名 recipe,如果寫全,會是

bash rime-install emoji-suggestion:recipe#schema=luna_pinyin

③ 如果一個包提供多個配方文件,文件名反映配方的用途,會有(配方文件、口令):

# rime/rime-luna-pinyin/enable.yaml
Rx luna-pinyin:enable
# rime/rime-util/enable_schema.yaml
Rx util:enable_schema#schema=luna_pinyin
# rime/rime-util/select_schema.yaml
Rx util:select_schema#schema=luna_pinyin

④ 上文的配方文件樣例是爲了明確地定義參數及補靪的打法。如果不寫配方文件,還可以怎樣實現呢?

對於不需要參數化的補靪,可以直接根據文件名 *.custom.yaml 識別補靪。
參數化補靪,我想可以約定前綴 arg_ 加上表示目標方案(或配置文件)的變量,比如本例可將補靪文件命名爲 arg_schema.custom.yaml 前提是安裝命令中必須指定 schema 參數。也就是說沒法提供默認值。

⑤ 還有一種實現比較快,但設計比較粗糙的實現:

# encoding: utf-8
# Show emoji suggestion with OpenCC
# Rx: emoji-suggestion
#$ target=${schema:-luna_pinyin}.custom.yaml
patch:
   # ...

這就是待合併的補靪文件,除了以上討論的格式要求之外,特殊註釋行 #$ 將作爲 bash 命令執行,然後通過 target 變量求得目標補靪文件。schema 是可能由安裝命令定義的變量。
實現略簡單,但比較容易出安全漏洞。如果不用 bash 實現,解析反而不如直接寫進 YAML 方便。

@lotem
Copy link
Member Author

lotem commented May 12, 2018

就 opencc emoji 這個例子來看,很可能需要一個專門打補靪用的配方,因爲需要給每個輸入方案都打上補靪,於是要分多次調用,每次給不同的 schema 參數。而複製 opencc/*.* 文件只需要一次。

所用配方大概是這樣的(Rx 代表給 rime-install 命令的配方參數):

Rx emoji-suggestion  # 只安裝文件(emoji-suggestion:opencc ?)
Rx emoji-suggestion:patch#schema=luna_pinyin
Rx emoji-suggestion:patch#schema=cangjie5
...

@LEOYoon-Tsaw
Copy link
Member

LEOYoon-Tsaw commented May 13, 2018

有幾個問題:

  1. 如果複製文件時,發現重名文件怎麼辦,最優的做法可能是報警告,繼續執行,並不覆寫?同時bash指令允許用戶自行用argument指定覆寫行爲
  2. recipe/install_files下是如何指定安裝路徑的?逐個匹配文件名,符合要求則放入相當位置/還是在repo裏面就要按照目標路徑放置文件?
  3. patch下的內容如果與recipe/patch_files衝突,優先級?可以考慮報警告,中止執行,同時允許用戶自行用argument指定覆寫行爲
  4. 能否根據argument內容選擇性安裝文件/patch?

@lotem
Copy link
Member Author

lotem commented May 13, 2018

  1. 這個現在還做不到。因爲每個包都是直接安裝到用戶文件夾,情況可能是覆蓋了來自另一個包的文件,也可能是升級安裝時覆蓋上一個版本。將來如果所有配置都用配方生成,每次在一個空文件夾開始逐個安裝配方,那就可以確定發生了第一種情況從而報警。

  2. 是的,現在已經配好的配方都與用戶文件夾格局一致。opencc/ 也長一樣的。

  3. 你說的衝突是指出現重複的 key 吧?如果只用 bash 和 linux 小工具做字符串處理,按 key 覆寫也很吃力。比如用不同的引號括住相同的 key,也會發生 YAML 語法錯誤。另外邏輯錯誤也會因補靪代碼相互影響而發生。這是沒法防範的。所以總的原則還是用戶自己嘗試搭配。剛開始的實現,就按我先時描述的,可能只合併補靪代碼行。不檢查衝突。
    不過你說的也對,確實有有意覆蓋補靪內容的場景,比如對某個已應用的補靪的微調。
    librime/tools/rime_patch 這個原來是爲 rimekit 製作的小工具,用來根據命令行參數生成補靪。一個完善的實現肯定還要用這種帶 YAML 庫的可執行程序。配方對補靪文件的修改可以看成對 *.custom.yaml 文件內容應用一個來自配方的補靪。

@lotem
Copy link
Member Author

lotem commented May 13, 2018

先找人用 bash 腳本實現一個簡單的用用看。
想要完善一些的話,我感覺 bash 很快就會不夠用了。
再考慮到多數 Windows 用戶不具有 bash,用嚴肅代碼實現配置管理器將有必要。

@lotem
Copy link
Member Author

lotem commented May 28, 2018

稍稍細化 patch_files 的語法

patch_files:
  文件名甲:
    - 補靪一
    - 補靪二
  文件名乙:
    - 補靪三

補靪行及其下縮進行原樣(也考慮做字符串模板展開)追加到目標文件根節點的 __patch 節點下(若無 __patch 節點則添加)。腳本實現會非常簡單,且靈活度高。
並約定:一律寫作以 - 開頭的補靪列表,以避免出現應用多份補靪後出現 YAML key 重複的錯誤。

示例:

# emoji_suggestion.recipe.yaml
# encoding: utf-8

---
recipe:
  Rx: emoji_suggestion
  args:
    - schema=luna_pinyin
  description: >-
    Customize input schema to show emoji suggestion with OpenCC

patch_files:
  ${schema:-luna_pinyin}.custom.yaml:
    - patch/+:
        __include: emoji_suggestion.recipe:/patch

patch:
  switches/@next:
    name: emoji_suggestion
    reset: 1
    states: [ "🈚️️\uFE0E", "🈶️️\uFE0F" ]
  'engine/filters/@before 0':
    simplifier@emoji_suggestion
  emoji_suggestion:
    opencc_config: emoji.json
    option_name: emoji_suggestion
    tips: all

配置管理器修改後的文件:

# encoding: utf-8
__patch:
  # Rx opencc-emoji:emoji_suggestion {
    - patch/+:
        __include: emoji_suggestion.recipe:/patch
  # }

更多示例:

patch_files:
  # This is possible but as always we recommend
  # putting user customization in luna_pinyin.custom.yaml, default.custom.yaml
  ${schema:-luna_pinyin}.schema.yaml:
    - ${schema:-luna_pinyin}.custom:/patch
    - emoji_suggestion.recipe:/patch
  default.yaml:
    - schema_list/=: []
    - schema_list/+:
      - schema: luna_pinyin
    - schema_list/+:
      - schema: combo_pinyin_kbcon
    - menu/page_size: 9
  default.custom.yaml:
    - patch/+:
        menu/page_size: 10

@marguerite
Copy link

@lotem

我用 golang 实现了一个 rime-plum-go,本来是准备给 openSUSE 发行版升级 brise 做一个类似脚本一样的东西,可以在本地去把 repo 都 clone 好,结果不知不觉就做成近似完全版本的 plum 了。

目前没有 --selector 的功能,另外由于对 recipe 新概念的不熟悉可能有 bug...但是看到您的帖子,我感觉它可能可以解决 windows 用不了 bash 的问题。

目前参数是这样的:

-b 类似 Makefile 里面的功能
-d 指定 RIME_DIR
-r <prefix:-https://github.com/><user:-rime><repo:-rime-emoji>:<recipe:-customize>:<rimeOptions>

比起原来的 pattern,多了一个 prefix,可以支持除了 github 以外的网站比如 marguerite.su/marguerite/rime-custom:customize:key=value,key1=value1

但是由于加了参数的原因, selector 就不太好做了...在想别的办法比如自己实现参数的库。

另外准备看一下您在这里发的草案,检查一下对语法的处理有没有 bug...

@lotem
Copy link
Member Author

lotem commented Jul 13, 2019

@marguerite 久仰

目前這個格式的設計,以及打補丁的方法都在遷就文本行編輯工具,因爲bash腳本沒有解析YAML文件的能力,只好按文本文件來編輯。如果有再複雜一些的操作,已經做不下去了。 rime-plum-go 正是今後需要的工具。

具體來說,配方對 patch 節點打補丁,就是對缺乏YAML解析能力妥協的產物。所輸出的代碼非常難以維護。

理想中,補丁要直接修改 *.custom.yaml。用嚴肅編程語言就可以實現了。
將來改成這樣做,要有兩個前提:兼容現有的配方補丁;保護好用戶自己寫的patch,補丁做的修改要能撤回。這些要怎樣實現還得大討論。

@marguerite
Copy link

@lotem 不知道我说的对不对,翻看 recipe.sh 和自己跑那些 shell script 进行测试后,我感觉现在的 recipe 是这样的,第一行是要操作的文件 $(schema:-luna_pinyin}.schema.yaml ,针对这个文件做一个 .custom.yaml,然后把第二行开始的东西都原封不动的丢到 # Rx: xxx {} 这个 block 里去。plum 目前就做了这些事情。我的实现也就抄了这些...

看了下 config 的语法,不知道我的理解对不对。

- patch/+: 由于只有 .custom.yaml 中的 “patch:” 部分才能应用到 .schema.yaml(?) 所以这行实际上的意思是“向 .custom.yaml 的 patch: 追加”? - 表示列表,这里哪来的列表?
__include: emoji_suggestion:/patch 内容是 emoji_suggestion.yaml 的 patch: 部分。

现在已经采用 yaml 和 bash 结合的方式了,考虑到兼容性,再改纯 yaml 已经不太容易了。好比 install_files 下面可以直接写不带 - 的 opencc/**/*。所以这里不差再多一个 reserved word 表示覆盖行为了,比如:

opencc/**/* !override
*.json !keep

反正这里肯定是要从 yaml 中提取出来单独 parse 的...至少我在 rime-plum-go 这边是这样做的。因为 golang 的在文件夹下 Glob 的函数是我自己写的(官方没有),只支持 re2 的 regexp。所以我这里需要转换比如 */ *.{txt,json} 这样的 shell script 为 regex。那就不差再多识别一个参数了...

另外感觉我这边可以尝试解析 yaml,直接写 custom.yaml 的。比如第一个 recipe 写 custom.yaml,我会读取 emoji_suggestion.yaml 直接把 patch: 内容写到 custom.yaml,反正 rime 是支持的。第二个 recipe 再写 custom.yaml,我也是读取 patch: 然后按 key 查找冲突。如果冲突则提示但不写入,只写入非冲突部分。至于怎么调整哪个 recipe 在先,目前没有这样的机制吧,毕竟用户安装顺序是乱序的。现在 recipe 可以嵌套吗?

@lotem
Copy link
Member Author

lotem commented Jul 14, 2019

install_files 本來應該用YAML列表,但是純粹爲了bash實現省力就做了一個長字符串

至於那個patch,怕是要多費些口舌才說得清……

我先說最後一個問題。安裝配方是講究順序的。
可能發生覆蓋,有些配方可能會有意覆蓋已有的配置項。
比方說,有個配方叫做清空輸入方案列表,那正常情況要用在所有添加輸入方案的配方之前。
所以使用配方的順序重要,並且最終效果是指定順序的用戶來保證。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants