1. sbt 伺服器

sbt 伺服器 

sbt 伺服器是 sbt 1.x 中新引入的功能,目前仍在開發中。您可能一開始會認為伺服器是在遠端伺服器上執行的東西,並且執行很棒的工作,但目前 sbt 伺服器並非如此。

實際上,sbt 伺服器只會將網路存取新增至 sbt 的 shell 命令,因此除了接受來自終端機的輸入外,伺服器也會接受來自網路的輸入。這允許多個用戶端連線至 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))

語言伺服器協定 3.0 

我們使用的連線協定是語言伺服器協定 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 伺服器可以在兩種模式下執行,這兩種模式在連線協定和初始化方面有所不同。自 sbt 1.1.x 以來的預設模式是網域通訊端模式,它使用 Unix 網域通訊端 (在 Unix 上) 或具名管道 (在 Windows 上) 來傳輸伺服器與用戶端之間的資料。此外,還有一個TCP 模式,它使用 TCP 來傳輸資料。

sbt 伺服器啟動的模式由索引鍵 serverConnectionType 控制,可以將其設定為 ConnectionType.Local 以用於網域通訊端/具名管道模式,或設定為 ConnectionType.Tcp 以用於 TCP 模式。

伺服器探索與驗證 

為了探索正在執行的伺服器,我們使用連接埠檔案

依預設,sbt shell 工作階段處於作用中狀態時,sbt 伺服器會執行。伺服器啟動時,它會建立一個稱為連接埠檔案的檔案。連接埠檔案位於 ./project/target/active.json。根據伺服器是在 TCP 模式下執行還是網域通訊端/具名管道模式下執行,連接埠檔案看起來會不同。它們看起來會像這樣

在網域通訊端/具名管道模式下,於 Unix 上

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

其中 uri 索引鍵將包含以 local:// 開頭,後接 sbt 伺服器正在接聽的通訊端位址的字串。

在網域通訊端/具名管道模式下,於 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。這些指向權杖檔案的位置。

權杖檔案的位置在執行之間不會變更。其內容看起來會像這樣

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

uri 欄位相同,而 token 欄位包含 128 位元非負整數。

初始化要求 

為了啟動與 sbt 伺服器的通訊,用戶端(例如 VS Code 之類的工具)必須先傳送 `initialize` 要求。這表示用戶端必須傳送一個要求,其中 method 設定為「initialize」,並且 InitializeParams 資料類型做為 params 欄位。

如果伺服器是在 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 要求會模擬使用者在 shell 中輸入。

  • method: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 要求可以用來查詢設定。

  • method: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 shell 的 Tab 鍵完成。

  • method: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 要求可以用來終止正在執行之任務的執行。

  • method:sbt/cancelRequest
  • params:` 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":[]}}

這會以動作的結果回應。