1. 命令

命令 

什麼是「命令」? 

「命令」看起來與任務類似:它是一個可從 sbt 主控台執行的具名操作。

然而,命令的實作將整個建置狀態(由 State 表示)作為其參數,並計算新的 State。這表示命令可以查看或修改其他 sbt 設定,例如。通常,當您需要執行常規任務中無法實現的操作時,您會使用命令。

簡介 

命令主要有三個面向

  1. 使用者用來調用命令的語法,包括

    • 語法的 Tab 鍵自動完成
    • 將輸入轉換為適當資料結構的剖析器
  2. 使用剖析的資料結構執行的動作。此動作會轉換建置的 State
  3. 提供給使用者的說明

在 sbt 中,語法部分(包括 Tab 鍵自動完成)是使用剖析器組合子指定的。如果您熟悉 Scala 標準程式庫中的剖析器組合子,這些非常相似。動作部分是函式 (State, T) => State,其中 T 是剖析器產生的資料結構。請參閱 剖析輸入 頁面,瞭解如何使用剖析器組合子。

State 提供對建置狀態的存取權,例如所有已註冊的 Command、要執行的其餘命令以及所有專案相關資訊。有關 State 的詳細資訊,請參閱 狀態和動作

最後,可能會提供基本說明資訊,help 命令會使用這些資訊來顯示命令說明。

定義命令 

命令將函式 State => Parser[T] 與動作 (State, T) => State 結合。使用 State => Parser[T] 而不只是 Parser[T] 的原因是,目前的 State 通常用於建構剖析器。例如,目前載入的專案(由 State 提供)會決定 project 命令的有效完成。以下章節將說明一般和特定情況的範例。

有關建構命令的來源 API 詳細資訊,請參閱 Command.scala

一般命令 

一般命令建構如下所示

val action: (State, T) => State = ...
val parser: State => Parser[T] = ...
val command: Command = Command("name")(parser)(action)

無參數命令 

有一個方便的方法可建構不接受任何參數的命令。

val action: State => State = ...
val command: Command = Command.command("name")(action)

單一參數命令 

有一個方便的方法可建構接受單一任意內容參數的命令。

// accepts the state and the single argument
val action: (State, String) => State = ...
val command: Command = Command.single("name")(action)

多參數命令 

有一個方便的方法可建構接受以空格分隔的多個參數的命令。

val action: (State, Seq[String]) => State = ...

// <arg> is the suggestion printed for tab completion on an argument
val command: Command = Command.args("name", "<arg>")(action)

完整範例 

以下範例是一個將命令新增至專案的範例建置。若要試用它

  1. 建立 build.sbtproject/CommandExample.scala
  2. 在專案上執行 sbt。
  3. 試用 hellohelloAllfailIfTruecolor 和 printState 命令。
  4. 使用 Tab 鍵自動完成和以下程式碼作為指南。

這是 build.sbt

import CommandExample._

ThisBuild / organization := "com.example"
ThisBuild / scalaVersion := "2.12.18"
ThisBuild / version      := "0.1.0-SNAPSHOT"

lazy val root = (project in file("."))
  .settings(
    commands ++= Seq(hello, helloAll, failIfTrue, changeColor, printState)
  )

這是 project/CommandExample.scala

import sbt._
import Keys._

// imports standard command parsing functionality
import complete.DefaultParsers._

object CommandExample {
  // A simple, no-argument command that prints "Hi",
  //  leaving the current state unchanged.
  def hello = Command.command("hello") { state =>
    println("Hi!")
    state
  }

  // A simple, multiple-argument command that prints "Hi" followed by the arguments.
  //   Again, it leaves the current state unchanged.
  def helloAll = Command.args("helloAll", "<name>") { (state, args) =>
    println("Hi " + args.mkString(" "))
    state
  }

  // A command that demonstrates failing or succeeding based on the input
  def failIfTrue = Command.single("failIfTrue") {
    case (state, "true") => state.fail
    case (state, _) => state
  }

  // Demonstration of a custom parser.
  // The command changes the foreground or background terminal color
  //  according to the input.
  lazy val change = Space ~> (reset | setColor)
  lazy val reset = token("reset" ^^^ "\033[0m")
  lazy val color = token( Space ~> ("blue" ^^^ "4" | "green" ^^^ "2") )
  lazy val select = token( "fg" ^^^ "3" | "bg" ^^^ "4" )
  lazy val setColor = (select ~ color) map { case (g, c) => "\033[" + g + c + "m" }

  def changeColor = Command("color")(_ => change) { (state, ansicode) =>
    print(ansicode)
    state
  }

  // A command that demonstrates getting information out of State.
  def printState = Command.command("printState") { state =>
    import state._
    println(definedCommands.size + " registered commands")
    println("commands to run: " + show(remainingCommands))
    println()

    println("original arguments: " + show(configuration.arguments))
    println("base directory: " + configuration.baseDirectory)
    println()

    println("sbt version: " + configuration.provider.id.version)
    println("Scala version (for sbt): " + configuration.provider.scalaProvider.version)
    println()

    val extracted = Project.extract(state)
    import extracted._
    println("Current build: " + currentRef.build)
    println("Current project: " + currentRef.project)
    println("Original setting count: " + session.original.size)
    println("Session setting count: " + session.append.size)

    state
  }

  def show[T](s: Seq[T]) =
    s.map("'" + _ + "'").mkString("[", ", ", "]")
}