🔌

デザイントークンを自動補完するVS Code拡張機能を開発しました

2022/09/10に公開

Ubie Discoveryでプロダクト開発をしている@jimboです。

Ubieでは、デザイン生産基盤の整備の一環としてデザイントークンを開発し、npmに公開しています。開発の経緯などは次の記事をごらんください。
https://zenn.dev/ubie/articles/7a6413af237eae

今回、このデザイントークン用のVS Code拡張機能を開発したのでご紹介します。

リポジトリはこちら。
https://github.com/ubie-oss/design-tokens-for-vscode

主な機能

CSSファイルおよびSCSSファイルの編集時に次のような機能が使えるようになります。

自動補完

colormarginなど、CSSプロパティのあとに -- を入力すると、デザイントークンのCSSカスタムプロパティが候補として表示されます。

自動補完のプレビュー

ホバー時のツールチップ表示

入力済みのデザイントークン(CSSカスタムプロパティ)にカーソルを当てると、その値を確認できます。

Hoverのプレビュー

拡張機能を開発した背景

現在、デザイントークンは症状検索エンジン「ユビー」ユビー病気のQ&Aといったプロダクトに適用されています。

しかし、トークンの数は100を超え、--color-ubie-black-500--size-spacing-mdなどCSSカスタムプロパティの名前とその値をすべて覚えるのは困難です。プロダクト開発ではその都度FigmaやCSSの定義ファイルを確認するという作業が発生していました。また、rem単位で定義されている値をpx単位に変換して確認したいときも一苦労でした。

こういった作業を効率化するために、VS Codeの拡張機能を開発することにしました。

実装の概要

この拡張機能は、Language Server Extension(言語サーバー拡張機能)として実装されています。

通常の拡張機能でも自動補完やホバー時のツールチップ表示を実装できるようですが、Language Serverを使うほうがパフォーマンス面などメリットが大きいとのことで、今回はそちらを採用しました。

Language Server Extensionとは

Language Server Extensionは、次の2つの要素で構成されています。

  • Language Client
  • Language Server

全体構成図
全体構成図

Language ClientはVS Code上で実行される通常の拡張機能です。

一方、Language ServerはVS Code本体とは別のプロセスで実行されます。別プロセスのため、言語解析などの重い処理が必要な場合でもVS Code自体のパフォーマンスを落とすことなく、様々な機能を提供できます。

Language ClientとLanguage ServerはJSONベースのLanguage Server Protocolを通じてコミュニケーションを取ります。(vscode-languageclientvscode-languageserverを使えば、このプロトコルの細かい仕様を把握しなくても実装可能です)

今回実装した拡張機能の大まかな処理の流れは、次のようになります。

  1. Language ClientがLanguage Serverを別プロセスで起動し、待機させる
  2. Language Clientは自動補完やホバー時に表示するべきコンテンツを(カーソル位置や文書全体の情報とともに)Language Serverにリクエストする
  3. Language Serverはリクエストを受け取ると、自動補完の候補リストやツールチップに表示するコンテンツを生成し、Language Clientに返す
  4. それらのコンテンツがVS Code上に表示される

それでは、Language ClientとLanguage Serverの実装を軽くご紹介します。

Language Clientの実装

lsp-sample/clientとほぼ同じコードです。処理の対象がCSSファイルとSCSSファイルなのでここだけ変更しています。

  const clientOptions: LanguageClientOptions = {
    // CSSファイルとSCSSファイルを処理の対象とする
    documentSelector: [
      { scheme: 'file', language: 'css' },
      { scheme: 'file', language: 'scss' },
    ],
  };

Language Serverの実装

まず、初期化の中で、自動補完とホバーをサポートすることをLanguage Clientに伝えています。これによって、Language Clientからは自動補完とホバーのリクエストだけ飛ぶようになります。

connection.onInitialize(() => {
  const result: InitializeResult = {
    capabilities: {
      textDocumentSync: TextDocumentSyncKind.Incremental,
      // 自動補完をサポートすることをLanguage Clientに伝える
      completionProvider: {
        triggerCharacters: ['--'], // 自動補完のトリガーとなる文字
      },
      // ホバーをサポートすることをLanguage Clientに伝える
      hoverProvider: true,
    },
  };
  return result;
});

自動補完

@ubie/design-tokensのJSファイルから自動補完の候補リストを生成しています。ユーザーが入力したCSSプロパティを見て、できるだけ利用可能なトークンだけを返しています。例えば、colorbackgroundborderなど色に関係するCSSプロパティを入力しているときは、color系のトークンのみを返す、といった感じです。

https://github.com/ubie-oss/design-tokens-for-vscode/blob/01efed0b4798d20bcc3669326d9550a2ce285981/src/server.ts#L60-L112

ホバー

カーソルがデザイントークンのCSSカスタムプロパティの上にあるときだけ、その値を返すようにしています。

https://github.com/ubie-oss/design-tokens-for-vscode/blob/4b0ddf465641dc206011bd04997a083d2179c3a3/src/server.ts#L114-L146

参考

実装はLanguage Server Extension Guideを読みながら進めました。また、次のコードが大変参考になりました。

開発してみた感想

Language Serverの仕組みと拡張機能のデバッグ方法を掴むのに少し学習コストが必要だったものの、全体の流れがわかってしまうと、今回のような機能実装はそれほど難しくありませんでした。ちょっとした社内向けの拡張機能であればわりと簡単に実装できそうなので、みなさんもぜひ試してみてください。

採用宣伝

Ubieではさまざまな職種のエンジニア・デザイナーを募集しています。まずはカジュアルにお話しましょう!

https://recruit.ubie.life/engineer
https://recruit.ubie.life/designer

Ubie テックブログ

Discussion