🚀 VS Code でを入手しましょう!

デバッガー拡張機能

Visual Studio Code のデバッグアーキテクチャにより、拡張機能の作成者は、既存のデバッガーを VS Code に簡単に統合でき、すべてのデバッガーで共通のユーザーインターフェイスを使用できます。

VS Code には、多くのデバッガー機能を紹介する優れた例である、組み込みのデバッガー拡張機能として、Node.js デバッガー拡張機能が同梱されています。

VS Code Debug Features

このスクリーンショットは、次のデバッグ機能を示しています。

  1. デバッグ構成の管理。
  2. 開始/停止およびステップ実行のためのデバッグアクション。
  3. ソース、関数、条件、インラインブレークポイント、およびログポイント。
  4. マルチスレッドおよびマルチプロセスサポートを含む、スタックトレース。
  5. ビューとホバーでの複雑なデータ構造のナビゲーション。
  6. ホバーまたはソースにインラインで表示される変数値。
  7. ウォッチ式の管理。
  8. オートコンプリートによるインタラクティブな評価のためのデバッグコンソール。

このドキュメントは、任意のデバッガーを VS Code で動作させるデバッガー拡張機能を作成するのに役立ちます。

VS Code のデバッグアーキテクチャ

VS Code は、デバッガーバックエンドと通信するために導入した抽象プロトコルに基づいて、汎用的な (言語に依存しない) デバッガー UI を実装しています。通常、デバッガーはこのプロトコルを実装していないため、デバッガーをプロトコルに「適合」させるための中間処理が必要です。この仲介役は通常、デバッガーと通信するスタンドアロンプロセスです。

VS Code Debug Architecture

この仲介役をデバッグアダプター (略して DA) と呼び、DA と VS Code の間で使われる抽象プロトコルをデバッグアダプタープロトコル (略して DAP) と呼びます。デバッグアダプタープロトコルは VS Code から独立しているため、独自の ウェブサイト があり、概要、詳細な 仕様、および 既知の実装とサポートツール のリストがあります。DAP の歴史と動機については、この ブログ記事 で説明されています。

デバッグアダプターは VS Code から独立しており、他の開発ツール で使用できるため、拡張機能と機能拡張ポイントに基づく VS Code の拡張機能アーキテクチャには適合しません。

このため、VS Code は機能拡張ポイント debuggers を提供しており、デバッグアダプターは特定のデバッグタイプ (例: Node.js デバッガーの場合は node) で機能拡張できます。VS Code は、ユーザーがそのタイプのデバッグセッションを開始すると、登録された DA を起動します。

したがって、最もシンプルな形式では、デバッガー拡張機能は、デバッグアダプターの実装を宣言的に機能拡張するだけであり、拡張機能は基本的に、追加のコードなしでデバッグアダプターのパッケージコンテナです。

VS Code Debug Architecture 2

より現実的なデバッガー拡張機能は、次の宣言項目またはすべての項目を VS Code に機能拡張します。

  • デバッガーでサポートされている言語のリスト。VS Code は、これらの言語のブレークポイントを設定するための UI を有効にします。
  • デバッガーによって導入されたデバッグ構成属性の JSON スキーマ。VS Code はこのスキーマを使用して、launch.json エディターで構成を検証し、IntelliSense を提供します。JSON スキーマの構造 $refdefinition はサポートされていないことに注意してください。
  • VS Code によって作成された最初の launch.json のデフォルトのデバッグ構成。
  • ユーザーが launch.json ファイルに追加できるデバッグ構成スニペット。
  • デバッグ構成で使用できる変数の宣言。

詳細については、contributes.breakpoints および contributes.debuggers のリファレンスを参照してください。

上記の純粋に宣言的な機能拡張に加えて、Debug Extension API は次のコードベースの機能を有効にします。

  • VS Code によって作成された最初の launch.json の動的に生成されたデフォルトのデバッグ構成。
  • 動的に使用するデバッグアダプターを決定します。
  • デバッグ構成がデバッグアダプターに渡される前に、検証または変更します。
  • デバッグアダプターと通信します。
  • デバッグコンソールにメッセージを送信します。

