LoginSignup
11
3

More than 3 years have passed since last update.

VS Code の Elixir Syntax Highlighting を少しカスタマイズ

Last updated at Posted at 2019-06-30

VS Code の Elixir Syntax Highlighting を少しカスタマイズ

記事に関連する範囲で自己紹介すると、

  • 最近 Elixir の勉強をしている
  • ふだん VS Code を使う
  • ノートは Markdown で書くことが多い
  • Croma という Elixir ライブラリを使う機会がある

という感じです。

この記事の大まかな内容は以下の 2 つです:

  • Markdown において Elixir の fenced code block に syntax highlighting したい
  • Croma の defun, defunp, defunpt も syntax highlighting したい

VS Code Extension もつくりました。

Elixir の fenced code block に syntax highlighting

実はこの機能は vscode-elixir にすでに PR (#124) されていますが、まだ merge されていません。待てないので、以下 2 通りの方法を考えてみます。

  • vscode-elixir を fork し、#124 の変更部分を merge し、自分で packaging する。
  • vscode-elixir とは別に extension をつくる。

前者のほうが簡単ですが、後述する Croma に関する部分も考慮して extension をつくってしまうことにしました。

Extension をつくる

公式の Getting started にあるように Yeoman と VS Code Extension Generator を使います。ただし、Getting started では type of extension として New Extension (TypeScript) を選択していますが、ここでは New Language Support を選択します。質問に答えていくと extension のソースを含む directory が生成されます。

Extension の中身については、上で述べた PR #124 や vscode-fenced-code-block-grammar-injection-example がとても参考になります(参考というか、そのまま使えます)。

Croma の syntax highlighting

ここでは Croma.Defun に注目します。このモジュールには型情報を伴う関数を簡潔に定義するためのマクロとして defun, defunp, defunpt があります。やっていることは関数定義なので、defdefp と同じようにハイライトされて欲しい、というのが私の希望です。

vscode-elixir では、ハイライトのルールは syntaxes/elixir.json に書かれています。関数に関する部分を抜き出すと下記のようになります。

    {
      "comment": "Function with parameters",
      "begin": "\\b(defp?|defmacrop?)\\b\\s*([_$a-z][$\\w]*[!?]?)\\s*\\(",
      "beginCaptures": {
        "1": {
          "name": "keyword.control.elixir"
        },
        "2": {
          "name": "entity.name.function.elixir"
        }
      },
      "end": "\\)",
      "patterns": [
        {
          "include": "$self"
        },
        {
          "include": "#function_parameter"
        }
      ]
    },
    {
      "comment": "Function without parameters",
      "match": "\\b(defp?|defmacrop?)\\b\\s*([_$a-z][$\\w]*[!?]?)",
      "captures": {
        "1": {
          "name": "keyword.control.elixir"
        },
        "2": {
          "name": "entity.name.function.elixir"
        }
      }
    },

(defp?|defmacrop?) のところを (defun(p|pt)?) とすればマッチしそうです。

Extension に機能を追加する

先に作成した extension に Elixir-Croma という言語名で syntax 情報を記述します。元々の Elixir との差分は defun, defunp, defunpt だけなので、パターンの大部分は include して使えます。ルールを記述した elixir-croma.json は下記のようになります。

{
  "comment": "Syntax Parser for Elixir using Croma.",
  "fileTypes": [
    "ex",
    "exs"
  ],
  "firstLineMatch": "^#!/.*\\belixir",
  "foldingStartMarker": "(after|else|catch|rescue|\\-\\>|\\{|\\[|do)\\s*$",
  "foldingStopMarker": "^\\s*((\\}|\\]|after|else|catch|rescue)\\s*$|end\\b)",
  "name": "Elixir-Croma",
  "patterns": [
    {
      "include": "source.elixir"
    },
    {
      "comment": "Function (Croma) with parameters",
      "begin": "\\b(defun(p|pt)?)\\b\\s*([_$a-z][$\\w]*[!?]?)\\s*\\(",
      "beginCaptures": {
        "1": {
          "name": "keyword.control.elixir"
        },
        "2": {
          "name": "keyword.control.elixir"
        },
        "3": {
          "name": "entity.name.function.elixir"
        }
      },
      "end": "\\)",
      "patterns": [
        {
          "include": "$self"
        },
        {
          "include": "#function_parameter"
        }
      ]
    },
    {
      "comment": "Function (Croma) without parameters",
      "match": "\\b(defun(p|pt)?)\\b\\s*([_$a-z][$\\w]*[!?]?)",
      "captures": {
        "1": {
          "name": "keyword.control.elixir"
        },
        "2": {
          "name": "keyword.control.elixir"
        },
        "3": {
          "name": "entity.name.function.elixir"
        }
      }
    }
  ],
  "scopeName": "source.elixir-croma"
}

個人的にポイントだと思っているのは以下の 2 点です:

  • "include": "source.elixir" で vscode-elixir のパターンを流用している。
  • (defp?|defmacrop?)(defun(p|pt)?) に変更したため () の数が 1 つ増えた。その結果、正規表現における 3 番目のグループが関数名になるため、"3": {"name": "entity.name.function.elixir"} とする。

このあたりの詳細は公式のここに書いてあります。

見た目はこんな感じです

image.png

おわりに

Language support の VS Code extension は JSON をいじるだけでつくれるのでお手軽です。

追記

vscode-icons を使っている場合、ファイルの言語を Elixir-Croma に設定したときに Elixir のファイルアイコンが表示されなくなってしまいます。settings.json に下記を追加しておく必要があります。このへんの設定を extension settings として簡単に管理できるようにしたいです。。。

    "vsicons.associations.files": [
        {
            "icon": "elixir",
            "languages": [
                {
                    "ids": "elixir-croma",
                    "defaultExtension": "ex"
                },
            ],
            "format": "svg"
        }
    ]

追々記

この記事では Elixir-Croma という新しい言語設定を作成しましたが、言語が変わることによって使えなくなる機能も出てくることが分かりました。結局、既存の Elixir syntax にパターンを追加するほうが良いという結論に至りました。このあたりの詳細は公式のここに書いてあります。

自作の VS Code extension もアップデートしてあります。

11
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
3