Skip to content

Setup poly

Actions

About

Install the poly CLI (formatter and linter) and put it on PATH
v0.20.0
Latest
Star (0)

Tags

 (1)

Poly

用「兩個 VSCode extension + 一個 CLI」取代為了各語言 highlight/lint/format 而 安裝的一大堆零散 extension。編輯器與 CI 共用同一個 binary、同一份設定,所以本機存 檔跟 pipeline 的結果一定一致。

產出物 職責
poly-syntax-highlight 多語言 syntax highlighting,接管全部 VSCode 內建語言文法
poly-lsp 存檔即時 lint/format、批次命令、編輯器端便利功能
poly 單一 binary CLI,供 CI、pre-commit、終端批次使用

兩個 extension 同版號一起發版,但彼此不相依,各自可以單獨裝。

功能

poly-syntax-highlight — highlighting

  • 156 個文法:接管 49 個 VSCode 內建語言,另加 49 個內建沒有的語言 (HCL/Terraform、nginx、zig、dotenv、protobuf、mermaid、caddyfile、systemd unit、jsonnet、just、nix、cabal、dune、ssh_config、Solidity/Cairo/Vyper、 DBML、PlantUML、CSV/TSV rainbow…)。DBML 另附 snippets 與檔案圖示,PlantUML 另附 snippets。
  • 來源共 97 條、31 個 pinned 上游 repo;只有 CSV/TSV/ssh_config 三個是自產的 (上游要嘛不存在,要嘛沒有授權檔)。
  • 輸出標準 TextMate scope,任何現有 color theme 直接生效,不自帶配色。
  • 部分語言改採比內建更好的社群文法(如 rust 用 dustypomerleau/rust-syntax)。
  • 文法一律以 pinned commit 從上游 repo/marketplace VSIX 同步,不手改。
  • markdown 清單按 Enter 自動接續下一項(-/*/+、1./1)、- [ ]、>), VSCode 內建沒有這個行為。同一份規則也套用在 SKILL.md、*.prompt.md、 *.instructions.md、.claude/agents/**、.claude/rules/** 這些 VSCode 1.120 起 不再算 markdown 的檔案上。有序清單接出來的是 1.(poly fmt 保留這種寫法)。
  • 零執行期程式碼,不佔 extension host 資源。

poly-lsp — 編輯器整合

  • Format:Format Document/editor.formatOnSave,加上批次命令 Format File/Folder/Workspace/Git Repo/Git Changed Files。專案有 .editorconfig 的話直接沿用,縮排與行寬不必再抄一份到 poly.toml。
  • Format Selection:只格式化選取的範圍,其餘的行原封不動。
  • 未信任的資料夾(Restricted Mode):照樣格式化與 lint,但只用內嵌的引擎,不執行其他程式 (專案內或 PATH 上的工具、下載、git、Code Runner、PlantUML、PDF/圖片匯出),也不讀資料夾 .vscode/settings.json 裡的 poly 設定;信任之後全部回來,不必重開視窗。
  • Lint:存檔即時 diagnostics 進 Problems panel;Poly: Lint (poly check) 在終端跑完整 CLI。
  • Poly: Minify(cmd+alt+m/ctrl+alt+m):把當前 buffer 壓成一行,涵蓋 JSON/JSONC、CSS、HTML、XML、JavaScript/TypeScript。只移除空白與註解,不改名、 不折常數、不刪分支。刻意不進 format-on-save——它是格式化的反向操作,poly fmt 下一次就會把它還原。改動以編輯器 edit 送出而非寫檔,所以 undo 是一個按鍵,未存檔的 buffer 也能用。
  • 規則說明:滑鼠移到 SQL 的波浪線上會顯示 sqruff 該條規則的 anti-pattern/ best-practice 全文。sqruff 沒有文件站可連,那份說明編在 binary 裡,版本精確、 離線可讀。其他工具有自己的規則頁,走規則代碼上的超連結。
  • 記憶體明細:Poly 輸出面板開到 Debug 時,每開一個檔、關一個檔,寫一行 daemon 現在握著什麼: 常駐記憶體、幾份文件與多少位元組、lint 與整包快取、各來源留著幾筆診斷。RSS 只有一個數字,而 poly 有六個地方放東西——這一行的用處是讓漲上去的 數字歸到某一個快取頭上。tools/lsp-smoke.py 的 soak 也讀它:120 輪開關之後,poly 握著的每一項都必須跟第 1 輪一樣多(實測把 lint_hashes 的清除拿掉,RSS 只漂 +0.2 MB 照樣綠,而這條直接指名 hashes 12 → 488)。
  • 專案內工具優先:偵測到專案的 biome/prettier/eslint/rustfmt 就用它們, 避免和團隊 CI 結果不一致。
  • 背景檢查 GitHub Releases(預設 7 天一次,可調可關),一鍵更新裝了的那幾個 extension——沒裝的不會被順手裝上,那不是更新。poly-syntax-highlight 也有自己的檢查與 間隔設定(poly.syntax.updateCheck.*),只裝它一個也收得到新版。

poly-lsp — 編輯器便利功能

沒有 CI 對應物的那些,不需要 daemon:binary 沒起來它們照樣能用。附加功能預設全關。

  • Poly: Copy Path with Line Numbers:複製 路徑:行號;選取多行時是 路徑:42-51。VSCode 內建的 Copy Relative Path 只到路徑為止,:42 是唯一的差別, 但那一截才是重點——src/lib.rs:42 正是 rg 印的、CI annotation 連過去的、終端機 能點的,也是 poly 自己診斷輸出的形狀。預設沒綁快捷鍵,命令面板與編輯器右鍵都有。
  • Poly: Insert Table of Contents:在游標處插入 markdown 目錄,用註釋標記框住, 再跑一次就地更新。錨點照 VSCode 自己的 slug 規則產生,所以連結一定跳得到;front matter 與 code block 裡的 # 不會被誤認成標題。
  • Poly: Toggle Bold / Toggle Italic:cmd/ctrl+b、cmd/ctrl+i,只在 markdown 檔生效。產生 **bold** 與 _italic_——就是 poly fmt 正規化出來的那兩種, 不會被下一次存檔改掉。
  • Poly: DBML to SQL / SQL to DBML:DBML 轉成 PostgreSQL/MySQL/SQL Server/ Oracle 的 SQL,或從 PostgreSQL/MySQL/SQL Server/Snowflake/Oracle 的 SQL 反推 DBML。 轉的是編輯器裡的內容,未存檔的修改也算;寫不進去的會指出第幾行第幾欄,存到哪裡用對話框選。 命令面板只在 .dbml 與 SQL 檔出現。
  • 簡繁轉換:Poly: 簡體轉繁體、繁體轉簡體,以及各一個「含台灣用詞」的版本,取代 cipchk.zh-hans-tt-hant-vscode。有選取就轉選取(多重選取每一段都轉),沒有就轉整份。用 OpenCC 的詞組字典:含台灣用詞的那一對會換用語(软件 ↔ 軟體、鼠标 ↔ 滑鼠),另一對只換字;繁體一律是 台灣字形(裡、著)。一次 undo 復原。
  • AutoCorrect(poly.autocorrect.enabled 打開):取代 huacnlee.autocorrect,用的是它自己的 引擎。中日韓文字與英數之間補空白、全半形標點、.autocorrectrc 裡的詞彙拼寫:開檔與打字時就 標出來,每條一個 quick fix,手動存檔時整份修正(自動存檔與「儲存但不格式化」不會), Poly: AutoCorrect: Format Document 手動跑。讀 workspace 資料夾的 .autocorrectrc 與 .autocorrectignore(加上 .gitignore),改了立即生效——拿掉一條規則也是。狀態列的 Lint/Format 開關連它一起關;那個 extension 還裝著時 poly 讓開。
  • Error Lens(poly.errorLens.enabled 打開):取代 usernamehw.errorlens。每個問題的訊息寫在那一行的 行尾,整行依嚴重度上色;gutter 圖示、狀態列的計數與訊息、CodeLens、hover 裡的按鈕可另外打開。命令面板的 Error Lens: …(切換各嚴重度、選取/複製/排除問題、上網搜尋、加上停用此行規則的註解等)關著時也能用。 設定在 poly.errorLens.*,名稱同它的。那個 extension 還裝著時 poly 讓開。
  • PlantUML:取代 jebbs.plantuml。alt+d 在旁邊預覽游標所在的那張圖(縮放、拖曳、分頁、 複製成圖片),匯出單張/整份檔案/整個 workspace(12 種格式,檔名與 out/ 底下的路徑同 jebbs.plantuml,舊的匯出會被原地覆寫),產生 server URL,從匯出的 PNG 取回原始碼;另有 補全、!define 巨集的參數提示、大綱,以及同名圖(匯出會互相覆蓋)與未命名圖的檢查。 jar 由 poly 下載,settings 的 poly.tools 寫 "plantuml": "<jar 路徑>" 可改指向別的 jar;Java 自備,不在 PATH 上就設 poly.plantuml.java。MIT 版的 jar 畫不了 ditaa,需要就指向 GPL 版的 jar。 jebbs.plantuml 還裝著時 poly 讓開。
  • Excalidraw:取代 pomdtr.excalidraw-editor。.excalidraw、.excalidraw.json、 .excalidraw.svg、.excalidraw.png 用 Excalidraw 開,存出來的檔案與它的相同:SVG/PNG 存的是 圖片本身並嵌著 scene,放進文件直接能看、再開還能編輯。標題列可切換原始檔、圖片與編輯器, 元件庫可存在工作區裡跟著專案走,主題與語言可設。設定在 poly.excalidraw.*,名稱同它的。 介面與手寫字型打包在 extension 裡,中文字型要用時才從 esm.sh 下載。兩個都裝著時 VSCode 會請你 選預設的編輯器。
  • Markdown 匯出:取代 yzane.markdown-pdf。Export Markdown (pdf)/(html)/(png)/(jpeg), 右鍵選單也有,也可以存檔時自動轉換;匯出的檔案與它的相同:語法上色、KaTeX 數學式、PlantUML 與 mermaid 圖、引入別的 markdown 檔,PDF 的紙張、邊界與頁首頁尾可設。設定在 poly.markdownPdf.*, 名稱同它的。PDF 與圖片由已安裝的 Chrome 或 Edge 印出;都沒有時第一次匯出才下載固定版本的 Chrome for Testing(macOS arm64 與 Windows x64),核對雜湊值後才使用。emoji 畫成字元,不是它的 Apple 圖片。兩個都裝著時 poly 的命令不放進右鍵選單。
  • CodeSnap:取代 adpyke.codesnap。選取程式碼後執行 CodeSnap 📸(右鍵選單也有),旁邊開出的 頁面照 VSCode 的顏色與字型把它畫成 macOS 風格的視窗,按快門存成 PNG 或複製到剪貼簿;拍出的圖與 它的相同。背景、陰影、視窗樣式與行號可設,設定在 poly.codeSnap.*,名稱同它的。頁面開著時,選取 會蓋掉剪貼簿,和它一樣。兩個都裝著時 poly 的命令不放進右鍵選單。
  • 貼上圖片:取代 mushan.vscode-paste-image。截圖或複製圖片後,在檔案裡按 cmd/ctrl+alt+shift+i(或 Paste Image),圖片存成 PNG 放在檔案旁邊,游標處插入連結:markdown 是 ![](路徑),AsciiDoc 是 image::路徑[]。有選取文字就拿它當檔名,否則以貼上的時間命名。存放的資料夾、連結的寫法與檔名都可設, 設定在 poly.pasteImage.*,名稱同它的。它的 cmd/ctrl+alt+v 在 poly 是 Extract Variable,所以換了鍵; 兩個都裝著時各用各的鍵。
  • 資料預覽:取代 RandomFractalsInc.vscode-data-preview。JSON、JSON Lines、JSON5、HJSON、YAML、CSV/TSV、 Markdown 表格、properties/ini/env、Excel 與 ODS、Arrow、Avro、Parquet 檔按編輯器標題的按鈕(或 Preview Data,檔案總管與分頁右鍵也有),開出可排序、篩選、分組的表格,也能換成圖表;網址上的檔也能 預覽。篩選後的資料可另存成別的格式,表格的設定可存成 .config 再載入。頁面與表格元件(Perspective 0.4)同它的,但不從網路載入任何東西。設定在 poly.dataPreview.*,名稱同它的。不綁快捷鍵;兩個都裝著時 poly 的按鈕與選單讓出來。
  • 工具箱:transform 的工具,類似 DevToys。活動列上列出格式轉換、 圖片轉換、Base 編碼/解碼、雜湊、URL、JWT、時間戳、進位、UUID、QR Code 等二十多個工具,各自開在編輯器分頁, 配色跟著主題,不連網路。選取文字的右鍵 Open Selection In Toolbox 帶著文字開工具,結果可以寫回原處; Transform Selection 就地編碼、解碼、雜湊或換寫法; Convert Document To Another Format 把 JSON、YAML、TOML、XML 等轉成另一種格式;檔案總管的 Convert File To Another Format 讓 CSV、Parquet、Avro、Arrow 與圖片互轉。設定是 poly.toolbox.enabled。
  • Swagger 預覽:取代 arjun.swagger-viewer。在 Swagger 2.0 或 OpenAPI 3 的 JSON/YAML 檔按 shift+alt+p(或 Preview Swagger,檔案總管右鍵也有),旁邊開出 Swagger UI,打字時跟著更新;也能 從網址預覽,檔案總管有工作區裡 spec 的清單。頁面與 Swagger UI 的版本同它的。JSON spec 照 schema 標出錯誤,YAML 的要另裝 redhat.vscode-yaml。spec 引用別的檔(外部 $ref)時,被引用的檔會併進預覽、 改了也跟著更新;它宣稱支援這點,實際上沒有作用。設定在 poly.swaggerViewer.*,名稱同它的。兩個都 裝著時 poly 讓出快捷鍵、右鍵選單與清單。
  • Marp 投影片:取代 marp-team.marp-vscode。front matter 寫了 marp: true 的 Markdown,預覽就是 Marp 投影片(主題、分頁、數學式、背景圖);directive 有上色、說明、補全與檢查,多半附快速修正。 Export Marp Slide Deck... 匯出 HTML、PDF、PPTX、PNG、JPEG 或講者備忘稿,Copilot Chat 裡是 #polyExportMarp。編輯器標題列的 Marp 按鈕開出命令清單,File > New File 有 Marp Markdown。設定在 poly.marp.*,名稱同它的(已棄用的 enableHtml、chromePath 除外)。PDF、PPTX 與圖片用已安裝的 Chrome、Edge 或 Firefox 匯出,和它一樣不另外下載。兩個都裝著時 poly 整個讓出。
  • Git History:取代 mhutchie.git-graph。按 status bar 的 Git History、原始檔控制標題列的按鈕(或 View Git History),所有分支、tag 與 stash 畫成一張圖,未提交的變更在最上面。點 commit 在下面展開它的 訊息與變更的檔案,cmd/ctrl 加點另一個是比較兩者;開著的 diff 用標題列的 ← →(cmd+alt+a/cmd+alt+z) 換到同一個 commit 的上/下一個檔案;可搜尋、只看某些分支。commit、分支、tag、stash 與 檔案的右鍵選單做簽出、merge、rebase、cherry-pick、reset、push、刪除等操作,remote 在工具列管理。 檔案總管對檔案或資料夾按右鍵的 View File History/View Folder History(命令面板也有,作用在目前的檔案 與它所在的資料夾)只列改過它的 commit,展開的 commit 只列它底下的檔案;工具列的 ✕ 回到整個 repository。 Changes 分頁把同一個路徑的歷史攤成連續的 patch(同 git log -p -- <路徑>),可以挑檔案或資料夾, 或讓它跟著編輯器裡的檔案走。功能 照它的行為做;圖的畫法移植自 VSCode 的原始檔控制圖(Source Control Graph,MIT),顏色是它的 scmGraph.* 佈景主題色。沒有它的 git-graph.* 設定。兩個都裝著時 poly 讓出 status bar 與原始檔控制的按鈕。 電腦上沒有 git 時改由 poly 讀 repository,圖、commit 細節、比較與 diff 照常,但只能看:會改動 repository 的操作都需要 git。
  • Code Runner(poly.codeRunner.enabled 打開):取代 formulahendry.code-runner。ctrl+alt+n(或 Run Code,編輯器標題的 ▶ 按鈕與右鍵選單也有)執行目前的檔案或選取的程式碼,結果在輸出面板的 Code Runner, runInTerminal 改在終端機;ctrl+alt+k 執行自訂命令,ctrl+alt+j 先挑語言再執行,ctrl+alt+m 停止。 各語言用什麼命令執行照它的表,設定在 poly.codeRunner.*,名稱同它的;Go、Rust、Python 的預設改成 go run .、cargo run 與選定的 Python 直譯器(沒有時 python3)。formulahendry.code-runner 還裝著時 poly 讓開。
  • 清單接續:在清單項目上按 Enter 接出下一項,有序清單號碼遞增(整份寫成 1. 的 清單維持 1.),任務項接出 - [ ],空的項目按 Enter 結束清單(往外退一層,最外層 就清掉 marker)。markdown 家族與 yaml 都有,yaml 只認 sequence 的破折號——> 在那裡是 folded block scalar。
  • 清單縮排:游標在清單項目的內容起點或更左邊時,tab 進一層、shift+tab 退一層。 一層是上一項內容開始的那一欄——- x 的內容在第 2 欄、1. x 在第 3 欄,也就是 poly fmt 正規化出來的縮排,不是 editor.tabSize。游標已經在文字裡、有選取、補全清單 開著、Copilot 的 inline suggestion 等著被接受時,Tab 原樣還給編輯器。
  • Postfix completion:err.if 展開成 if err != nil { }(Go)、if (err) { } (TS)、if err:(Python)。go/rust/swift/ts/js/python/lua/c/cpp 都有。這是 文字重排不是分析——poly 只讀 . 左邊那串字元塞進模板,不知道 err 是什麼型別,也正 因為如此同一份表才蓋得住每個語言。排在 language server 的答案後面。
  • Poly: Extract Variable / Inline Variable:cmd/ctrl+alt+v、 cmd/ctrl+alt+shift+v,每個語言都通用。內建的 editor.action.refactor 開的是一張選單, 而你要的那一項每個 server 講法都不同(Extract variable/Extract into variable/ Extract subexpression to variable/Extract to constant in enclosing scope),快捷鍵 綁不到任何一個。poly 問的是 LSP 標準的 refactor.extract/refactor.inline kind, 過濾掉 Extract function 那種不是變數的,剛好一項就直接套用。做事的是語言自己的 server。 你裝的別的擴充綁了同一組鍵(mushan.vscode-paste-image 的貼圖、quicktype 的 Paste JSON as Types)時 poly 讓出來,命令面板照樣叫得到。
  • Poly: Move to New File / Change Signature / Implement Interface:同一個形狀再 三個,從命令面板叫。Move to New File 問 refactor.extract 挑 toNewFile;Change Signature 在游標原位問 refactor.rewrite,游標要在參數上;Implement Interface 問 quickfix 挑 「補上缺的方法」,要先有一個編不過的斷言(Go 是 var _ Shape = Triangle{}),因為 server 是對著診斷提供那條修正的。
  • 引用與實作 CodeLens:每個宣告一行 11 refs;interface 多一顆 3 impls,具體型別有滿足 interface 才多一顆 1 interface,方法寫在型別外面的語言(Go)再多一顆 4 methods; Markdown 有人連結的標題也有,數的是整個 workspace 連到它的 #錨點 連結。只有一筆就直接跳過去,多筆開檔案總管裡的 References 面板——那是 poly 自己的樹,每一列除了 原始碼還帶行號與 它落在哪個符號裡(method Handle、func main),內建的 references-view 兩欄都沒有, 而別人的樹加不了欄位。數字來自該語言已註冊的 provider,poly 只數與畫。GraphQL、nginx 與 Makefile 沒有 server 答得出引用,由 poly 自己按名字算:GraphQL 的 type、fragment、directive;nginx 的 upstream、location @name、set/map 等宣告的變數、limit_req_zone 之類的 zone、 log_format;Makefile 的 target(.PHONY 與 prerequisites 算使用,%.o 這類 pattern 不算) 與變數($(X)、${X}、$(call x,…) 算使用,+= 也算使用而不是宣告)。範圍是整個 workspace folder,沒有 scope;同一份答案也給 outline 與 Find All References。數字存在 $XDG_CACHE_HOME/poly/refs/(預設 ~/.cache/poly/refs/),重開視窗時先畫上次的 數字、背景重新問過再更新。poly.referencesCodeLens.enabled 可關。
  • Makefile 與 shell 跳到定義:F12(Go to Definition)在 Makefile 跳到 target、變數、define, 在 shell 跳到 function 與變數(local、export、for、read、getopts 給值的都算宣告)。先找 同一個檔,有好幾個宣告時取游標上方最近的一個——所以是游標所在 function 的 local;上方沒有才 取下方的。檔案裡沒宣告才找整個 workspace folder,source 進來的 lib 與 include 的 .mk 就是 這樣找到的(只認 .sh/.bash/.zsh/.ksh/.bats 與 VSCode 認得的 Makefile 檔名)。只比對 名字,不跟 source/include 的路徑。另外裝了 bash-language-server 的話,編輯器會把兩邊的答案 合在一起。
  • run | debug CodeLens:程式進入點(Go 只算 package main 的 main;Rust/C/C++/Java 的 main、 C# 的 Main、Python 的 if __name__ == "__main__"、shell 的 shebang)上方一行。run 存檔後像 Code Runner 的 Run Code 那樣執行整個檔案,命令來自 poly.codeRunner.*(go run ./cargo run/選定的 Python 直譯器/shebang 指定的直譯器),結果在輸出面板的 Code Runner,不經過 debugger;那些設定 有這個檔的命令就有 run,Code Runner 關著也一樣。poly 沒有 debugger,debug 是交給你已經裝的 debug extension。 Go 的 _test.go 裡,go test 真的會跑的函式上方是 run test | debug test(benchmark 是 run benchmark | debug benchmark),標題、命令與 golang.go 的一樣,執行與除錯也是交給 golang.go, 所以要裝了 golang.go 才有。golang.go 只看名字,poly 還看簽名:Test/Benchmark/Fuzz 開頭(後面 不能接小寫字母)的頂層函式,剛好一個 *testing.T/*testing.B/*testing.F 參數、沒有回傳值 (testing 用別名或 . import 也認得);Example 開頭的不收參數、沒有回傳值。TestFoo(x int)、 TestFoo(t testing.T)、Testfoo、method 與 TestMain(m *testing.M) 都沒有按鈕。 poly.runCodeLens.enabled 可關。
  • protobuf → 生成的 Go:.proto 的 message/enum 上方 go type,service 上方 go server/go client,跳到 protoc 生出來的宣告;rpc 上方 N impls,跳到寫在 Go 裡的 handler。認 protoc-gen-go 與 protoc-gen-go-grpc 的命名規則;生成檔不在 workspace 裡就不畫。 poly.protobufCodeLens.enabled 可關。
  • 跨檔案 next/previous change + Poly: Revert Selected Changes and Save: cmd/ctrl+alt+z/cmd/ctrl+alt+a 跳到上/下一個有改動的檔案並落在改動上,alt+q 還原游標所在的 hunk 並存檔。VSCode 內建的是「同一個檔案裡的下一處改動」,跨檔案那 一步沒有——而那是 review 一個 branch 時按最多次的一步。順序照路徑排,所以同一顆 按鍵按兩次一定走同一條路。別的擴充綁了同一組鍵(Rewrap 的 alt+q)時 poly 讓出來。
  • 縮排上色:每層縮排的空白塗底色,四色循環;填不滿一層的空白另外標色,那正是 「縮排改到一半」的樣子。內建的 indent guides 畫線回答「block 從哪開始」,上色回答的 是「我在第幾層」。只畫可見範圍,顏色走 theme color。
  • Unicode 高亮:gremlins 的替代。不可見字元、雙向控制字元、怪空白、冒充 ASCII 的字元 (en dash、彎引號),所有檔案、邊打邊標:gutter 記號(error 😡、warning 🤔、info ℹ️)、 捲軸刻度、行尾寫出字元名稱,等級與顏色照 gremlins。poly.unicodeHighlight.enabled 打開。
  • Gutter 圖片預覽:某行提到的圖檔存在就在 gutter 放縮圖。不寫語法解析器—— markdown/HTML/CSS 各有寫法,而檔案存不存在才是真正的過濾器。
  • markdown preview 的 mermaid 圖表:```mermaid fence 在 preview 裡畫成圖,配色與字型 跟著編輯器主題。VSCode 1.135 起內建就有這個功能,那時候 poly 會自動讓開——所以這一項 實際生效的是 1.85 到 1.134。poly.markdownMermaid.enabled 可關。
  • markdown preview 的其他圖表:fence 語言為 nomnoml、flowchart(或 flow)、 sequence、vega、vega-lite、markmap、excalidraw(scene 的 JSON),範圍與畫法同 MarkNote。函式庫在文件第一次用到時才載入; 深色主題下,本身不吃配色的圖畫在淺色底卡上。plantuml(或 puml、uml)也在內:有設 poly.plantuml.server 就交給 server,否則在本機用 Java 畫。poly.markdownDiagrams.enabled 打開。
  • GitHub 樣式的 preview:取代 Markdown Preview Github Styling,九種 GitHub 配色(含 高對比與色盲友善)、跟隨編輯器或系統的深淺色。poly.markdownGithubStyle.enabled 打開; 那個 extension 還裝著的時候 poly 讓開。
  • TODOs 檢視:檔案總管多一個面板,列出整個 workspace 的 TODO/FIXME/HACK/ XXX/BUG。只在面板顯示時才掃描,排除規則沿用 files.exclude/search.exclude, 而且掃描上限會寫在標題上——「清單很短」跟「清單被截斷」不該長得一樣。
  • 語法顏色設定 poly.syntaxColors:使用者 settings.json 的 poly 區塊裡一張表,項目是 TextMate scope、值是顏色加選用樣式,例如 comment → #6A9955 italic。改完畫面當場重新上色, 刪掉那一項就回到 theme 的顏色。一個 scope 也套到它底下更長的 scope,comment 涵蓋每一種註解。 主題只讀 editor.tokenColorCustomizations,所以 poly 把這張表複製進去,成為名為 poly.syntaxColors 的 rule;你自己在那裡寫的 rule 與各 theme 專屬的設定不會被動到。 Go、TypeScript、Python 這類有 semantic tokens 的語言,language server 的顏色會蓋在上面, 那部分要改 editor.semanticTokenColorCustomizations。
  • Poly: Set Syntax Color:不知道 scope 叫什麼就用這個。從目前這個檔的文法的 scope 清單挑一個 (可打字過濾,已設定的會顯示目前的值),輸入 #C586C0 或 #C586C0 italic,寫進 poly.syntaxColors;留空就刪掉那一項。遠端視窗(WSL、SSH)讀不到文法,改成直接輸入 scope 名稱。
  • Poly: Syntax Colors for This Language:同一份清單整份列出,做成可以直接複製的 poly.syntaxColors 片段。改配色這件事 VSCode 一直都做得到,卡住的是沒人知道 scope 叫什麼—— 內建的 Inspect Editor Tokens and Scopes 一次只給游標下的那一個。顏色欄位是 #RRGGBB 佔位字串而不是某個預設色:整份貼上去不會改變任何顏色,你只會改你改過的那幾條。

poly — CLI

  • 內嵌引擎(免安裝、離線可用):TypeScript/JavaScript(lint 是 deno_lint 的 recommended 規則集,code 長 deno_lint/*;專案自己裝了 eslint 或 biome 就換它們, 同一份檔案不會被兩套規則各報一次)、JSON/JSONC、 Markdown(格式化,lint 是 rumdl 的 7 條規則,code 長 rumdl/MD*——見下面的說明)、 TOML、YAML、CSS/SCSS/LESS、HTML/Vue/Svelte/Astro/Jinja、 Python/Jupyter(格式化與 lint 都是 ruff)、SQL、XML、 GraphQL(格式化,lint 只有語法檢查——見下面的說明)、 Dockerfile(格式化,lint 是 poly 自己寫的規則,code 長 poly/docker-*; hadolint 預設關閉,因為它跟 poly 的規則大部分重疊——見下面的外部工具), Lua(格式化 stylua、lint selene)、 PHP(格式化與 lint 都是 mago,lint 是它 190 條規則裡的 7 條——見下面的說明)、 Protobuf(lint 是 poly 自己寫的規則,code 長 poly/proto-*;格式化仍是 buf)、 PowerShell/批次檔(.ps1/.psm1/.psd1、.bat/.cmd,格式化是 poly 自己寫的, 只重新縮排、清行尾空白與連續空行,行內照你寫的;批次檔的行尾空白不動)。 這些都是編進 binary 的 Rust library, 不再下載。拼字檢查(typos)也在裡面,而且不分語言——它讀的是每一個檔案, 包含 poly 認不出語言的那些。
  • 外部工具(受管下載):shellcheck、shfmt、actionlint、 tflint、gofumpt、golangci-lint、swiftlint、buf (Protobuf 的格式化)、 arity(R 的格式化與 lint)、 jsonnetfmt(Jsonnet 的格式化,.jsonnet/.libsonnet,用它的預設值,不另設旗標)、 PlantUML(MIT 版的 jar,給編輯器的預覽與匯出用,不做 lint;Java 要自己裝)。PATH 上 已經有同一個工具就直接用它,版本不同沒關係,只要主版號跟 poly 釘的一樣(golangci-lint 要 2.x,1.x 的命令列不相容);PlantUML 例外,一律用 poly 下載的 jar。PATH 上沒有才 下載 poly 釘死的版本,每個平台的 sha256 都預先寫進 poly-tools.lock 並編進 poly—— 下載對不上就直接失敗,而不是信任第一次抓到的東西。
  • zsh 只格式化,不 lint(.zsh)。shfmt 讀得懂 zsh 文法,shellcheck 讀不懂——它只支 援 sh/bash/dash/ksh。以前 poly 把 .zsh 一起丟給 shellcheck,結果是拿 bash 文法解析 zsh:361 個真實 .zsh 檔案上產生 2,454 條 findings,佔量測語料裡全部 shellcheck findings 的 55%,其中最多的一條還是叫你替 zsh 根本不會做 word splitting 的展開加引號。現在 .zsh 是自己的語言,格式化照舊,lint 沒有——覆蓋回報的 shellcheck N files 也不再把它們算進 去。[format.zsh] 是它的設定區段。
  • 預設關閉但仍可用:hadolint。poly 現在有自己的 Dockerfile 規則,兩邊一起跑 等於同一個缺陷印兩次、掛兩個 code、兩種嚴重度,[lint] fail-on 會變成看誰先講話。 拿 256 個真實 Dockerfile 量過:hadolint 的 shellcheck findings 是 poly 自己那套 的子集(65 對 534,沒有一個 code 是 hadolint 有而 poly 沒有的),而且位置更差 (每個 RUN 的第 1 欄,poly 指到出問題的那個字)。poly 沒有補的是它三條 info 級 規則:DL3047、DL3059、DL3066。想同時看兩邊就寫 [tools] hadolint = "on", poly 會照樣下載並執行它,不會囉嗦。
  • actionlint 有兩組檢查關掉了,都是同一個道理:poly 自己已經在做那件事。一是它 的 shellcheck pass——workflow 的 run: 由 poly 自己跑 shellcheck,掛 shellcheck/SC… 並指到出問題的那個字;開著等於同一個缺陷被 actionlint 用 error 再報一次、指在 run: 那一鍵。二是跟 poly 規則重疊的五個檢查:runner label、step 的未知 key、既沒 uses: 也沒 run: 的 step、event filter、permission scope。
  • 其中 runner label 那條原本會讓自架 runner 的專案一跑就整片紅。 1,190 個真實 workflow 上,actionlint 全部 926 條 findings 有 655 條是它(70.7%),而其中 621 條是自架 runner 的名字:amd-medium、blacksmith-4vcpu-ubuntu-2404,某個 repo 自己的 pool 就佔 530 次。它沒有辦法知道那些名字是真的,而且每一條都是 error。poly 的規則只在三種情況出聲——已退役的 image、跟真名差一兩個字、不存在的版本號——其餘 一律當作自架或第三方 runner 放過。另外四個檢查關掉零成本:重複的 69 條全部落在同 一行同一欄,而且 poly 報得不比 actionlint 少(27:14、13:13、7:4、5:4)。代價只有 一項:你如果在 .github/actionlint.yaml 列了自己的 label,actionlint 讀得到而 poly 讀不到,那份清單裡的拼錯就沒人抓。actionlint 其餘的檢查一條都沒少,它的 expression type checker 更是它留在這裡的全部理由。
  • 只用專案 toolchain、不代裝:rustfmt、clang-format、swift-format、 terraform fmt、cargo clippy。
  • Rust 的 lint 是 cargo clippy,範圍是整個 cargo workspace——跟 Go 的 golangci-lint(整個 module)、Terraform 的 tflint(一個目錄)同一個機制:存檔時 在編輯器裡跑的,跟 poly check 在 CI 裡跑的,是同一次呼叫。build 目錄是 target/poly 而不是預設的 target/,這樣你在終端打的 cargo test 不會等 編輯器(rust-analyzer 也是這麼做的);代價是多一棵 build tree,第一次會編一次。 不想要就 [tools] cargo = "off"。
  • TypeScript 裡的 css/html/sql 標籤模板會一起格式化,用的是 poly 格式化 .css/.html/.sql 檔的同一個引擎,所以 styled-components 的樣式、lit 的模板存檔後 跟獨立檔案長得一樣。styled.div 與 styled(Button) 開頭的模板也算 CSS。插值 (${…})原地保留,格式化不會動到它裡面的運算式。標籤是其他名字,或片段本身解析不了 (標籤模板常常只是一個片段,不是完整的檔案),就原樣留著——不會讓整個檔案格式化失敗。
  • Protobuf 的 lint 是 poly 自己的規則,不需要 buf module:.proto 上方沒有 buf.yaml 也照樣檢查(以前這種檔案是整個跳過的)。有 buf.yaml 的話,它的 lint 區段——use、except、ignore、ignore_only,v1 v2 都讀——決定哪幾條規則跑, // buf:lint:ignore 註釋也照樣有效。poly 這 14 條對應 buf BASIC 那層的 單檔規則;buf STANDARD 多加的那批命名慣例(ENUM_VALUE_PREFIX、 PACKAGE_VERSION_SUFFIX、SERVICE_SUFFIX 等)、需要整個 module 的規則 (PACKAGE_SAME_*、RPC_REQUEST_RESPONSE_UNIQUE …)與需要解析 import 的規則 (IMPORT_USED、PROTOVALIDATE …)都沒有。編譯錯誤現在由 poly fmt 抓(那仍是 buf)。 poly 的 parser 還不支援 edition = "2023",遇到讀不了的檔案會報 poly/proto-unreadable——那是「poly 沒檢查這個檔案」,不是「這個檔案有問題」。 格式化不受影響,.proto 一律格式化。
  • 看起來不是本人的字元(code 長 poly/unicode-*,類別 confusable-character, 等級一律 warning)。不分語言,每個檔案都檢查——這是取代 gremlins 那類編輯器裝飾的部分, 差別在於它同樣會在 CLI 與 CI 裡紅。五條規則:-bidi(雙向控制字元,也就是 Trojan Source)、-invisible(零寬字元、軟連字號、ETX/VT 控制字元、行與段落分隔符號 U+2028/U+2029、物件取代字元 U+FFFC;檔首的 BOM 不算)、-space (不斷行空格、全形空格這類「看起來是空白但不是」)、-lookalike(EN DASH 之於 -、 彎引號之於 '/")、-mixed-script(同一個字裡混了西里爾或希臘字母)。 界線是**「會被誤認成某個 ASCII 字元」而不是「非 ASCII」**,而且是量出來的: 這個 repo 有 1504 個 EM DASH、1124 個全形逗號冒號分號,全都是正確的中文排版, 一併報就是 1162 筆噪音;只報真正會混淆的則是 22 筆。所以 em dash、全形標點、 法文引號都不在規則裡。編輯器要把看不見的字元畫出來,VSCode 內建 editor.unicodeHighlight.invisibleCharacters/.ambiguousCharacters 就是那個功能, poly 不再畫第二次。
  • GraphQL 的 lint 只有語法檢查(code 是 graphql/syntax,等級 error),與 TOML、 TypeScript 一樣是「這個檔案不是它副檔名說的那個語言」。用的是格式化時的同一支 parser (apollo-parser,October 2021 版規格),所以編輯器與 CI 指的是同一個字元。 不做 schema 驗證,這是量過的決定:854 個真實 .graphql 上,驗證器報的東西有 98.8% 是「定義在別的檔案」——federation 的 @link、隔壁模組宣告的 type、只有幾個 type 而沒有 root 的 schema 片段。一份 schema 是好幾個檔案組起來的,而 poly 一次看一個檔案,那些不是 這個檔案的缺陷。
  • YAML 與 TOML 照它們指定的 JSON Schema 驗證(code 長 schema/<keyword>,例如 schema/required、schema/type、schema/additionalProperties,等級 warning)。schema 由 檔案自己指定,寫法跟 redhat.vscode-yaml 與 even-better-toml 讀的那行相同——YAML 寫 # yaml-language-server: $schema=<網址或路徑>,TOML 寫 #:schema <網址或路徑>——或在 poly.toml 的 [lint.schemas] 用 glob 對應。兩者都沒有就不檢查,poly check 的 coverage 只有在真的有檔案被檢查時才多一列 schema。錯誤標在出錯的那個 key 或值上:拼錯的 key、 型別不對的值、少了必填欄位的那個區段的 key。網址的 schema 一天抓一次、存在 poly 的快取, 沒網路時用快取的那份;完全讀不到時算「沒檢查到」(結束碼 2),不會當成通過。 SchemaStore 的檔名對應不用,這是量過的決定:1,329 個真實 YAML/TOML 上它對到 569 個, 其中 106 個是錯的檔(一個 **/scenarios/*/*.yaml 就把 104 個 QA fixture 當成資安工具的設 定檔);而對得對的那些,報出來的東西大多是 schema 比讀它的程式還嚴或還舊。XML 的 XSD/DTD 不做;JSON 由 VSCode 內建的 JSON 支援驗證。
  • Markdown 的 lint 是 rumdl 的 7 條規則,全部只報壞掉的東西:相對連結指向不存在的檔 (MD057)、錨點不存在(MD051)、連結寫反 (文字)[網址](MD011)、空連結(MD042)、 參考式連結沒有定義(MD052)、標題跳級(MD001)、圖片沒有 alt(MD045)。code 長 rumdl/MD*,rumdl 自己的 rumdl-disable/rumdl-disable-next-line 註解(<!-- ... --> 那種形式,寫成 HTML 註解)照樣有效,要整個關掉某一條就寫 [lint] ignore。 版面的規則(行長、標題與清單前後的空行、強調的寫法…)一條都不開,專案自己的 .rumdl.toml 也不讀:那些是 poly fmt 的事,開下去等於報 poly fmt 前一秒才寫出來的 東西——實測 4,947 個檔案,光 MD036 就有 1,054 條是格式化自己造出來的。 一個已知落差:MD051 只答得出同一個檔案裡的錨點。other.md#section 這種跨檔錨點, rumdl 自己是先索引整棵樹才能檢查的,而 poly 一次只看一個檔案(編輯器裡本來也只有那一個 檔案),所以不報——同一批檔案裡有 58 條。
  • PHP 的格式化是 mago,預設就是 PSR-12 的 120 欄/4 空白,[format.php] 三個 knob 全部有效。.php/.php4/.php5/.phtml/.ctp 都認,模板裡的 HTML 不動。 lint 只開 7 條(code 長 mago/*,等級 warning):preg_quote() 沒給 delimiter、 用 ==/=== 比對 token 或密碼、迴圈第一圈就必定跳出、printf 佔位符比引數多、 finally 裡有 return/break、短開頭標籤 <?、explode() 兩個引數寫反。 另外 php/syntax(等級 error)是「PHP 不會跑這個檔案」,與 TOML、TypeScript、GraphQL 同一種說法。其餘 106 條預設規則一條都不開,這是量過的決定:8 個真實 PHP 專案的 20,200 個檔案上,mago 的預設規則集報 83,756 條、命中 81.5% 的檔案,其中三分之二是 「每個檔案都要寫 declare(strict_types=1)」「不要用 isset」「不要用 else」這類 house style;它的 error 等級也有 64% 是複雜度指標而不是缺陷。專案自己的 mago.toml 不讀,@mago-ignore 註釋也不是 poly 的抑制方式——要關某一條就寫 [lint] ignore, 單行就寫 // poly: ignore mago/<rule>。
  • R(.R/.r)的格式化、lint 與語言功能都是 arity,一支 binary,poly 代抓, 不必先裝 R。專案自己的 arity.toml 或 air.toml(版面、select/ignore)照樣生效, # arity-ignore <rule>: 理由 註釋也照樣有效。code 長 arity/*;lint 以 R 套件為 單位跑——R/ 底下互相引用的符號不會被誤報成未定義,編輯器與 poly check 是同一個答案。 自己宣告是產生出來的檔案不會被重排:開頭八行有註釋寫 do not edit 的,poly fmt 一律跳過——Rcpp 的 RcppExports.R、cpp11 的 cpp11.R、rlang 的 import-standalone-*.R 都是這一類。剩下一個已知落差:arity 另外還跳過 revdep/ 與 renv/ 這兩個目錄,poly 不跳,因為那底下是手寫腳本而不是產生出來的(七個真實 R 套件、1,464 個檔裡有 8 個)。不想格式化就寫進 [format] exclude。
  • poly minify [路徑...]:把 JSON/JSONC 就地壓成一行,移除空白與註解。走跟 poly fmt 同一套 walk 與 [format] exclude,所以 CLI 與編輯器命令答案一致。 獨立命令而不是 poly fmt 的旗標——兩者契約相反,fmt 是「符合專案風格」,而沒有 人的風格是一行 40KB。不支援 --strict/--format/--fail-on(沒有 findings 可 塑形,也沒有外部工具會缺席),拼對了卻無效的旗標一律拒絕。
  • 工具解析順序:指定(VSCode 的 poly.tools,其次 poly.toml 的 [tools])→ 專案內工具 → 內嵌引擎 → PATH(主版號相同即可)→ 受管下載。指定版本號時跳過 PATH,直接下載那個版本。

安裝

需要 VSCode 1.85 以上。從 Releases 下載:

  1. poly-syntax-highlight-<版本>.vsix — 通用,不分平台。
  2. poly-lsp-<平台>-<版本>.vsix — 要挑對平台,內含對應的 poly binary: darwin-arm64(Apple Silicon)、win32-x64、linux-x64(WSL 也用這個)。其他平台沒有 poly-lsp,CLI 請用下方的獨立 binary。

安裝方式:VSCode 側邊欄 Extensions → 右上角 ... → Install from VSIX... → 選檔案 → 重新載入視窗。或用命令列:

code --install-extension poly-syntax-highlight-0.20.0.vsix
code --install-extension poly-lsp-darwin-arm64-0.20.0.vsix

之後的版本由 extension 自己下載安裝,裝好只問要不要重新載入視窗——兩個各自照自己的間隔檢查, 哪個先發現就一起更新你已經裝了的那幾個。不想自動更新就關掉 poly.updateCheck.enabled (只裝 poly-syntax-highlight 的話是 poly.syntax.updateCheck.enabled)。

Extensions 面板齒輪選單裡的「自動更新」勾選框管不到 poly:那是 VSCode 從 Marketplace 更新用的, poly 不在 Marketplace 上,勾了也沒有東西可以更新,而且每次從 VSIX 安裝都會被 VSCode 重設。

從 0.18.20 以前升上來

設定從一項一行("poly.format.enabled": false)改成一個 poly 區塊(見設定)。 升上來之後,把舊的那幾行的值填進區塊裡對應的項目,再刪掉舊的;poly 不會替你搬。 poly.languageServers 與 poly.languageServerLogs 已經拿掉:poly 只做格式化與 lint, 跳到定義、補全這些語言功能交給各語言自己的 extension(Go 是 golang.go),舊的設定直接刪掉。

從 0.18.2 以前升上來

poly-editor 在 0.18.3 併進了 poly-lsp。舊的 Poly Editor 要自己解除安裝,否則同一份 功能會跑兩份(兩排 lens、Enter 接兩次清單)。

從 0.5.0 以前升上來

兩個 extension 在 0.6.0 改了名字(poly-lint → poly-lsp、poly-syntax → poly-syntax-highlight)。換名字等於換 extension id,所以新版是另一個 extension,只能手動裝。

0.5.0 的更新提示還是會跳,但按下 Install 一定失敗,而且訊息會騙你:

Poly: automatic install failed (Error: release has no asset poly-syntax-0.20.0.vsix). The VSIX files were downloaded — install them manually via "Extensions: Install from VSIX".

其實一個檔都沒下載(它在第一個找不到的 asset 就放棄了),所以「Show Files」按下 去也沒有東西。這段程式碼凍在已安裝的 0.5.0 裡,改不了。照下面手動做:

code --uninstall-extension ricky.poly-lint
code --uninstall-extension ricky.poly-syntax

再檢查 settings.json:如果裡面有 "editor.defaultFormatter": "ricky.poly-lint" (曾經點過 format-on-save 提示的話就會有),要改成 "ricky.poly-lsp"。留著舊 值不會報錯,只會指向一個不存在的 extension,然後格式化安靜地不動作。

只要 CLI

不必裝 extension。macOS/Linux:

curl -fsSL https://raw.githubusercontent.com/linzeyan/vscode-syntax/main/install.sh | sh

Windows(PowerShell):

irm https://raw.githubusercontent.com/linzeyan/vscode-syntax/main/install.ps1 | iex

腳本挑對平台、對 SHA256SUMS 驗 sha256、把 binary 放進 ~/.local/bin (Windows 是 %LOCALAPPDATA%\Programs\poly,並寫進使用者 PATH)。要換位置或釘 版本就設環境變數——irm | iex 沒辦法傳參數,所以兩邊都認得:

POLY_VERSION=0.20.0 POLY_INSTALL_DIR=~/bin sh install.sh

Windows on ARM 上會裝 arm64 版,即使腳本本身跑在 x64 模擬層裡(從 ssh 或某些 終端機啟動時會發生,此時 PROCESSOR_ARCHITECTURE 說的是 process 不是機器)。

要自己來的話,Release 另附獨立 binary(poly-darwin-arm64、poly-linux-x64、 poly-win32-arm64.exe …):

curl -fsSLO https://github.com/linzeyan/vscode-syntax/releases/latest/download/poly-darwin-arm64
xattr -d com.apple.quarantine ./poly-darwin-arm64   # 瀏覽器下載才需要
chmod +x poly-darwin-arm64 && mv poly-darwin-arm64 /usr/local/bin/poly

SHA256SUMS 一併發佈,可先驗再用。Windows 上未簽章的 poly.exe 可能被 SmartScreen 擋,處理方式見 extensions/lsp/README.md。

在 GitHub Actions 裡用

- uses: linzeyan/vscode-syntax@v0
- run: poly check --strict .

@v0 會跟著最新的 release 走。要釘死版本就寫 with: { version: "0.20.0" }——poly 會改寫檔案,所以新版本自己跑進來有可能把綠的分支變紅。

Action 做三件事:抓對應平台的 binary、對 SHA256SUMS 驗 sha256、放進 PATH。順便 快取 poly 之後會下載的外部 linter(with: { cache: false } 可關)——冷跑一次 poly check 在 lint 任何東西之前要先抓幾十 MB 的 shellcheck、hadolint。

在容器裡用

docker run --rm -v "$PWD:/work" ghcr.io/linzeyan/poly check --strict .

linux/amd64 與 linux/arm64 都有。tag 有 latest、0.20.0、0.20;pre-release 不會動到 latest。image 裡的 binary 就是 release 附的那一支,不是另外編的。

image 不含任何語言 toolchain,只含 poly 自己會下載的那些 linter。所以 Rust 專案在容器裡要拿掉 --strict——cargo clippy 只可能來自 toolchain,poly 下載不 到,--strict 會(正確地)把它當成錯誤。同理 poly fmt 在容器裡不會有 clang-format、swift-format、terraform fmt。

外部 linter 快取在 /cache,CI 裡掛個 volume 上去就不用每次重抓:

docker run --rm -v "$PWD:/work" -v poly-cache:/cache ghcr.io/linzeyan/poly check .

在 pre-commit 裡用

單一 binary、沒有 runtime 依賴,直接當 hook 用。手寫 .git/hooks/pre-commit:

#!/bin/sh
poly fmt --check --changed || {
  echo "run: poly fmt --changed" >&2
  exit 1
}
poly check --changed --strict

--changed 的範圍是 working tree vs HEAD 加上 untracked,比「只看 staged」寬——用 git add -p 分次 stage 時會檢查到還沒 stage 的改動,是刻意的保守近似。

用 pre-commit framework 的話它自己會把 staged 檔案逐個傳進來,範圍更準:

repos:
  - repo: local
    hooks:
      - id: poly-fmt
        name: poly fmt --check
        entry: poly fmt --check
        language: system
        pass_filenames: true
      - id: poly-check
        name: poly check
        entry: poly check --strict
        language: system
        pass_filenames: true

驗證裝好了

poly tools          # 列出每個外部工具解析到哪裡
poly fmt --check .  # 對整個 repo 做 dry-run

在編輯器裡開一個 .rs 檔,Developer: Inspect Editor Tokens and Scopes,把游標 放在 -> 上——scope 含 keyword.operator.arrow.skinny.rust 就代表 poly 的文法 生效了(內建文法沒有這個 scope)。

CLI 用法

poly fmt <paths...>            # 就地格式化
poly fmt --check <paths...>    # 只回報,不改檔(CI 用)
poly check <paths...>          # 跑 lint
poly check --strict <paths...> # 工具缺席時視為錯誤,而不是跳過(fmt 也吃)
poly fmt --changed             # 只處理 git 變更的檔案(pre-commit 用)
poly tools list                # 工具解析狀態
poly tools install [tool...]   # 預先抓好受管工具(離線環境先在有網路的機器跑)
poly config export             # 印出含所有預設值與註解的 poly.toml
poly deadcode [路徑]           # 進入點走不到的程式碼(見下)
poly lsp                       # 給編輯器用的 LSP daemon
poly --help                    # 完整說明
poly --version                 # 版本(確認 PATH 上是哪一支)

fmt 與 check 共用的旗標裡,這五個值得說明:--format 決定 stdout 的形狀(見 下),--compact 每個問題只印一行,--no-ignore 連 git 忽略的檔案也處理, --hidden 連點開頭的檔案/目錄也處理,--strict 讓「工具找不到」變成錯誤而不是 跳過該檔。--check 只有 fmt 認得——check 本來就不寫檔,給它 --check 會直接 報錯而不是靜默忽略。

--strict 值得特別說:預設情況下 gofumpt 或 swift-format 沒裝,poly 會在 stderr 說一聲然後跳過那些檔案,exit code 不受影響。這對「不是每台機器都裝了每套 toolchain」是對的預設,但 CI 需要的是相反的答案——--strict 就是那個開關。

poly deadcode:進入點走不到的程式碼

跟 poly check 分開,因為它回答的是另一個問題。單檔 linter 問「這個 package/模組裡 有沒有人提到它」,所以匯出的東西永遠不算 unused——外面可能有人用。這個命令問的是 「從進入點有沒有任何路徑會跑到它」,那才是刪掉一段程式碼之前要問的問題。代價是它要花 一次 build 的時間,而且對 library 來說每個匯出的 API 都會是「死的」(呼叫者在別人的 repo 裡)——所以它是你去問的,不是存檔時自動跑的,也不進 CI gate。

四個語言,三支工具,poly 一支都不自己寫(R7/A6):

語言 工具 範圍怎麼決定
Go golang.org/x/tools/cmd/deadcode 往上找到 go.work 就用它,否則最近的 go.mod
TypeScript/JS knip 那個 knip 旁邊的 package.json
Python vulture 最近的 pyproject.toml/setup.py/setup.cfg

給一個檔案就只跑它那個語言的;給一個目錄,則凡是往上找得到標記的都跑——monorepo 裡 只回答三個語言中的一個,是沒有人要的子集。

Rust 沒有,這是誠實的空白:rustc 自己的 dead_code 已經隨 cargo clippy 每次存檔 就到了,而跨 crate 的那一問沒有主流工具在回答。

Go 的跨 module 靠 go.work:有 go.work 的話分析從 workspace 根開始,liba 裡只被 appb 呼叫的函式就是活的;沒有 go.work,liba 根本不在 build list 裡。這是 Poly: Create go.work for the Open Go Modules 那個命令的第二個用途。

三支工具都跟著各自的 toolchain 走,poly 不代裝:

go install golang.org/x/tools/cmd/deadcode@latest   # Go
npm install --save-dev knip                          # TypeScript/JavaScript
pip install vulture                                  # Python
poly deadcode .

vulture 是唯一需要 poly 幫忙的:它自己走目錄,而且不知道 venv 是什麼——直接指給它一個 專案根目錄,會拿到 pip 內建那份 vendored 程式碼的幾千條回報。poly 改成把自己走出來的 檔案清單交給它(同一套 .gitignore 與 [lint] exclude,跟 poly check 一致),再加 一層 --exclude 擋掉 venv/site-packages。

編輯器裡是 Poly: Analyze Dead Code,在終端跑同一行;上面這些語言的每個檔案,第一行 程式碼上面也會有一條 analyze dead code lens(poly.deadCodeCodeLens.enabled 可關)。 不是第 0 行——shebang、版權標頭、//go:build 都在那上面。一個檔一條,不是一個函式一條 ——分析本來就是整個 program 的,一個函式一條只是同一個答案的 N 個入口。

什麼算失敗:--fail-on

poly 預設對任何問題都 exit 1,連 info 等級的錯字也算。要放寬就設嚴重度門檻:

poly check --fail-on warning .   # info/hint 照樣回報,但不擋
poly check --fail-on error .     # 只有語法錯誤等級才擋
poly check --fail-on never .     # 純報告,永遠 exit 0

--fail-on=warning 與 --fail-on warning 都認得。低於門檻的問題還是會印出來, summary 會加註 (N below fail-on),所以綠色的 run 有輸出不會被誤讀成 bug。

四個等級是 poly 的判斷,不是照抄上游工具的:

等級 意思
error 幾乎確定是缺陷:會壞、不安全,或不合法
warning 可疑但可能是故意的,值得看一眼
info 風格與一致性,不影響正確性
hint 建議與偏好

同一份判準套到每個工具,所以 --fail-on error 在 Lua、SQL、Dockerfile、workflow 上 擋的是同一類東西。本來就有等級而且意思相同的工具(shellcheck、clippy、biome、eslint、 swiftlint、selene、tflint、hadolint、arity、rumdl)照用它們自己的;不排序的工具由 poly 排一次 (ruff、golangci-lint、sqruff、deno_lint 是 warning,typos 是 info——deno_lint 把每一條 都印成 error 是它 CLI 的顯示方式,不是分級);poly 自己的規則則是一條規則一個等級。

寫進 poly.toml 才能讓編輯器與 CI 同一套標準,而且兩邊可以不同——「沒格式化要擋, 錯字不用」是很常見的政策:

[format]
fail-on = "warning" # 未格式化是 warning,所以 "error" 等於讓格式化只是建議

[lint]
fail-on = "error" # typos 報 info、多數 linter 報 warning、語法錯誤報 error

旗標壓過設定檔。這不是 Rust 的 -D warnings——那個旗標存在是因為 Rust 的 warning 預設不會 fail,poly 是相反的問題。

Exit code:0 乾淨、1 有差異或違規、2 執行錯誤。--help 與 --version 放在 哪個位置都認得(poly fmt --help 跟 poly --help 一樣);--help 走 stdout、 exit 0,指令打錯則是同一份說明走 stderr、exit 2。

說明文字有英文與正體中文兩版,看系統 locale(LC_ALL/LC_MESSAGES/LANG), POLY_LANG=en 或 POLY_LANG=zh-TW 可強制指定——CI 要讓 log 語言固定時用它。只有 說明文字翻譯:診斷紀錄的格式是給 script 解析的契約,而且訊息有一半來自只講英文的 上游工具,翻譯嚴重度只會弄壞所有消費端。

輸出格式

不管問題是哪個 linter 或 formatter 找到的,fmt 與 check 都印同一種紀錄,走 stdout:

src/app.py:1:8: warning [ruff/F401] `os` imported but unused
    fix   Remove unused import: `os`
    docs  https://docs.astral.sh/ruff/rules/unused-import
deploy.sh:4:8: info [shellcheck/SC2086] Double quote to prevent globbing and word splitting.
    fix   shellcheck can rewrite this automatically
    docs  https://www.shellcheck.net/wiki/SC2086
schema.sql:1:1: warning [poly/unformatted] file is not formatted
    fix   run `poly fmt`

第一行是完整紀錄:路徑:行:欄: 嚴重度 [工具/規則] 說明,永遠只有一行且前綴固定, 所以 rg、CI annotation script、終端機的檔案連結都吃得下。後面縮排的 fix/docs 只在該工具真的有給時才出現——多數 linter 只說哪裡錯、把怎麼修留給文件,poly 不替它 們編造。--compact 會把縮排行全部拿掉。

編輯器裡是同一份資訊:fix 併進 Problems 的訊息(LSP 沒有對應欄位),docs 變成 規則代碼上的超連結,用字與 CLI 完全相同。

錯誤(parse 失敗、工具缺席、引擎不接受的設定)也是同一種紀錄,嚴重度 error、規則 poly/format,位置指在 parser 停下來的地方;引擎畫的 code frame 縮排接在後面,一個 問題仍然只佔一行有錨點的輸出。

這次誰看過:coverage

綠燈有兩種意思——「沒問題」跟「沒人看」。poly check 每次都在 stderr 印一份 coverage 把兩者分開:這次走到的檔案是被誰檢查的、誰沒檢查、為什麼。

coverage:
  actionlint      6 files  ran
  biome          64 files  absent — no node_modules/.bin/biome with a biome.json above these files
  cargo          25 files  ran
  deno_lint      27 files  ran
  eslint         27 files  absent — no node_modules/.bin/eslint with an eslint config above these files
  hadolint        1 file   off-by-default — add `hadolint = "on"` under [tools] to run it as well
  poly/actions    6 files  ran
  poly/docker     1 file   ran
  ruff           12 files  ran
  shellcheck     10 files  ran
  toml            8 files  ran
  typos         136 files  ran
8 tools ran, 0 issues

只列這次有檔案可看的 checker:沒有 Go 的 repo 不會印 golangci-lint。狀態六種:

狀態 意思
ran 跑過了
missing poly 找不到這支工具,--strict 會因此 exit 2
disabled poly.toml 寫了 [tools] <名字> = "off"
off-by-default poly 預設不跑它,= "on" 才會
absent 專案自己沒有(eslint/biome 只用專案裝的那份)
failed 跑了但壞了,exit 2

files 是 poly 交給它的檔案數,不是它最後讀了幾個——工具自己的設定(_typos.toml 的 exclude、buf.yaml 選的規則)還會再縮一次。Windows 沒有 shellcheck build,所以 Dockerfile 的 RUN 與 workflow 的 run: 在那裡是 missing 而不是靜靜地跳過。

同一份資料在 --format json 的 summary.coverage,四個欄位:tool、files、 status、reason。

換個形狀:--format

--format 只改 stdout 的形狀,不改判定結果——exit code 與 stderr 的 summary 在四種形狀下完全一樣。

值 用途
text 上面那種紀錄,預設
json 單一份文件,欄位齊全,不必再從文字解析
table 對齊的欄位,掃過去用
table_markdown 貼進 PR 留言或 $GITHUB_STEP_SUMMARY
poly check --format table .
FILE         SEVERITY  RULE               MESSAGE
lint.py:1:8  warning   ruff/F401          `os` imported but unused
run.sh:2:6   info      shellcheck/SC2086  Double quote to prevent globbing and word splitting.

json 是給 pipeline 的。位置是 1-based(跟紀錄一致),message 完整保留(含引擎畫的 code frame),fix 是跟終端機、編輯器一字不差的同一句話,fatal 直接告訴你這一筆在 當前 --fail-on 下算不算擋——消費端不用重寫嚴重度排序。category 是這是哪一類缺 陷,全部語言共用同一套詞(unpinned-dependency、unused-code、silently-discarded …),所以一份 pipeline 的 findings 可以照類別分組而不是照工具。poly 自己的規則每條都 有;大部分上游規則是 null——ruff、clippy、eslint 各有數百條而且專案可以自由開關,替 每一條取一個 poly 名字等於再養一套會漂移的規則表。summary.uncategorized 就是這一次 有幾筆沒有類別:

{
  "version": 2,
  "command": "check",
  "issues": [
    {
      "file": "lint.py",
      "line": 1,
      "col": 8,
      "end_line": 1,
      "end_col": 10,
      "severity": "warning",
      "tool": "ruff",
      "rule": "F401",
      "category": null,
      "message": "`os` imported but unused",
      "fix": "Remove unused import: `os`",
      "docs": "https://docs.astral.sh/ruff/rules/unused-import",
      "fatal": true
    }
  ],
  "summary": {
    "issues": 1,
    "fatal": 1,
    "uncategorized": 1,
    "coverage": [
      { "tool": "ruff", "files": 12, "status": "ran", "reason": null },
      {
        "tool": "shellcheck",
        "files": 4,
        "status": "missing",
        "reason": "shellcheck has no managed build for win-x64 and is not on PATH"
      }
    ]
  }
}

version 只在欄位改變意義或消失時才加,新增欄位不動它。stdout 只有這份文件, poly check --format json . | jq 不會被 stderr 的 summary 弄髒。

兩種 table 只有四欄,不含 fix/docs——一列必須是一行,而一整欄的 URL 比其他三欄 加起來還寬。table_markdown 把 docs 連結掛在規則名上,不多佔寬度。要完整資訊用 text 或 json。--compact 只對 text 有意義,配其他形狀會直接報錯。

放進 GitHub Actions:

- run: poly check --format table_markdown . >> "$GITHUB_STEP_SUMMARY"

設定

VSCode:settings.json 的 poly 區塊

poly-lsp 的設定全部在一個 poly 物件裡,extension 啟用時把它整個寫進使用者的 settings.json:每一項上面的註解寫用途、可選的值與預設值(照編輯器的語言,英文或正體中文), 沒設的項目是註解掉的預設值。要改哪一項就取消註解再改值。每次啟用都會依裝的版本重寫註解與 預設值,你設過的值原樣保留,poly 不認得的鍵也留著並註明。

"poly": {
  "format": {
    // 用途:允許 poly 改寫檔案:Format Document、存檔時格式化、Format Selection……
    // 可選:true|false;預設:true
    "enabled": false,
  },
  …
}

這份 README 寫 poly.format.enabled 的地方,指的就是 "poly": { "format": { "enabled": … } }。 設定畫面裡 poly 只有一項「在 settings.json 內編輯」;要逐項挑值,用命令 Poly: Settings (設定畫面那一項的說明裡也有連結):每一列是一項與它目前的值,可以用鍵名、值或用途篩選; 選了就從清單挑值或輸入,挑回預設值等於取消設定,陣列與物件則跳到 settings.json 的那一行。 它寫的就是這個區塊。Format/Lint 開關與 Poly: Set Syntax Color 直接改這個區塊,註解不會掉;settings.json 有沒存檔的修改時它們會請你 先存檔,啟用時的重寫則等下一次。工作區的 .vscode/settings.json 用同樣的巢狀寫法,只寫要覆蓋的 那幾項。

要自己在 settings.json 設的

這兩項是 VSCode 與 golang.go 自己的,poly 不改它們:

// 編輯器自己的 Find All References 開 peek 還是開內建的 References 面板。預設 "peek";
// 設成 "view" 會和 poly 的 `N refs` CodeLens 一樣留著清單不跑掉——但兩邊是不同的樹,
// 帶行號與符號欄位的是 poly 那棵。
"references.preferredLocation": "view",
// golang.go 自己的 `run test | debug test` 只看函式名字,`TestFoo(x int)` 也有按鈕;
// 開了 poly.runCodeLens.enabled 時 poly 畫的是看過簽名的同一組按鈕,關掉這個才不會
// 每個測試上方各有兩組。golang.go 的 `run package tests | run file tests` 也會一起消失。
"go.enableCodeLens": { "runtest": false }

poly.toml

poly.toml 是選用的。完全沒有設定檔時,語言用內建副檔名表判斷,格式化用各引擎 預設值,走訪檔案時尊重 git 會尊重的忽略檔——.gitignore、.ignore、 .git/info/exclude,以及 core.excludesFile(沒設就是 $XDG_CONFIG_HOME/git/ignore),沿路每一層祖先目錄的都算。跟 git 一樣,全域忽略 檔只在 git repo 裡生效。點開頭的檔案與目錄預設跳過,.github/ 例外(workflow 是原 始碼,actionlint 就是為它接的)。

drawio 與 excalidraw 的存檔(.drawio、.dio、.drawio.svg、.dio.svg、 .excalidraw、.excalidraw.svg、.excalidraw.json)不歸任何語言:編輯器每次存檔都照自己的排版整份重寫,格式化它只會 跟下一次存檔來回改。拼字與 Unicode 也不檢查,抓到的多半是編輯器產生的 id 片段,在檔案 裡改不掉。真的要 poly 管,在 [languages.map] 指定;map 比內建判斷優先而且分大小寫, 所以 "*.json" = "jsonc" 這種寬的 pattern 也會把它們一起接回來。

--no-ignore 關掉前一段的忽略檔,--hidden 讓走訪進入點開頭的路徑;.git/ 兩者 都進不去,物件庫不是原始碼。用在要檢查的正好是被藏起來的東西:generated code、 vendored tree、.config/ 底下的專案腳本。

專案的原始碼本來就住在點開頭目錄時,改用設定檔而不是旗標,這樣編輯器與 CI 看到的 檔案集合才一致:

[walk]
include-hidden = true

兩者都只能把範圍放寬、不能收窄;要收窄請用 exclude。poly.toml 的 exclude 不受 --no-ignore/--hidden 影響——那是專案自己說「別碰」,跟 VCS 說「別追蹤」是兩件 事。

要覆蓋預設值時,專案層的真相放 repo 根目錄的 poly.toml,CLI 與 extension 都讀 它,保證編輯器與 CI 行為一致:

[languages.map] # 副檔名 ↔ 語言
"*.tpl" = "jinja"

[format]
exclude = ["vendor/**", "**/*.generated.ts"]

[format.python] # 每語言可調的三個選項
line-width = 100
indent-width = 4
use-tabs = false

[lint]
exclude = ["third_party/**"]
ignore = ["typos/typo", "unused-code"] # 整個 repo 都不報這些

[lint.severity] # 不同意 poly 的等級就改掉,最精確的那條贏
unpinned-dependency = "info"
"poly/docker-latest-base" = "error"

[lint.per-file-ignores] # 只關掉某條規則,檔案照樣 lint
"tests/fixtures/**" = ["ruff/F401"]
"vendor/*.sh" = ["shellcheck/*"] # tool/* 是整支工具

[lint.schemas] # 這些 YAML/TOML 照哪份 JSON Schema 驗證;檔案自己的 $schema 行優先
"compose*.yaml" = "https://json.schemastore.org/docker-compose.json"
"config/*.toml" = "schemas/config.schema.json" # 路徑相對於這個 poly.toml

[walk]
include-hidden = false # 預設;true 會連點開頭的路徑一起走(.git/ 仍然跳過)

[tools] # 指定路徑、釘版本,或設 "off" 關掉;VSCode 裡改用 settings 的 poly.tools,見下
shellcheck = "C:/tools/shellcheck.exe"
tflint = "off"

規則代碼就是輸出裡印的那個——看到 [ruff/F401] 就複製 ruff/F401,沒有第二套語法要 查。三個地方(ignore、[lint.severity]、[lint.per-file-ignores])與原始碼裡的註 釋用的是同一套寫法,差別只在範圍:exclude 讓整個檔案不進 lint,ignore 是整個 repo 不報這條,per-file-ignores 只拿掉某個路徑的那一條,註釋只管一行。少了工具名的 "F401" 會讓 poly.toml 解析失敗,而不是安靜地什麼都沒關掉。

也可以寫類別(unused-code、unpinned-dependency…):就是 --format json 那個 category,一句話管到所有語言——今天決定「不看沒用到的程式碼」,明天加進來的語言照樣 算數,不必回頭補 vulture/*。poly config export 印得出完整清單;打錯字會讓解析失敗, 因為類別是 poly 自己的封閉集合,拼錯就永遠對不到任何東西。

[lint.severity] 是專案跟 poly 的等級意見不同時用的——最精確的那條贏,所以類別設 基準、tool/rule 設例外。它同時改終端機印的字、編輯器波浪線的顏色與 fail-on 擋不擋, 三者是同一個決定。編輯器與 CI 讀同一份設定,所以關掉的規則在 Problems 裡也不會出現。

要關掉的只是某一行而不是整個檔案時,把同一組代碼寫成註釋放進原始碼:

import os  # poly: ignore ruff/F401
# poly: ignore poly/docker-apt-get-unpinned, shellcheck/SC2086
RUN apt-get install -y $PACKAGES

註釋管自己這一行;獨佔一整行時再多管下面一行——長行與用 \ 續行的指令沒地方擺行尾 註釋,這個位置就是給它們的。代碼與 tool/* 跟設定檔那邊完全一樣,而且對 poly 跑的 每一支工具都有效,包含下載回來的 shellcheck、actionlint。兩者的分工是:註釋指不到路 徑,設定檔指不到行。

Dockerfile 只認寫在上一行的形式:poly fmt 會把 Dockerfile 的行尾註釋搬到獨立一 行,搬完就變成在管下一道指令,所以寫在行尾的會被報成 poly/ignore-syntax、而且什麼 都不關——與其讓它現在有效、下次格式化後改去關別行,不如當場說清楚。這是唯一有這條規 則的語言,其他語言的 formatter 都會把行尾註釋留在原地。

有 #////-- 行註釋的語言才讀得到;markdown、HTML、CSS 這類沒地方寫,就只剩 per-file-ignores 一條路。註釋裡寫了 poly 讀不懂的代碼會以 poly/ignore-syntax 報出 來,而不是讓整個 run 中斷——一個檔案裡的一行註釋不值得讓整個 repo 停下來,而且它原 本想關掉的那條 finding 還是照樣印在旁邊。各工具自己的 # noqa、 # shellcheck disable=、-- selene: allow(...) 一律照舊有效,poly 不碰。

例外是 # hadolint ignore=:hadolint 預設關閉,這行註釋就什麼都關不掉了。poly 會把 它報成 poly/ignore-syntax,並直接告訴你該改寫成哪一行——例如 # poly: ignore poly/docker-apt-get-unpinned。poly 不會去解讀 hadolint 的語法, 這是遷移提示不是相容層:講一次,讓你把註釋換掉然後刪了它。把 hadolint 開回來 ([tools] hadolint = "on")的話這行註釋照常有效,poly 也就不再提。

用 VSCode 的話,[tools] 這張表改寫在 settings.json 的 poly.tools,鍵與值都一樣 ("off"、"on"、版本號、路徑;路徑請寫絕對路徑),不必為了釘一個版本在 repo 裡多放一個 poly.toml:

"poly": {
  "tools": {
    "shellcheck": "off",
    "tflint": "0.53.0",
  },
}

extension 啟動的每個 poly 都會套用:daemon(poly lsp)、PlantUML 的 jar、終端機裡的 poly check 與 dead code 分析;改了會自動重啟 daemon。同一個工具兩邊都有寫時以 settings 為準,其他 工具照 poly.toml。純命令列(包括 CI)不讀 settings,只讀 poly.toml——要讓 CI 也用同一個 版本,就寫在 poly.toml。

[format.<lang>] 只認 line-width(1-1000)/indent-width(1-16)/use-tabs 三個鍵,拼錯或超出範圍都會直接讓解析失敗而不是靜默忽略;只作用於內嵌引擎,走外部 工具的語言請用該工具自己的設定檔。VSCode settings 放個人偏好 (poly.serverPath、poly.lintOnSave、poly.updateCheck.*)與上面的 poly.tools。

專案已經有 .editorconfig 的話什麼都不用做:內嵌引擎會沿用它的 indent_style/ indent_size/max_line_length,這三個鍵剛好就是上面那三個 knob,不是另一套要維護 的設定表面。[format.<lang>] 寫過的鍵逐鍵壓過它,所以 poly.toml 設 line-width、 .editorconfig 設縮排時兩邊都算數。引擎吃不下的值(例如 XML 沒有 line-width)在 這裡是安靜丟掉,而不像寫在 poly.toml 裡會讓解析失敗——.editorconfig 是寫給這個 repo 用過的每一個編輯器看的,不是寫給 poly 的,為了它拒絕格式化整個專案只會讓人以為是 poly 壞了。走外部工具的語言不經過這條路,那些工具自己就會讀 .editorconfig。

編輯器那半也一併沿用:打字時的 tab 寬度、存檔時的行尾空白與檔尾換行、行尾字元, 連 poly 不格式化的檔案(.ini、Makefile……)都算。charset 與 max_line_length 不處理。

專案之外還有一份全域的:~/.config/poly/poly.toml(設了 XDG_CONFIG_HOME 就在它底下; Windows 是 %APPDATA%\poly\poly.toml),命令列與編輯器都讀。它在最底層,專案裡的 poly.toml 逐鍵壓過它,而且它永遠不算專案根目錄。poly lsp 啟動時發現它不存在,就用 poly config export 寫一份完整的;之後只要裡面的值都還是預設值,就跟著新版重寫,改過任何一個值就不再動它。

完整的鍵、可填的值、每個引擎的預設值都寫在 poly.example.toml 裡。那份檔案是 poly config export 產生 的,工具名稱、pin 住的版本、語言清單都直接讀自 binary,所以不會跟你手上這一版 poly 說的不一樣;想拿當下這支 binary 的版本就跑 poly config export > poly.toml, 整份存下來不改任何一行也不會改變 poly 的行為。

從原始碼建置

cargo build --release --manifest-path cli/Cargo.toml   # → cli/target/release/poly
(cd extensions/lsp && pnpm install && pnpm run build) # extension bundle
(cd extensions/lsp && pnpm test)                      # 真 extension host E2E
pip install pyyaml && python tools/grammar-sync.py     # 重新同步文法
python tools/tool-sync.py --check                      # 驗證外部工具 pin(離線)
python tools/tool-sync.py --update                     # 跟上游對一次版本(需網路)

.vscode/settings.json 已把 poly.serverPath 指向 cli/target/release/poly, 所以在本 repo 裡按 F5 就會用剛建好的 binary。

授權

poly 自身的程式碼為 MIT,全文見 LICENSE(一併附在每個 release、兩個 VSIX 與 container image 裡)。內嵌文法與相依套件各自保留上游授權,完整清單見兩份 THIRD-PARTY-NOTICES.md(由同步管線自動產生,含 pinned 版本)。授權允許清單由 tools/grammar-sync.py 與 tools/third-party-notices.py 在 CI 強制執行: permissive 授權加 MPL-2.0,GPL/AGPL/SSPL 與無 permissive 選項的 LGPL 一律擋下。

Setup poly is not certified by GitHub. It is provided by a third-party and is governed by separate terms of service, privacy policy, and support documentation.

About

Install the poly CLI (formatter and linter) and put it on PATH
v0.20.0
Latest

Tags

 (1)

Setup poly is not certified by GitHub. It is provided by a third-party and is governed by separate terms of service, privacy policy, and support documentation.