このドキュメントの残りの部分では、デバッガー拡張機能を開発する方法を示します。

Mock Debug 拡張機能

デバッグアダプターをゼロから作成するのはこのチュートリアルでは少し大変なので、教育用の「デバッグアダプタースターターキット」として作成したシンプルな DA から始めます。これは、実際のデバッガーとは通信せず、モックであるため、Mock Debug と呼ばれています。Mock Debug はデバッガーをシミュレートし、ステップ、続行、ブレークポイント、例外、および変数アクセスをサポートしますが、実際のデバッガーには接続されていません。

mock-debug の開発セットアップを詳しく調べる前に、まず VS Code Marketplace から プレビルドバージョン をインストールして試してみましょう。

  • 拡張機能ビューレットに切り替えて、「mock」と入力して Mock Debug 拡張機能を検索します。
  • 拡張機能を「インストール」して「リロード」します。

Mock Debug を試すには

  • 新しい空のフォルダー mock test を作成し、VS Code で開きます。
  • ファイル readme.md を作成し、任意のテキストを数行入力します。
  • 実行とデバッグビュー (⇧⌘D (Windows、Linux Ctrl+Shift+D)) に切り替え、launch.json ファイルを作成する リンクを選択します。
  • VS Code では、デフォルトの起動構成を作成するために「デバッガー」を選択できます。「Mock Debug」を選択します。
  • 緑色の 開始 ボタンを押してから、Enter キーを押して、提案されたファイル readme.md を確認します。

デバッグセッションが開始され、readme ファイルを「ステップ実行」したり、ブレークポイントを設定してヒットさせたり、例外 (行に単語 exception が表示されている場合) を実行したりできます。

Mock Debugger running

Mock Debug を独自の開発の出発点として使用する前に、まずプレビルドバージョンをアンインストールすることをお勧めします。

  • 拡張機能ビューレットに切り替え、Mock Debug 拡張機能のギアアイコンをクリックします。
  • 「アンインストール」アクションを実行し、「ウィンドウをリロード」します。

Mock Debug の開発セットアップ

次に、Mock Debug のソースを入手し、VS Code 内で開発を開始しましょう。

git clone https://github.com/microsoft/vscode-mock-debug.git
cd vscode-mock-debug
yarn

プロジェクトフォルダー vscode-mock-debug を VS Code で開きます。

パッケージの内容は?

  • package.json は mock-debug 拡張機能のマニフェストです。
    • mock-debug 拡張機能の機能拡張をリストします。
    • compile および watch スクリプトは、TypeScript ソースを out フォルダーにトランスパイルし、後続のソース変更を監視するために使用されます。
    • 依存関係 vscode-debugprotocolvscode-debugadapter、および vscode-debugadapter-testsupport は、ノードベースのデバッグアダプターの開発を簡素化する NPM モジュールです。
  • src/mockRuntime.ts は、シンプルなデバッグ API を備えたモックランタイムです。
  • ランタイムをデバッグアダプタープロトコルに適合させるコードは、src/mockDebug.ts にあります。ここでは、DAP のさまざまな要求のハンドラーを見つけることができます。
  • デバッガー拡張機能の実装はデバッグアダプターにあるため、拡張機能コード (つまり、拡張機能ホストプロセスで実行されるコード) を持つ必要はありません。ただし、Mock Debug には小さな src/extension.ts があります。これは、デバッガー拡張機能の拡張機能コードで何ができるかを示しているためです。

次に、Extension 起動構成を選択して F5 キーを押して、Mock Debug 拡張機能をビルドして起動します。最初は、TypeScript ソースを out フォルダーに完全にトランスパイルします。フルビルド後、ウォッチャータスクが開始され、行った変更がトランスパイルされます。

