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 (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 が保持されます。このモードでは、ポートファイルには、tokenfilePath
とtokenfileUri
の 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
イベントを使用してクライアントに送信されます。
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
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
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
` 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":[]}}
これは、アクションの結果を応答として返します。