1. sbt サーバー

sbt サーバー 

sbt サーバーは sbt 1.x で新たに導入された機能であり、まだ開発中です。最初はサーバーがリモートサーバーで動作して素晴らしいことをするのを想像するかもしれませんが、今のところ sbt サーバーはそうではありません。

実際には、sbt サーバーは sbt のシェルコマンドへのネットワークアクセスを追加するだけであり、端末からの入力を受け付けることに加えて、サーバーはネットワークからの入力も受け付けます。これにより、複数のクライアントが sbt の*単一セッション*に接続できます。クライアントとして想定している主なユースケースは、エディターや IDE などのツール統合です。「IDE 統合」のページをご覧ください。

構成 

サーバーの構成に使用できる設定がいくつかあります。以下に、これらの設定とそのデフォルト値をいくつか示します。設定は、プロジェクトごとまたは~/.sbt/1.0/global.sbtに値を設定することで変更できます。

// If set to a defined value, sbt server will exit if it goes at least the
// specified duration without receiving any commands.
Global / serverIdleTimeout := Some(new FiniteDuration(5, TimeUnit.MINUTES))

Language Server Protocol 3.0 

使用するワイヤプロトコルは、Language Server Protocol 3.0 (LSP) であり、これはJSON-RPCに基づいています。

基本プロトコルは、ヘッダーとコンテンツ部分で構成されています (HTTP と同等)。ヘッダーとコンテンツ部分は\r\nで区切られています。

現在、次のヘッダーフィールドがサポートされています

  • Content-Length: コンテンツ部分の長さ (バイト単位)。このヘッダーを指定しない場合、行末まで読み込まれます。
  • Content-Type: application/vscode-jsonrpc; charset=utf-8に設定するか、省略する必要があります。

以下に例を示します

Content-Type: application/vscode-jsonrpc; charset=utf-8\r\n
Content-Length: ...\r\n
\r\n
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "textDocument/didSave",
  "params": {
    ...
  }
}

JSON-RPC リクエストは、id番号、method名、およびオプションのparamsオブジェクトで構成されます。したがって、すべての LSP リクエストは、メソッド名とparams JSON のペアです。

JSON-RPC リクエストに対する応答の例は次のとおりです

Content-Type: application/vscode-jsonrpc; charset=utf-8\r\n
Content-Length: ...\r\n
\r\n
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    ...
  }
}

または、サーバーがエラー応答を返す場合があります

Content-Type: application/vscode-jsonrpc; charset=utf-8\r\n
Content-Length: ...\r\n
\r\n
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "some error message"
  }
}

応答に加えて、サーバーはイベントも送信する可能性があります (LSP 用語では「通知」)。

Content-Type: application/vscode-jsonrpc; charset=utf-8\r\n
Content-Length: ...\r\n
\r\n
{
  "jsonrpc": "2.0",
  "method": "textDocument/publishDiagnostics",
  "params": {
    ...
  }
}

サーバーモード 

sbt サーバーは、ワイヤプロトコルと初期化が異なる 2 つのモードで実行できます。sbt 1.1.x 以降のデフォルトモードは*ドメインソケットモード*であり、サーバーとクライアント間のデータ転送に Unix ドメインソケット (Unix の場合) または名前付きパイプ (Windows の場合) を使用します。さらに、データ転送に TCP を使用する*TCP モード*があります。

sbt サーバーが起動するモードは、serverConnectionTypeキーによって制御されます。これは、ドメインソケット/名前付きパイプモードの場合はConnectionType.Local、TCP モードの場合はConnectionType.Tcpに設定できます。

サーバーの検出と認証 

実行中のサーバーを検出するには、*ポートファイル*を使用します。

デフォルトでは、sbt シェルセッションがアクティブな場合、sbt サーバーが実行されます。サーバーが起動すると、ポートファイルと呼ばれるファイルが作成されます。ポートファイルは./project/target/active.jsonにあります。ポートファイルは、サーバーが TCP モードで実行されているか、ドメインソケット/名前付きパイプモードで実行されているかによって異なります。次のようなものになります。

Unix でのドメインソケット/名前付きパイプモードの場合

{"uri":"local:///Users/someone/.sbt/1.0/server/0845deda85cb41abdb9f/sock"}

uriキーには、sbt サーバーがリッスンしているソケットアドレスの後にlocal://で始まる文字列が含まれます。

Windows でのドメインソケット/名前付きパイプモードの場合、次のようなものになります。

{"uri":"local:sbt-server-0845deda85cb41abdb9f"}

uriキーには、名前付きパイプの名前の後にlocal:で始まる文字列が含まれます。この例では、名前付きパイプのパスは\\.\pipe\sbt-server-0845deda85cb41abdb9fになります。

TCP モードの場合、次のようになります。

{
  "uri":"tcp://127.0.0.1:5010",
  "tokenfilePath":"/Users/xxx/.sbt/1.0/server/0845deda85cb41abdb9f/token.json",
  "tokenfileUri":"file:/Users/xxx/.sbt/1.0/server/0845deda85cb41abdb9f/token.json"
}

この場合、uriキーには、サーバーがリッスンしているアドレスを持つ TCP uri が保持されます。このモードでは、ポートファイルには、tokenfilePathtokenfileUriの 2 つの追加キーが含まれます。これらは*トークンファイル*の場所を指します。