ソースをトランスパイルした後、Mock Debug 拡張機能がデバッグモードで実行されている新しい VS Code ウィンドウ "[Extension Development Host]" が表示されます。そのウィンドウから、readme.md ファイルを含む mock test プロジェクトを開き、「F5」でデバッグセッションを開始して、ステップ実行します。

Debugging Extension and Server

拡張機能をデバッグモードで実行しているため、src/extension.ts にブレークポイントを設定してヒットさせることができますが、前述したように、拡張機能で実行されている興味深いコードはあまりありません。興味深いコードは、別のプロセスであるデバッグアダプターで実行されます。

デバッグアダプター自体をデバッグするには、デバッグモードで実行する必要があります。これは、デバッグアダプターをサーバーモードで実行し、VS Code がそれに接続するように構成することで最も簡単に実現できます。VS Code vscode-mock-debug プロジェクトで、ドロップダウンメニューから Server 起動構成を選択し、緑色の開始ボタンを押します。

拡張機能のアクティブなデバッグセッションがすでにあったため、VS Code デバッガー UI はマルチセッションモードになります。これは、CALL STACK ビューに ExtensionServer の 2 つのデバッグセッションの名前が表示されることで示されます。

Debugging Extension and Server

これで、拡張機能と DA の両方を同時にデバッグできます。ここに至るより速い方法は、両方のセッションを自動的に起動する Extension + Server 起動構成を使用することです。

拡張機能と DA をデバッグするための別の、さらに簡単なアプローチは、以下 にあります。

ファイル src/mockDebug.ts のメソッド launchRequest(...) の先頭にブレークポイントを設定し、最後のステップとして、モックデバッガーが DA サーバーに接続するように、ポート 4711debugServer 属性をモックテストの起動構成に追加します。

{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "mock",
      "request": "launch",
      "name": "mock test",
      "program": "${workspaceFolder}/readme.md",
      "stopOnEntry": true,
      "debugServer": 4711
    }
  ]
}

これでこのデバッグ構成を起動すると、VS Code はモックデバッグアダプターを別のプロセスとして起動せず、すでに実行中のサーバーのローカルポート 4711 に直接接続し、launchRequest のブレークポイントにヒットするはずです。

このセットアップにより、Mock Debug を簡単に編集、トランスパイル、およびデバッグできます。

しかし、ここからが本当の作業の始まりです。src/mockDebug.ts および src/mockRuntime.ts のデバッグアダプターのモック実装を、「実際の」デバッガーまたはランタイムと通信するコードに置き換える必要があります。これには、デバッグアダプタープロトコルを理解して実装することが含まれます。これに関する詳細については、こちら を参照してください。

デバッガー拡張機能の package.json の構造

デバッガー固有のデバッグアダプターの実装を提供することに加えて、デバッガー拡張機能には、さまざまなデバッグ関連の機能拡張ポイントに機能拡張する package.json が必要です。

Mock Debug の package.json を詳しく見てみましょう。

すべての VS Code 拡張機能と同様に、package.json は拡張機能の基本的なプロパティである namepublisher、および version を宣言します。categories フィールドを使用して、VS Code 拡張機能マーケットプレイスで拡張機能を見つけやすくします。

