好用到飛起!VSCode插件DevUIHelper設計開發全攻略(一)

0.png

DevUI是一支兼具設計視角和工程視角的團隊,服務於華爲雲 DevCloud平臺和華爲內部數箇中後臺系統,服務於設計師和前端工程師。
官方網站: devui.design
Ng組件庫: ng-devui(歡迎Star)
官方交流羣:添加DevUI小助手(微信號:devui-official)進羣
DevUIHelper插件:DevUIHelper-LSP(歡迎Star)

1. 開發背景

爲了提高DevUI組件對於使用者的易用性:javascript

  1. 針對DevUI組件庫目前只有插入代碼塊的插件,用戶可在html文件中輸入關鍵字生成代碼塊

例如輸入d-button,vscode會自動聯想,選中後會出現代碼塊:
<d-button (btnClick)=‘’> </d-button>1css

  1. 避免在使用組件的過程當中不停地切換到組件庫網站查詢Demo和API

即提升開發者寫出<d-button type=‘‘primary‘’></d-button>的效率,當寫完後鼠標懸浮到type上時,應該出現type的相關描述.
而類型,默認值與說明,應與網站中API頁面type的描述保持一致.拆分一下能夠有這兩個需求:html

  • 自動補全,輸入<d-這樣的組件標籤,能夠提示API並補全,同時能夠提示參數並補全.
  • 對已有代碼進行相關懸浮提示:用戶懸停到關鍵字上,顯示相關的描述.

目前彷佛許多UI庫尚未實現將api文檔從網站搬到代碼中,咱們或許能夠實現"api文檔賦能頁面".前端

2. 開發概述

2.1. 功能設想

原始版本功能設想以下,魚骨圖靠近魚頭的部分意味着優先級較高,應優先開發。

作着作着伴隨着解bug,新想法又冒出來:依賴查找報警/補全時圖片提示/提供GUI的自定義圖片....java

2.2. 已有成果展現

small

2.3. 怎麼實現的?

本插件站在了前輩基礎上,以及參考了Angular Language Service插件的解析器部分進行開發,總的邏輯是獲取素材+合適工具=功能實現.插件開發細節如函數註冊/package配置等請見前輩文章,很是感謝他們的分享!node

2.3.1. 各版本思路

  • 1.0版本思路:經過字符串匹配查找導出的素材模塊,在合適的時候調用對應的api完成功能
  • 2.0版本思路:面向對象封裝功能與參數,以便後續擴展,並集成素材模塊,重構其形式,爲3.0資源模塊對象化作鋪墊
  • 3.0版本思路:引入Server端的LSP,對標籤句通過詞法/語法/語義分析生成AST代替本來的正則匹配,並將本來的Local API替換爲Server API
  • 4.0版本思路:面向接口編程進一步下降各模塊耦合度,優化parser,嘗試新增對指令的全功能支持以及自定義命令實現快速開發等

題外話:工具怎麼找到呢?事先學到了是一個, 讀文檔看API是一個,讀源碼溯源是一個.git

2.3.2. 目前總架構

目前的架構以下圖所示:
github

  1. 控制層 :以server.ts爲入口,經過LSP提供的TCP鏈接創建與語言服務器的鏈接以便HOST統籌調用功能API,同時激活(初始化)Parser與資源模塊,一個依據源代碼生成抽象語法樹,一個依據根據API文檔對應生成的資源文件(.json等格式)生成資源樹。
  2. 模型層:以後Hunter節點樹與資源樹之間架起一座橋樑,一方面從抽象語法樹中獲得節點信息後去資源樹中查詢對應資源,一方面也採用緩存的辦法提升下一次的查詢效率。
  3. 業務層:獲得的節點信息如Position和對應資源(可能還須要處理資源)做爲參數傳入LSP提供的功能API,進而實現對應功能。

整體上架構未離開獲取素材+合適工具=功能實現的總邏輯,不過值得注意Hunter類的存在解決了節點資源樹一同刷新的問題,也賦予了二者各自更多的可能性。這也正是高內聚低耦合的好處。正則表達式

3. 功能開發

3.1. 獲取素材

爲供api調用,預計網絡傳輸開銷大於本地預處理,所以素材可express

  • 爬蟲結果處理後獲得素材
  • 素材文檔在本地由Java處理後導出成module模塊
  • node_modules中模塊中已集成

素材示例:

export const HTML_SCHEMA=[
"accordion||這是一個accordion組件",
"data||Array<any>或AccordionMenuType||null||數據源||必選,數據源,能夠自定義數組或者使用預設的AccordionMenuType||true||false||[]",
"titleKey||string||title||標題的屬性名||可選,標題的屬性名,item[titleKey]類型爲string,爲標題顯示內容||false||false||[]",
"loadingKey||string||loading||子菜單是否加載中的判斷屬性名||可選,子菜單是否加載中的判斷屬性名,item[loadingKey]類型爲boolean||false||false||[]",
"childrenKey||string||children||子菜單的屬性名||可選,子菜單的屬性名,item[childrenKey]類型爲Array<any>||false||false||[]"
]

若是api文檔不夠規範影響素材處理,可與組件團隊協調解決或者建議團隊採用Angular doc這樣的業界規範。而爲了解決版本化的問題,最好的方案則是將素材模塊集成入對應組件庫的node_modules中,也能夠考慮產品自帶模塊處理腳本。

3.2. 使用工具

3.2.1. 正則表達式

正則表達式(regular expression)描述了一種字符串匹配的模式(pattern),能夠用來檢查一個串是否含有某種子串、將匹配的子串替換或者從某個串中取出符合某個條件的子串等。
對於組件的特徵,如

  • DevUI的組件的d-buttond-,Antdesign的Button大寫的B

以及引入的模塊

  • import { DevUIModule } from 'ng-devui'

開發過程當中的操做

  • 敲空格,[](屬性),()(事件)等

均可以做爲正則匹配條件來實現條件限制或觸發,代碼示例:

//devui的使用以d-開頭,如d-button,以此作觸發
const componentRegex = /<(d-[a-zA-Z0-9-]*)\b[^<>]***/g;
// 匹配"",而不是""和空格或者""和>,以此作""不觸發而""外可觸發的條件限制
const attributeValue= /^=\"[\s\S]*\"(?! |(>)\1)/;

3.2.2. 調用API

經過看vscode本身的API文檔2,能夠打開實現各類功能的新世界。有可能得耐心看,能夠逐層瞭解接口與返回值,以registerHoverProvider爲例:

3.2.3. Language Server Protocol

3.2.3.1. 什麼是LSP?

微軟推出LSP,意圖標準化語言工具和代碼編輯器之間的通訊:
2
其實在Angular框架和Jdk中都自帶了LSP的實現.在VS Code中,語言服務器包含兩個部分:

  • 語言客戶端:用JavaScript / TypeScript編寫的普通VS Code擴展。該擴展能夠訪問全部VS Code命名空間API。
  • 語言服務器:在單獨的進程中運行的語言分析工具。

依託於語言或語言工具開發者提供的素材庫/符合規範的接口,只要C/S之間創建起可靠的鏈接(如TCP),Language Server就能夠執行靜態程序分析爲指定特徵代碼(如組件庫代碼)或所有代碼創建抽象語法樹,來獲得LSP所提供API的接口所需的參數,代碼編輯器即可以實現Client端包括自動補全、懸浮提示和自動糾錯在內的許多功能.

3.2.3.2. 爲何利用AST?

抽象語法樹是程序源代碼結構的樹狀表示。程序源代碼通過詞法分析器(Lexer)獲得各類不一樣種類的單詞(Token),再由語法分析器(Parser3)分析和語法檢查後獲得抽象語法樹(AST)。對於以下C語言代碼:

while(i < n){  
    sum += A[i++];
}

能夠生成如圖的AST結構:
AST2
一般AST的根節點表示整個程序,內部節點是抽象語法結構或者單詞。AST的核心在於它能與輸入源代碼中的各個語法元素一一對應。正好事實上,LSP的大部分請求都是在表達"在指定位置執行規定動做"---這意味着插件開發者只須要考慮什麼時候觸發以及何處觸發指定操做便可,所以常見的參數即

  • 文件的URI
  • 文件的change.document
  • 單詞的position

語法樹即可以準確地提供Token(包括單詞)的position.這正是經過正則表達式的匹配沒法達到的優勢。

固然,當深刻語言的編譯原理時,其實也能夠建立本身的語言定義了,想象空間十分大.

3.2.3.3. 引入Server端的LSP4

當在server文件夾的package.json中引入vscode-languageserver:

"dependencies": {
    "vscode-languageserver": "^4.1.3"
}

以後開發者就可使用server的API了.

3.2.3.4. LSP類插件基本結構
.
├── client // Language Client: able to use vscode API
│   ├── src
│   │   ├── test // End to End tests for Language Client / Server
│   │   └── extension.ts // Language Client entry point
├── package.json // The extension manifest
└── server // Language Server
    └── src
        └── server.ts // Language Server entry point

3.2.4. 組件庫的語法樹

如前對AST的介紹,html/css/ts/.d.ts的文件均可以解析成語法樹。以html文件爲例,能夠將每一級標籤做爲一個DOM節點,組件語句則通過詞法/語法/語義分析打碎成token
好比對<d-button bsStyle="primary"></button>分割成

  • 標籤(如d-button)
  • api(如style)
  • 屬性(如primary)
  • 符號(如=""),