トークンファイルの場所は、実行間で変更されません。その内容は次のようになります

{
  "uri":"tcp://127.0.0.1:5010",
  "token":"12345678901234567890123456789012345678"
}

uriフィールドは同じで、tokenフィールドには 128 ビットの非負の整数が含まれています。

初期化リクエスト 

sbt サーバーとの通信を開始するには、クライアント (VS Code などのツール) は、最初に`initialize`リクエストを送信する必要があります。これは、クライアントがメソッドが "initialize" に設定され、paramsフィールドとしてInitializeParamsデータ型を持つリクエストを送信する必要があることを意味します。

サーバーが TCP モードで実行されている場合、認証するには、次のようにinitializationOptionsにトークンを渡す必要があります

type InitializationOptionsParams {
  token: String!
}

Telnet では次のようになります。

$ telnet 127.0.0.1 5010
Content-Type: application/vscode-jsonrpc; charset=utf-8
Content-Length: 149

{ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "initializationOptions": { "token": "84046191245433876643612047032303751629" } } }

サーバーが名前付きパイプモードで実行されている場合、トークンは必要なく、initializationOptionsは空のオブジェクト{}にする必要があります。

Unix で netcat を使用して、ドメインソケット/名前付きパイプモードで初期化メッセージを送信すると、次のようになります

$ nc -U /Users/foo/.sbt/1.0/server/0845deda85cb41abcdef/sock
Content-Length: 99^M
^M
{ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "initializationOptions": { } } }^M

名前付きパイプモードで実行されている場合のサーバーへの接続は、ソケットまたはパイプに最初に接続するプロセスに排他的です。

sbt がリクエストを受信すると、`initialized`イベントを送信します。

textDocument/publishDiagnosticsイベント 

コンパイラの警告とエラーは、textDocument/publishDiagnosticsイベントを使用してクライアントに送信されます。

以下に出力例を示します (JSON-RPC ヘッダーは省略)

{
  "jsonrpc": "2.0",
  "method": "textDocument/publishDiagnostics",
  "params": {
    "uri": "file:/Users/xxx/work/hellotest/Hello.scala",
    "diagnostics": [
      {
        "range": {
          "start": {
            "line": 2,
            "character": 0
          },
          "end": {
            "line": 2,
            "character": 1
          }
        },
        "severity": 1,
        "source": "sbt",
        "message": "')' expected but '}' found."
      }
    ]
  }
}

textDocument/didSaveイベント 

sbt 1.1.0 の時点では、sbt はtextDocument/didSave通知を受信するとcompileタスクを実行します。この動作は変更される可能性があります。

sbt/execリクエスト 

sbt/execリクエストは、ユーザーがシェルに入力するのをエミュレートします。

  • メソッド: sbt/exec
  • params
type SbtExecParams {
  commandLine: String!
}

Telnet では次のようになります。

Content-Length: 91

{ "jsonrpc": "2.0", "id": 2, "method": "sbt/exec", "params": { "commandLine": "clean" } }

ビルドで他のコマンドが実行されている可能性があるため、その場合はリクエストがキューに入れられます。

sbt/settingリクエスト 

sbt/settingリクエストを使用して、設定をクエリできます。

  • メソッド: sbt/setting
  • params
type SettingQuery {
  setting: String!
}

Telnet では次のようになります。

Content-Length: 102

{ "jsonrpc": "2.0", "id": 3, "method": "sbt/setting", "params": { "setting": "root/scalaVersion" } }
Content-Length: 87
Content-Type: application/vscode-jsonrpc; charset=utf-8

{"jsonrpc":"2.0","id":"3","result":{"value":"2.12.2","contentType":"java.lang.String"}}

コマンド実行とは異なり、これはすぐに応答します。

sbt/completionリクエスト 

(sbt 1.3.0+)

sbt/completionリクエストは、sbt シェルのタブ補完をエミュレートするために使用されます。

  • メソッド: sbt/completion
  • params: ` type CompletionParams { query: String! } `

Telnet では次のようになります。

Content-Length: 100

{ "jsonrpc": "2.0", "id": 15, "method": "sbt/completion", "params": { "query": "testOnly org." } }
Content-Length: 79
Content-Type: application/vscode-jsonrpc; charset=utf-8

{"jsonrpc":"2.0","id":15,"result":{"items":["testOnly org.sbt.ExampleSpec"]}}

これは、sbt の最後に利用可能な状態に基づいてすぐに応答します。

sbt/cancelRequest 

(sbt 1.3.0+)

sbt/cancelRequest リクエストは、実行中のタスクを終了させるために使用できます。

  • メソッド: sbt/cancelRequest
  • パラメータ: ` type CancelRequestParams { id: String! } `

telnetでは、以下のように表示されます(Id “foo” のタスクが現在実行中であると仮定)。

Content-Length: 93

{ "jsonrpc": "2.0", "id": "bar", "method": "sbt/cancelRequest", "params": { "id": "foo" } }
Content-Length: 126
Content-Type: application/vscode-jsonrpc; charset=utf-8

{"jsonrpc":"2.0","id":"bar","result":{"status":"Task cancelled","channelName":"network-1","execId":"foo","commandQueue":[]}}

これは、アクションの結果を応答として返します。