{
  "name": "mock-debug",
  "displayName": "Mock Debug",
  "version": "0.24.0",
  "publisher": "...",
  "description": "Starter extension for developing debug adapters for VS Code.",
  "author": {
    "name": "...",
    "email": "..."
  },
  "engines": {
    "vscode": "^1.17.0",
    "node": "^7.9.0"
  },
  "icon": "images/mock-debug-icon.png",
  "categories": ["Debuggers"],

  "contributes": {
    "breakpoints": [{ "language": "markdown" }],
    "debuggers": [
      {
        "type": "mock",
        "label": "Mock Debug",

        "program": "./out/mockDebug.js",
        "runtime": "node",

        "configurationAttributes": {
          "launch": {
            "required": ["program"],
            "properties": {
              "program": {
                "type": "string",
                "description": "Absolute path to a text file.",
                "default": "${workspaceFolder}/${command:AskForProgramName}"
              },
              "stopOnEntry": {
                "type": "boolean",
                "description": "Automatically stop after launch.",
                "default": true
              }
            }
          }
        },

        "initialConfigurations": [
          {
            "type": "mock",
            "request": "launch",
            "name": "Ask for file name",
            "program": "${workspaceFolder}/${command:AskForProgramName}",
            "stopOnEntry": true
          }
        ],

        "configurationSnippets": [
          {
            "label": "Mock Debug: Launch",
            "description": "A new configuration for launching a mock debug program",
            "body": {
              "type": "mock",
              "request": "launch",
              "name": "${2:Launch Program}",
              "program": "^\"\\${workspaceFolder}/${1:Program}\""
            }
          }
        ],

        "variables": {
          "AskForProgramName": "extension.mock-debug.getProgramName"
        }
      }
    ]
  },

  "activationEvents": ["onDebug", "onCommand:extension.mock-debug.getProgramName"]
}

次に、デバッグ拡張機能に固有の機能拡張が含まれている contributes セクションを見てください。

まず、breakpoints 機能拡張ポイントを使用して、ブレークポイントの設定が有効になる言語をリストします。これがないと、Markdown ファイルにブレークポイントを設定することはできません。

次は、debuggers セクションです。ここでは、1 つのデバッガーがデバッグ type mock で導入されています。ユーザーは起動構成でこのタイプを参照できます。オプションの属性 label を使用して、UI に表示するときにデバッグタイプにわかりやすい名前を付けることができます。

デバッグ拡張機能はデバッグアダプターを使用するため、そのコードへの相対パスが program 属性として指定されています。拡張機能を自己完結型にするために、アプリケーションは拡張機能フォルダー内に存在する必要があります。慣例により、このアプリケーションは out または bin という名前のフォルダー内に保持しますが、別の名前を自由に使用できます。

VS Code はさまざまなプラットフォームで実行されるため、DA プログラムがさまざまなプラットフォームもサポートしていることを確認する必要があります。このために、次のオプションがあります。

  1. プログラムがプラットフォームに依存しない方法で実装されている場合 (例: サポートされているすべてのプラットフォームで利用可能なランタイムで実行されるプログラムとして)、runtime 属性を介してこのランタイムを指定できます。現在、VS Code は node および mono ランタイムをサポートしています。上記の Mock デバッグアダプターはこのアプローチを使用しています。

  2. DA 実装でプラットフォームごとに異なる実行可能ファイルが必要な場合は、program 属性を次のように特定のプラットフォームで修飾できます。

    "debuggers": [{
        "type": "gdb",
        "windows": {
            "program": "./bin/gdbDebug.exe",
        },
        "osx": {
            "program": "./bin/gdbDebug.sh",
        },
        "linux": {
            "program": "./bin/gdbDebug.sh",
        }
    }]
    
  3. 両方のアプローチの組み合わせも可能です。次の例は、macOS および Linux ではランタイムが必要ですが、Windows では必要ない Mono アプリケーションとして実装されている Mono DA の例です。

    "debuggers": [{
        "type": "mono",
        "program": "./bin/monoDebug.exe",
        "osx": {
            "runtime": "mono"
        },
        "linux": {
            "runtime": "mono"
        }
    }]
    

configurationAttributes は、このデバッガーで使用可能な launch.json 属性のスキーマを宣言します。このスキーマは、launch.json を検証し、起動構成を編集するときに IntelliSense とホバーヘルプをサポートするために使用されます。

initialConfigurations は、このデバッガーのデフォルトの launch.json の初期コンテンツを定義します。この情報は、プロジェクトに launch.json がなく、ユーザーがデバッグセッションを開始するか、実行とデバッグビューで launch.json ファイルを作成する リンクを選択した場合に使用されます。この場合、VS Code はユーザーにデバッグ環境を選択させ、対応する launch.json を作成します。