然後對每一分割的元素周圍劃定識別的範圍(keySpan),壓棧後經序列化最終生成AST語法樹。如對下列使用了DevUI組件庫的代碼:

<div class="main">
  <div class="left">
    <d-accordion 
        [data]="menu" 
        class="menu" 
        [restrictOneOpen]="restrictOneOpen" 
        (itemClick)="itemClick(**event)"
        (menuToggle)="menuToggle(**event)">
    hello
    </d-accordion>
  </div>
  <div class="content">
    <app-table></app-table>
  </div>
</div>

本插件生成的AST可部分表示爲:
AST1

不過必須 注意,相似的工做已經有 posthtml這樣的工具作了 5,而且若是要解析 markdown之類也有對應的庫,本身在開發以前"不要重複造輪子」.

每個組件標籤最後能夠實現一棵單樹,在這樣的森林裏最後能夠對光標周圍的某個節點(position)讀取後對其父親和孩子查找來實現節點樹於資源樹節點的匹配,理論上還能夠autofix或者依賴檢查的功能.
這樣的森林能夠經過初始化分析創建Snapshot來保存,當局部樹改變時,做爲dirty data處理局部刷新快照.在早期(指2.0版本)的時候,能夠借鑑這個思路作成Map,也即認爲是單節點的樹來查詢.

3.2.4.1. 幾個問題
  • 與Angular或者其餘工具的AST生成有何不一樣?

答:主要在於分析的範圍。以Angular爲例,它是全文的,而且依賴於ts的解析器;而分析組件庫就只有devui的部分:d-button -- bsStyle -- primary這樣的三級.

  • 能夠複用其餘工具的AST嗎?

答:理論上能夠,可是要實現如Angular的AST對本身的DevUI很差擴展,且顯得臃腫.

  • 能夠在本地調用markdown的解析包,或者本地實現markdown的解析展現嗎?

答:有準備嘗試!

  • 能夠適配其餘組件庫嗎?

答:能夠!獲取素材+合適工具=功能實現是通用的,而對於某些特殊狀況,除了單獨建樹也能夠採用相似於限定某種功能僅當使用組件的同時使用[]綁定變量或者()綁定函數時被激活的操做,就像Angular對基於它自身的devui組件作的。事實上,業界Rome更進一步,但願把基於AST的全部功能都統一塊兒來,不要每一個工具本身作一次AST解析。

4. 代碼與性能優化

  1. 面向接口編程,以及包括將重複使用的模塊如getName()導出成模塊解耦
  2. 考慮插件沒必要要時沒必要激活

    • 是否引入了對應依賴,如@angualr
    • 是否寫了標誌性的符號,如<d-
    • 是否時對應的文件,htmlts

值得注意的是,在早期這個或許只能考慮讀取本地文件6使用正則表達式檢測app.module.ts中是否引入了devui的依賴從而避免無效啓動插件浪費資源;當升級了工具--使用AST來獲取元素Token時,問題就迎刃而解了.

  1. 其餘設計模式

5. 上線接受檢驗!

使用vsce打包發佈到vscode插件市場,打補丁更新的時候vsce publish patch便可.注意可經過.vscodeignore省略testout等部分.

目前7DevUIHelper4.0版本已在VSCode插件市場上線,歡迎star,歡迎issue & pr,歡迎使用! :-)

加入咱們

咱們是DevUI團隊,歡迎來這裏和咱們一塊兒打造優雅高效的人機設計/研發體系。招聘郵箱:muyang2@huawei.com。


做者: 幕賓

責編: DevUI團隊

《好用到飛起!VSCode插件DevUIHelper設計開發全攻略(二)》即將出爐,敬請期待~

往期文章推薦

《使用Git,10件你可能須要「反悔」的事》
《Web界面深色模式和主題化開發》
《手把手教你搭建一個灰度發佈環境》


  1. Snippets in VSCode
  2. Programmatic Language Features
  3. parser除了轉化程序文本成AST這樣的數據結構,也有用於處理 CSV,JSON,XML 之類的格式。
  4. Language Server Extension Guide
  5. 可是posthtml轉成的節點樹對於位置信息不敏感,即上述的keySpan.所以本項目選擇了重寫htmlparser,後續可能會考慮在規範的posthtml基礎上添加節點的開始與結束信息.
  6. 基於Electron開發的vscode可使用nodejs隨意讀寫本地文件、跨域請求、甚至建立一個本地server.
  7. 2.0版本及以前可見這個連接
相關文章
相關標籤/搜索