Debugger Quickpick

package.jsonlaunch.json の初期コンテンツを静的に定義する代わりに、DebugConfigurationProvider を実装することで、初期構成を動的に計算できます (詳細については、以下の DebugConfigurationProvider の使用 セクションを参照してください)。

configurationSnippets は、launch.json を編集するときに IntelliSense に表示される起動構成スニペットを定義します。慣例として、スニペットの label 属性にデバッグ環境名をプレフィックスとして付けることで、多数のスニペット候補のリストに表示されたときに明確に識別できるようにします。

variables 機能拡張は、「変数」を「コマンド」にバインドします。これらの変数は、起動構成で ${command:xyz} 構文を使用して使用でき、デバッグセッションが開始されると、変数はバインドされたコマンドから返された値に置き換えられます。

コマンドの実装は拡張機能にあり、UI なしの単純な式から、拡張機能 API で利用可能な UI 機能に基づく洗練された機能まで、多岐にわたります。Mock Debug は、変数 AskForProgramName をコマンド extension.mock-debug.getProgramName にバインドします。実装 (src/extension.ts) は、showInputBox を使用して、ユーザーにプログラム名を入力させます。

vscode.commands.registerCommand('extension.mock-debug.getProgramName', config => {
  return vscode.window.showInputBox({
    placeHolder: 'Please enter the name of a markdown file in the workspace folder',
    value: 'readme.md'
  });
});

これで、変数を起動構成の任意の文字列型値で ${command:AskForProgramName} として使用できるようになりました。

DebugConfigurationProvider の使用

package.json でのデバッグ機能拡張の静的な性質が不十分な場合、DebugConfigurationProvider を使用して、デバッグ拡張機能の次の側面を動的に制御できます。

  • 新しく作成された launch.json の初期デバッグ構成は、ワークスペースで利用可能なコンテキスト情報などに基づいて動的に生成できます。
  • 起動構成は、新しいデバッグセッションを開始するために使用される前に解決 (または変更) できます。これにより、ワークスペースで利用可能な情報に基づいてデフォルト値を入力できます。2 つの解決メソッドが存在します。resolveDebugConfiguration は、起動構成で変数が置換される前に呼び出されます。resolveDebugConfigurationWithSubstitutedVariables は、すべての変数が置換された後に呼び出されます。検証ロジックが追加の変数をデバッグ構成に挿入する場合は、前者を使用する必要があります。検証ロジックがすべてのデバッグ構成属性の最終的な値にアクセスする必要がある場合は、後者を使用する必要があります。

src/extension.tsMockConfigurationProvider は、launch.json が存在しない場合にデバッグセッションが開始されたが、Markdown ファイルがアクティブなエディターで開かれている場合を検出するために、resolveDebugConfiguration を実装します。これは、ユーザーがエディターでファイルを開いており、launch.json を作成せずにデバッグしたい場合の典型的なシナリオです。

デバッグ構成プロバイダーは、通常、拡張機能の activate 関数で、vscode.debug.registerDebugConfigurationProvider を介して特定のデバッグタイプに登録されます。DebugConfigurationProvider が十分に早く登録されるようにするには、デバッグ機能が使用されるとすぐに拡張機能をアクティブ化する必要があります。これは、package.jsononDebug イベントの拡張機能のアクティベーションを構成することで簡単に実現できます。

"activationEvents": [
    "onDebug",
    // ...
],

この包括的な onDebug は、デバッグ機能が使用されるとすぐにトリガーされます。これは、拡張機能の起動コストが安い場合 (つまり、起動シーケンスに多くの時間を費やさない場合) は問題ありません。デバッグ拡張機能の起動にコストがかかる場合 (たとえば、言語サーバーを起動するため)、onDebug アクティベーションイベントは、他のデバッグ拡張機能に悪影響を与える可能性があります。これは、イベントがかなり早くトリガーされ、特定のデバッグタイプを考慮しないためです。

コストのかかるデバッグ拡張機能のより良いアプローチは、よりきめ細かいアクティベーションイベントを使用することです。

  • onDebugInitialConfigurations は、DebugConfigurationProviderprovideDebugConfigurations メソッドが呼び出される直前に発生します。
  • onDebugResolve:type は、指定されたタイプの DebugConfigurationProviderresolveDebugConfiguration または resolveDebugConfigurationWithSubstitutedVariables メソッドが呼び出される直前に発生します。

経験則: デバッグ拡張機能のアクティベーションが安価な場合は、onDebug を使用します。高価な場合は、DebugConfigurationProvider が対応するメソッド provideDebugConfigurations および/または resolveDebugConfiguration を実装しているかどうかに応じて、onDebugInitialConfigurations および/または onDebugResolve を使用します。

デバッガー拡張機能の公開

デバッガー拡張機能を作成したら、マーケットプレイスに公開できます。

  • デバッガー拡張機能の命名と目的を反映するように package.json の属性を更新します。
  • 拡張機能の公開 で説明されているように、マーケットプレイスにアップロードします。

デバッガー拡張機能を開発する別の方法

見てきたように、デバッガー拡張機能の開発には通常、拡張機能とデバッグアダプターの両方を 2 つの並列セッションでデバッグすることが含まれます。上記のように、VS Code はこれをうまくサポートしていますが、拡張機能とデバッグアダプターの両方が 1 つのプログラムであり、1 つのデバッグセッションでデバッグできる場合は、開発がより簡単になる可能性があります。

このアプローチは、デバッグアダプターが TypeScript/JavaScript で実装されている限り、実際には簡単に実行できます。基本的なアイデアは、デバッグアダプターを拡張機能内で直接実行し、セッションごとに新しい外部デバッグアダプターを起動する代わりに、VS Code がそれに接続するようにすることです。

このために、VS Code はデバッグアダプターの作成および実行方法を制御するための拡張機能 API を提供します。DebugAdapterDescriptorFactory には、デバッグセッションが開始され、デバッグアダプターが必要な場合に VS Code によって呼び出されるメソッド createDebugAdapterDescriptor があります。このメソッドは、デバッグアダプターの実行方法を記述する記述子オブジェクト (DebugAdapterDescriptor) を返す必要があります。

現在、VS Code はデバッグアダプターを実行するための 3 つの異なる方法をサポートしており、その結果、3 つの異なる記述子タイプを提供しています。

  • DebugAdapterExecutable: このオブジェクトは、パスとオプションの引数およびランタイムを持つ外部実行可能ファイルとしてデバッグアダプターを記述します。実行可能ファイルはデバッグアダプタープロトコルを実装し、stdin/stdout を介して通信する必要があります。これは VS Code のデフォルトの動作モードであり、DebugAdapterDescriptorFactory が明示的に登録されていない場合、VS Code は package.json からの対応する値でこの記述子を自動的に使用します。
  • DebugAdapterServer: このオブジェクトは、特定のローカルまたはリモートポートを介して通信するサーバーとして実行されているデバッグアダプターを記述します。vscode-debugadapter npm モジュールに基づくデバッグアダプターの実装は、このサーバーモードを自動的にサポートします。
  • DebugAdapterInlineImplementation: このオブジェクトは、vscode.DebugAdapter インターフェイスを実装する JavaScript または Typescript オブジェクトとしてデバッグアダプターを記述します。vscode-debugadapter npm モジュールのバージョン 1.38-pre.4 以降に基づくデバッグアダプターの実装は、インターフェイスを自動的に実装します。

Mock Debug は、3 種類の DebugAdapterDescriptorFactory の例と、それらが 「mock」デバッグタイプに登録されている方法 を示しています。使用する実行モードは、グローバル変数 runMode 可能な値 externalserver、または inline のいずれかに設定することで選択できます。

開発の場合、inline モードと server モードは、単一のプロセス内で拡張機能とデバッグアダプターをデバッグできるため、特に役立ちます。