mirror of https://github.com/sbt/sbt.git
Merge pull request #3101 from dwijnand/document-and-cleanup-commands-and-misc
Document & cleanup commands (& misc cleanups)
This commit is contained in:
commit
6834def7b8
|
|
@ -315,7 +315,7 @@ lazy val docProjects: ScopeFilter = ScopeFilter(
|
|||
inConfigurations(Compile)
|
||||
)
|
||||
def fullDocSettings = Util.baseScalacOptions ++ Docs.settings ++ Sxr.settings ++ Seq(
|
||||
scalacOptions += "-Ymacro-no-expand", // for both sxr and doc
|
||||
scalacOptions += "-Ymacro-expand:none", // for both sxr and doc
|
||||
sources in sxr := {
|
||||
val allSources = (sources ?? Nil).all(docProjects).value
|
||||
allSources.flatten.distinct
|
||||
|
|
|
|||
|
|
@ -17,15 +17,15 @@ object BasicCommandStrings {
|
|||
val TerminateAction: String = Exit
|
||||
|
||||
def helpBrief = (HelpCommand, s"Displays this help message or prints detailed help on requested commands (run '$HelpCommand <command>').")
|
||||
def helpDetailed = HelpCommand + """
|
||||
def helpDetailed = s"""$HelpCommand
|
||||
|
||||
Prints a help summary.
|
||||
|
||||
""" + HelpCommand + """ <command>
|
||||
$HelpCommand <command>
|
||||
|
||||
Prints detailed help for command <command>.
|
||||
|
||||
""" + HelpCommand + """ <regular expression>
|
||||
$HelpCommand <regular expression>
|
||||
|
||||
Searches the help according to the provided regular expression.
|
||||
"""
|
||||
|
|
@ -50,6 +50,7 @@ object BasicCommandStrings {
|
|||
val detailed = levels.map(l => (l.toString, logLevelDetail(l))).toMap
|
||||
Help(brief, detailed)
|
||||
}
|
||||
|
||||
private[this] def logLevelDetail(level: Level.Value): String =
|
||||
s"""$level
|
||||
|
||||
|
|
@ -130,24 +131,21 @@ object BasicCommandStrings {
|
|||
|
||||
val AliasCommand = "alias"
|
||||
def AliasDetailed =
|
||||
AliasCommand + """
|
||||
s"""$AliasCommand
|
||||
|
||||
Prints a list of defined aliases.
|
||||
|
||||
""" +
|
||||
AliasCommand + """ name
|
||||
$AliasCommand name
|
||||
|
||||
Prints the alias defined for `name`.
|
||||
|
||||
""" +
|
||||
AliasCommand + """ name=value
|
||||
$AliasCommand name=value
|
||||
|
||||
Sets the alias `name` to `value`, replacing any existing alias with that name.
|
||||
Whenever `name` is entered, the corresponding `value` is run.
|
||||
If any argument is provided to `name`, it is appended as argument to `value`.
|
||||
|
||||
""" +
|
||||
AliasCommand + """ name=
|
||||
$AliasCommand name=
|
||||
|
||||
Removes the alias for `name`."""
|
||||
|
||||
|
|
@ -194,9 +192,9 @@ object BasicCommandStrings {
|
|||
def IfLast = "iflast"
|
||||
def IfLastCommon = "If there are no more commands after this one, 'command' is run."
|
||||
def IfLastDetailed =
|
||||
IfLast + """ <command>
|
||||
s"""$IfLast <command>
|
||||
|
||||
""" + IfLastCommon
|
||||
$IfLastCommon"""
|
||||
|
||||
val ContinuousExecutePrefix = "~"
|
||||
def continuousDetail = "Executes the specified command whenever source files change."
|
||||
|
|
|
|||
|
|
@ -2,7 +2,15 @@ package sbt
|
|||
|
||||
import sbt.util.Level
|
||||
import sbt.internal.util.{ AttributeKey, FullReader }
|
||||
import sbt.internal.util.complete.{ Completion, Completions, DefaultParsers, History => CHistory, HistoryCommands, Parser, TokenCompletions }
|
||||
import sbt.internal.util.complete.{
|
||||
Completion,
|
||||
Completions,
|
||||
DefaultParsers,
|
||||
History => CHistory,
|
||||
HistoryCommands,
|
||||
Parser,
|
||||
TokenCompletions
|
||||
}
|
||||
import sbt.internal.util.Types.{ const, idFun }
|
||||
import sbt.internal.inc.classpath.ClasspathUtilities.toLoader
|
||||
import sbt.internal.inc.ModuleUtilities
|
||||
|
|
@ -19,32 +27,34 @@ import sbt.io.IO
|
|||
import scala.util.control.NonFatal
|
||||
|
||||
object BasicCommands {
|
||||
lazy val allBasicCommands = Seq(nop, ignore, help, completionsCommand, multi, ifLast, append, setOnFailure, clearOnFailure,
|
||||
stashOnFailure, popOnFailure, reboot, call, early, exit, continuous, history, shell, client, read, alias) ++ compatCommands
|
||||
lazy val allBasicCommands: Seq[Command] = Seq(
|
||||
nop, ignore, help, completionsCommand, multi, ifLast, append, setOnFailure, clearOnFailure,
|
||||
stashOnFailure, popOnFailure, reboot, call, early, exit, continuous, history, shell, client,
|
||||
read, alias
|
||||
) ++ compatCommands
|
||||
|
||||
def nop = Command.custom(s => success(() => s))
|
||||
def ignore = Command.command(FailureWall)(idFun)
|
||||
def nop: Command = Command.custom(s => success(() => s))
|
||||
def ignore: Command = Command.command(FailureWall)(idFun)
|
||||
|
||||
def early: Command = Command.arb(earlyParser, earlyHelp)((s, other) => other :: s)
|
||||
|
||||
def early = Command.arb(earlyParser, earlyHelp) { (s, other) => other :: s }
|
||||
private[this] def levelParser: Parser[String] =
|
||||
token(Level.Debug.toString) | token(Level.Info.toString) | token(Level.Warn.toString) | token(Level.Error.toString)
|
||||
private[this] def earlyParser: State => Parser[String] = (s: State) =>
|
||||
(token(EarlyCommand + "(") flatMap { _ =>
|
||||
otherCommandParser(s) <~ token(")")
|
||||
}) |
|
||||
(token("-") flatMap { _ =>
|
||||
levelParser
|
||||
})
|
||||
Iterator(Level.Debug, Level.Info, Level.Warn, Level.Error) map (l => token(l.toString)) reduce (_ | _)
|
||||
|
||||
private[this] def earlyParser: State => Parser[String] = (s: State) => {
|
||||
val p1 = token(EarlyCommand + "(") flatMap (_ => otherCommandParser(s) <~ token(")"))
|
||||
val p2 = token("-") flatMap (_ => levelParser)
|
||||
p1 | p2
|
||||
}
|
||||
|
||||
private[this] def earlyHelp = Help(EarlyCommand, EarlyCommandBrief, EarlyCommandDetailed)
|
||||
|
||||
def help = Command.make(HelpCommand, helpBrief, helpDetailed)(helpParser)
|
||||
def help: Command = Command.make(HelpCommand, helpBrief, helpDetailed)(helpParser)
|
||||
|
||||
def helpParser(s: State) =
|
||||
def helpParser(s: State): Parser[() => State] =
|
||||
{
|
||||
val h = (Help.empty /: s.definedCommands) { (a, b) =>
|
||||
a ++
|
||||
(try b.help(s) catch { case NonFatal(ex) => Help.empty })
|
||||
}
|
||||
val h = (Help.empty /: s.definedCommands)((a, b) =>
|
||||
a ++ (try b.help(s) catch { case NonFatal(_) => Help.empty }))
|
||||
val helpCommands = h.detail.keySet
|
||||
val spacedArg = singleArgument(helpCommands).?
|
||||
applyEffect(spacedArg)(runHelp(s, h))
|
||||
|
|
@ -52,24 +62,20 @@ object BasicCommands {
|
|||
|
||||
def runHelp(s: State, h: Help)(arg: Option[String]): State =
|
||||
{
|
||||
val message = try
|
||||
Help.message(h, arg)
|
||||
catch {
|
||||
case NonFatal(ex) =>
|
||||
ex.toString
|
||||
}
|
||||
val message = try Help.message(h, arg) catch { case NonFatal(ex) => ex.toString }
|
||||
System.out.println(message)
|
||||
s
|
||||
}
|
||||
|
||||
def completionsCommand = Command.make(CompletionsCommand, CompletionsBrief, CompletionsDetailed)(completionsParser)
|
||||
def completionsParser(state: State) =
|
||||
{
|
||||
val notQuoted = (NotQuoted ~ any.*) map { case (nq, s) => nq ++ s }
|
||||
val quotedOrUnquotedSingleArgument = Space ~> (StringVerbatim | StringEscapable | notQuoted)
|
||||
def completionsCommand: Command =
|
||||
Command(CompletionsCommand, CompletionsBrief, CompletionsDetailed)(completionsParser)(runCompletions(_)(_))
|
||||
|
||||
def completionsParser(state: State): Parser[String] = {
|
||||
val notQuoted = (NotQuoted ~ any.*) map { case (nq, s) => nq ++ s }
|
||||
val quotedOrUnquotedSingleArgument = Space ~> (StringVerbatim | StringEscapable | notQuoted)
|
||||
token(quotedOrUnquotedSingleArgument ?? "" examples ("", " "))
|
||||
}
|
||||
|
||||
applyEffect(token(quotedOrUnquotedSingleArgument ?? "" examples ("", " ")))(runCompletions(state))
|
||||
}
|
||||
def runCompletions(state: State)(input: String): State = {
|
||||
Parser.completions(state.combinedParser, input, 9).get map {
|
||||
c => if (c.isEmpty) input else input + c.append
|
||||
|
|
@ -82,34 +88,40 @@ object BasicCommands {
|
|||
def multiParser(s: State): Parser[List[String]] =
|
||||
{
|
||||
val nonSemi = token(charClass(_ != ';').+, hide = const(true))
|
||||
(token(';' ~> OptSpace) flatMap { _ => matched((s.combinedParser & nonSemi) | nonSemi) <~ token(OptSpace) } map (_.trim)).+ map { _.toList }
|
||||
val semi = token(';' ~> OptSpace)
|
||||
val part = semi flatMap (_ => matched((s.combinedParser & nonSemi) | nonSemi) <~ token(OptSpace))
|
||||
(part map (_.trim)).+ map (_.toList)
|
||||
}
|
||||
|
||||
def multiApplied(s: State): Parser[() => State] =
|
||||
Command.applyEffect(multiParser(s))(_ ::: s)
|
||||
|
||||
def multi = Command.custom(multiApplied, Help(Multi, MultiBrief, MultiDetailed))
|
||||
def multi: Command = Command.custom(multiApplied, Help(Multi, MultiBrief, MultiDetailed))
|
||||
|
||||
lazy val otherCommandParser: State => Parser[String] =
|
||||
(s: State) => token(OptSpace ~> combinedLax(s, NotSpaceClass ~ any.*))
|
||||
|
||||
lazy val otherCommandParser = (s: State) => token(OptSpace ~> combinedLax(s, NotSpaceClass ~ any.*))
|
||||
def combinedLax(s: State, any: Parser[_]): Parser[String] =
|
||||
matched(s.combinedParser | token(any, hide = const(true)))
|
||||
|
||||
def ifLast = Command(IfLast, Help.more(IfLast, IfLastDetailed))(otherCommandParser) { (s, arg) =>
|
||||
if (s.remainingCommands.isEmpty) arg :: s else s
|
||||
}
|
||||
def append = Command(AppendCommand, Help.more(AppendCommand, AppendLastDetailed))(otherCommandParser) { (s, arg) =>
|
||||
s.copy(remainingCommands = s.remainingCommands :+ Exec(arg, s.source))
|
||||
}
|
||||
def ifLast: Command = Command(IfLast, Help.more(IfLast, IfLastDetailed))(otherCommandParser)((s, arg) =>
|
||||
if (s.remainingCommands.isEmpty) arg :: s else s)
|
||||
|
||||
def append: Command =
|
||||
Command(AppendCommand, Help.more(AppendCommand, AppendLastDetailed))(otherCommandParser)((s, arg) =>
|
||||
s.copy(remainingCommands = s.remainingCommands :+ Exec(arg, s.source)))
|
||||
|
||||
def setOnFailure: Command =
|
||||
Command(OnFailure, Help.more(OnFailure, OnFailureDetailed))(otherCommandParser)((s, arg) =>
|
||||
s.copy(onFailure = Some(Exec(arg, s.source))))
|
||||
|
||||
def setOnFailure = Command(OnFailure, Help.more(OnFailure, OnFailureDetailed))(otherCommandParser) { (s, arg) =>
|
||||
s.copy(onFailure = Some(Exec(arg, s.source)))
|
||||
}
|
||||
private[sbt] def compatCommands = Seq(
|
||||
Command.command(Compat.ClearOnFailure) { s =>
|
||||
s.log.warn(Compat.ClearOnFailureDeprecated)
|
||||
s.copy(onFailure = None)
|
||||
},
|
||||
Command.arb(s => token(Compat.OnFailure, hide = const(true)).flatMap(x => otherCommandParser(s))) { (s, arg) =>
|
||||
Command.arb(s => token(Compat.OnFailure, hide = const(true))
|
||||
.flatMap(_ => otherCommandParser(s))) { (s, arg) =>
|
||||
s.log.warn(Compat.OnFailureDeprecated)
|
||||
s.copy(onFailure = Some(Exec(arg, s.source)))
|
||||
},
|
||||
|
|
@ -119,43 +131,55 @@ object BasicCommands {
|
|||
}
|
||||
)
|
||||
|
||||
def clearOnFailure = Command.command(ClearOnFailure)(s => s.copy(onFailure = None))
|
||||
def stashOnFailure = Command.command(StashOnFailure)(s => s.copy(onFailure = None).update(OnFailureStack)(s.onFailure :: _.toList.flatten))
|
||||
def popOnFailure = Command.command(PopOnFailure) { s =>
|
||||
def clearOnFailure: Command = Command.command(ClearOnFailure)(s => s.copy(onFailure = None))
|
||||
|
||||
def stashOnFailure: Command = Command.command(StashOnFailure)(s =>
|
||||
s.copy(onFailure = None).update(OnFailureStack)(s.onFailure :: _.toList.flatten))
|
||||
|
||||
def popOnFailure: Command = Command.command(PopOnFailure) { s =>
|
||||
val stack = s.get(OnFailureStack).getOrElse(Nil)
|
||||
val updated = if (stack.isEmpty) s.remove(OnFailureStack) else s.put(OnFailureStack, stack.tail)
|
||||
updated.copy(onFailure = stack.headOption.flatten)
|
||||
}
|
||||
|
||||
def reboot = Command(RebootCommand, Help.more(RebootCommand, RebootDetailed))(rebootParser) { (s, full) =>
|
||||
s.reboot(full)
|
||||
}
|
||||
def rebootParser(s: State) = token(Space ~> "full" ^^^ true) ?? false
|
||||
def reboot: Command =
|
||||
Command(RebootCommand, Help.more(RebootCommand, RebootDetailed))(rebootParser)((s, full) => s reboot full)
|
||||
|
||||
def call = Command(ApplyCommand, Help.more(ApplyCommand, ApplyDetailed))(_ => callParser) {
|
||||
def rebootParser(s: State): Parser[Boolean] = token(Space ~> "full" ^^^ true) ?? false
|
||||
|
||||
def call: Command = Command(ApplyCommand, Help.more(ApplyCommand, ApplyDetailed))(_ => callParser) {
|
||||
case (state, (cp, args)) =>
|
||||
val parentLoader = getClass.getClassLoader
|
||||
state.log.info("Applying State transformations " + args.mkString(", ") + (if (cp.isEmpty) "" else " from " + cp.mkString(File.pathSeparator)))
|
||||
def argsStr = args mkString ", "
|
||||
def cpStr = cp mkString File.pathSeparator
|
||||
def fromCpStr = if (cp.isEmpty) "" else s" from $cpStr"
|
||||
state.log info s"Applying State transformations $argsStr$fromCpStr"
|
||||
val loader = if (cp.isEmpty) parentLoader else toLoader(cp.map(f => new File(f)), parentLoader)
|
||||
val loaded = args.map(arg => ModuleUtilities.getObject(arg, loader).asInstanceOf[State => State])
|
||||
(state /: loaded)((s, obj) => obj(s))
|
||||
}
|
||||
def callParser: Parser[(Seq[String], Seq[String])] = token(Space) ~> ((classpathOptionParser ?? Nil) ~ rep1sep(className, token(Space)))
|
||||
|
||||
def callParser: Parser[(Seq[String], Seq[String])] =
|
||||
token(Space) ~> ((classpathOptionParser ?? Nil) ~ rep1sep(className, token(Space)))
|
||||
|
||||
private[this] def className: Parser[String] =
|
||||
{
|
||||
val base = StringBasic & not('-' ~> any.*, "Class name cannot start with '-'.")
|
||||
def single(s: String) = Completions.single(Completion.displayOnly(s))
|
||||
val compl = TokenCompletions.fixed((seen, level) => if (seen.startsWith("-")) Completions.nil else single("<class name>"))
|
||||
val compl = TokenCompletions.fixed((seen, _) =>
|
||||
if (seen.startsWith("-")) Completions.nil else single("<class name>"))
|
||||
token(base, compl)
|
||||
}
|
||||
|
||||
private[this] def classpathOptionParser: Parser[Seq[String]] =
|
||||
token(("-cp" | "-classpath") ~> Space) ~> classpathStrings <~ token(Space)
|
||||
|
||||
private[this] def classpathStrings: Parser[Seq[String]] =
|
||||
token(StringBasic.map(s => IO.pathSplit(s).toSeq), "<classpath>")
|
||||
|
||||
def exit = Command.command(TerminateAction, exitBrief, exitBrief)(_ exit true)
|
||||
def exit: Command = Command.command(TerminateAction, exitBrief, exitBrief)(_ exit true)
|
||||
|
||||
def continuous =
|
||||
def continuous: Command =
|
||||
Command(ContinuousExecutePrefix, continuousBriefHelp, continuousDetail)(otherCommandParser) { (s, arg) =>
|
||||
withAttribute(s, Watched.Configuration, "Continuous execution not configured.") { w =>
|
||||
val repeat = ContinuousExecutePrefix + (if (arg.startsWith(" ")) arg else " " + arg)
|
||||
|
|
@ -163,7 +187,8 @@ object BasicCommands {
|
|||
}
|
||||
}
|
||||
|
||||
def history = Command.custom(historyParser, BasicCommandStrings.historyHelp)
|
||||
def history: Command = Command.custom(historyParser, BasicCommandStrings.historyHelp)
|
||||
|
||||
def historyParser(s: State): Parser[() => State] =
|
||||
Command.applyEffect(HistoryCommands.actionParser) { histFun =>
|
||||
val logError = (msg: String) => s.log.error(msg)
|
||||
|
|
@ -177,48 +202,56 @@ object BasicCommands {
|
|||
}
|
||||
}
|
||||
|
||||
def shell = Command.command(Shell, Help.more(Shell, ShellDetailed)) { s =>
|
||||
def shell: Command = Command.command(Shell, Help.more(Shell, ShellDetailed)) { s =>
|
||||
val history = (s get historyPath) getOrElse Some(new File(s.baseDir, ".history"))
|
||||
val prompt = (s get shellPrompt) match { case Some(pf) => pf(s); case None => "> " }
|
||||
val reader = new FullReader(history, s.combinedParser)
|
||||
val line = reader.readLine(prompt)
|
||||
line match {
|
||||
case Some(line) =>
|
||||
val newState = s.copy(onFailure = Some(Exec(Shell, None)), remainingCommands = Exec(line, s.source) +: Exec(Shell, None) +: s.remainingCommands).setInteractive(true)
|
||||
val newState = s.copy(
|
||||
onFailure = Some(Exec(Shell, None)),
|
||||
remainingCommands = Exec(line, s.source) +: Exec(Shell, None) +: s.remainingCommands
|
||||
).setInteractive(true)
|
||||
if (line.trim.isEmpty) newState else newState.clearGlobalLog
|
||||
case None => s.setInteractive(false)
|
||||
}
|
||||
}
|
||||
|
||||
def client = Command.make(Client, Help.more(Client, ClientDetailed))(clientParser)
|
||||
def clientParser(s0: State) =
|
||||
{
|
||||
val p = (token(Space) ~> repsep(StringBasic, token(Space))) | (token(EOF) map { case _ => Nil })
|
||||
applyEffect(p)({ inputArg =>
|
||||
val arguments = inputArg.toList ++
|
||||
(s0.remainingCommands match {
|
||||
case e :: Nil if e.commandLine == "shell" => Nil
|
||||
case xs => xs map { _.commandLine }
|
||||
})
|
||||
NetworkClient.run(arguments)
|
||||
"exit" :: s0.copy(remainingCommands = Nil)
|
||||
})
|
||||
}
|
||||
def client: Command = Command(Client, Help.more(Client, ClientDetailed))(_ => clientParser)(runClient)
|
||||
|
||||
def read = Command.make(ReadCommand, Help.more(ReadCommand, ReadDetailed))(s => applyEffect(readParser(s))(doRead(s)))
|
||||
def readParser(s: State) =
|
||||
def clientParser: Parser[Seq[String]] =
|
||||
(token(Space) ~> repsep(StringBasic, token(Space))) | (token(EOF) map (_ => Nil))
|
||||
|
||||
def runClient(s0: State, inputArg: Seq[String]): State = {
|
||||
val arguments = inputArg.toList ++
|
||||
(s0.remainingCommands match {
|
||||
case e :: Nil if e.commandLine == "shell" => Nil
|
||||
case xs => xs map (_.commandLine)
|
||||
})
|
||||
NetworkClient.run(arguments)
|
||||
"exit" :: s0.copy(remainingCommands = Nil)
|
||||
}
|
||||
|
||||
def read: Command = Command(ReadCommand, Help.more(ReadCommand, ReadDetailed))(readParser)(doRead(_)(_))
|
||||
|
||||
def readParser(s: State): Parser[Either[Int, Seq[File]]] =
|
||||
{
|
||||
val files = (token(Space) ~> fileParser(s.baseDir)).+
|
||||
val portAndSuccess = token(OptSpace) ~> Port
|
||||
portAndSuccess || files
|
||||
}
|
||||
|
||||
def doRead(s: State)(arg: Either[Int, Seq[File]]): State =
|
||||
arg match {
|
||||
case Left(portAndSuccess) =>
|
||||
val port = math.abs(portAndSuccess)
|
||||
val previousSuccess = portAndSuccess >= 0
|
||||
readMessage(port, previousSuccess) match {
|
||||
case Some(message) => (message :: (ReadCommand + " " + port) :: s).copy(onFailure = Some(Exec(ReadCommand + " " + (-port), s.source)))
|
||||
case Some(message) =>
|
||||
(message :: (ReadCommand + " " + port) :: s).copy(
|
||||
onFailure = Some(Exec(ReadCommand + " " + (-port), s.source))
|
||||
)
|
||||
case None =>
|
||||
System.err.println("Connection closed.")
|
||||
s.fail
|
||||
|
|
@ -226,12 +259,14 @@ object BasicCommands {
|
|||
case Right(from) =>
|
||||
val notFound = notReadable(from)
|
||||
if (notFound.isEmpty)
|
||||
readLines(from).toList ::: s // this means that all commands from all files are loaded, parsed, and inserted before any are executed
|
||||
// this means that all commands from all files are loaded, parsed, & inserted before any are executed
|
||||
readLines(from).toList ::: s
|
||||
else {
|
||||
s.log.error("Command file(s) not readable: \n\t" + notFound.mkString("\n\t"))
|
||||
s
|
||||
}
|
||||
}
|
||||
|
||||
private def readMessage(port: Int, previousSuccess: Boolean): Option[String] =
|
||||
{
|
||||
// split into two connections because this first connection ends the previous communication
|
||||
|
|
@ -243,14 +278,13 @@ object BasicCommands {
|
|||
}
|
||||
}
|
||||
|
||||
def alias = Command.make(AliasCommand, Help.more(AliasCommand, AliasDetailed)) { s =>
|
||||
def alias: Command = Command(AliasCommand, Help.more(AliasCommand, AliasDetailed)) { s =>
|
||||
val name = token(OpOrID.examples(aliasNames(s): _*))
|
||||
val assign = token(OptSpace ~ '=' ~ OptSpace)
|
||||
val sfree = removeAliases(s)
|
||||
val to = matched(sfree.combinedParser, partial = true).failOnException | any.+.string
|
||||
val base = (OptSpace ~> (name ~ (assign ~> to.?).?).?)
|
||||
applyEffect(base)(t => runAlias(s, t))
|
||||
}
|
||||
OptSpace ~> (name ~ (assign ~> to.?).?).?
|
||||
}(runAlias)
|
||||
|
||||
def runAlias(s: State, args: Option[(String, Option[Option[String]])]): State =
|
||||
args match {
|
||||
|
|
@ -273,34 +307,45 @@ object BasicCommands {
|
|||
s.copy(definedCommands = newAlias(name, value) +: s.definedCommands)
|
||||
|
||||
def removeAliases(s: State): State = removeTagged(s, CommandAliasKey)
|
||||
def removeAlias(s: State, name: String): State = s.copy(definedCommands = s.definedCommands.filter(c => !isAliasNamed(name, c)))
|
||||
|
||||
def removeTagged(s: State, tag: AttributeKey[_]): State = s.copy(definedCommands = removeTagged(s.definedCommands, tag))
|
||||
def removeTagged(as: Seq[Command], tag: AttributeKey[_]): Seq[Command] = as.filter(c => !(c.tags contains tag))
|
||||
def removeAlias(s: State, name: String): State =
|
||||
s.copy(definedCommands = s.definedCommands.filter(c => !isAliasNamed(name, c)))
|
||||
|
||||
def removeTagged(s: State, tag: AttributeKey[_]): State =
|
||||
s.copy(definedCommands = removeTagged(s.definedCommands, tag))
|
||||
|
||||
def removeTagged(as: Seq[Command], tag: AttributeKey[_]): Seq[Command] =
|
||||
as.filter(c => !(c.tags contains tag))
|
||||
|
||||
def isAliasNamed(name: String, c: Command): Boolean = isNamed(name, getAlias(c))
|
||||
def isNamed(name: String, alias: Option[(String, String)]): Boolean = alias match { case None => false; case Some((n, _)) => name == n }
|
||||
|
||||
def isNamed(name: String, alias: Option[(String, String)]): Boolean =
|
||||
alias match { case None => false; case Some((n, _)) => name == n }
|
||||
|
||||
def getAlias(c: Command): Option[(String, String)] = c.tags get CommandAliasKey
|
||||
def printAlias(s: State, name: String): Unit = printAliases(aliases(s, (n, v) => n == name))
|
||||
def printAlias(s: State, name: String): Unit = printAliases(aliases(s, (n, _) => n == name))
|
||||
def printAliases(s: State): Unit = printAliases(allAliases(s))
|
||||
|
||||
def printAliases(as: Seq[(String, String)]): Unit =
|
||||
for ((name, value) <- as)
|
||||
println("\t" + name + " = " + value)
|
||||
|
||||
def aliasNames(s: State): Seq[String] = allAliases(s).map(_._1)
|
||||
def allAliases(s: State): Seq[(String, String)] = aliases(s, (n, v) => true)
|
||||
def allAliases(s: State): Seq[(String, String)] = aliases(s, (_, _) => true)
|
||||
def aliases(s: State, pred: (String, String) => Boolean): Seq[(String, String)] =
|
||||
s.definedCommands.flatMap(c => getAlias(c).filter(tupled(pred)))
|
||||
|
||||
def newAlias(name: String, value: String): Command =
|
||||
Command.make(name, (name, "'" + value + "'"), "Alias of '" + value + "'")(aliasBody(name, value)).tag(CommandAliasKey, (name, value))
|
||||
Command.make(name, (name, s"'$value'"), s"Alias of '$value'")(aliasBody(name, value))
|
||||
.tag(CommandAliasKey, (name, value))
|
||||
|
||||
def aliasBody(name: String, value: String)(state: State): Parser[() => State] = {
|
||||
val aliasRemoved = removeAlias(state, name)
|
||||
// apply the alias value to the commands of `state` except for the alias to avoid recursion (#933)
|
||||
val partiallyApplied = Parser(Command.combine(aliasRemoved.definedCommands)(aliasRemoved))(value)
|
||||
val arg = matched(partiallyApplied & (success(()) | (SpaceClass ~ any.*)))
|
||||
// by scheduling the expanded alias instead of directly executing, we get errors on the expanded string (#598)
|
||||
// by scheduling the expanded alias instead of directly executing,
|
||||
// we get errors on the expanded string (#598)
|
||||
arg.map(str => () => (value + str) :: state)
|
||||
}
|
||||
|
||||
|
|
@ -310,5 +355,9 @@ object BasicCommands {
|
|||
case Some((n, v)) => aliasBody(n, v)(state)
|
||||
}
|
||||
|
||||
val CommandAliasKey = AttributeKey[(String, String)]("is-command-alias", "Internal: marker for Commands created as aliases for another command.")
|
||||
val CommandAliasKey: AttributeKey[(String, String)] =
|
||||
AttributeKey[(String, String)](
|
||||
"is-command-alias",
|
||||
"Internal: marker for Commands created as aliases for another command."
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,98 +8,155 @@ import sbt.internal.util.complete.{ DefaultParsers, EditDistance, Parser }
|
|||
import sbt.internal.util.Types.const
|
||||
import sbt.internal.util.{ AttributeKey, AttributeMap, Util }
|
||||
|
||||
/**
|
||||
* An operation that can be executed from the sbt console.
|
||||
*
|
||||
* <p>The operation takes a [[sbt.State State]] as a parameter and returns a [[sbt.State State]].
|
||||
* This means that a command can look at or modify other sbt settings, for example.
|
||||
* Typically you would resort to a command when you need to do something that's impossible in a regular task.
|
||||
*/
|
||||
sealed trait Command {
|
||||
def help: State => Help
|
||||
def parser: State => Parser[() => State]
|
||||
|
||||
def tags: AttributeMap
|
||||
def tag[T](key: AttributeKey[T], value: T): Command
|
||||
}
|
||||
private[sbt] final class SimpleCommand(val name: String, private[sbt] val help0: Help, val parser: State => Parser[() => State], val tags: AttributeMap) extends Command {
|
||||
assert(Command validID name, "'" + name + "' is not a valid command name.")
|
||||
def tag[T](key: AttributeKey[T], value: T): SimpleCommand = new SimpleCommand(name, help0, parser, tags.put(key, value))
|
||||
|
||||
private[sbt] final class SimpleCommand(
|
||||
val name: String,
|
||||
private[sbt] val help0: Help,
|
||||
val parser: State => Parser[() => State],
|
||||
|
||||
val tags: AttributeMap
|
||||
) extends Command {
|
||||
|
||||
assert(Command validID name, s"'$name' is not a valid command name.")
|
||||
|
||||
def help = const(help0)
|
||||
|
||||
def tag[T](key: AttributeKey[T], value: T): SimpleCommand =
|
||||
new SimpleCommand(name, help0, parser, tags.put(key, value))
|
||||
|
||||
override def toString = s"SimpleCommand($name)"
|
||||
}
|
||||
private[sbt] final class ArbitraryCommand(val parser: State => Parser[() => State], val help: State => Help, val tags: AttributeMap) extends Command {
|
||||
def tag[T](key: AttributeKey[T], value: T): ArbitraryCommand = new ArbitraryCommand(parser, help, tags.put(key, value))
|
||||
|
||||
private[sbt] final class ArbitraryCommand(
|
||||
val parser: State => Parser[() => State], val help: State => Help, val tags: AttributeMap
|
||||
) extends Command {
|
||||
def tag[T](key: AttributeKey[T], value: T): ArbitraryCommand =
|
||||
new ArbitraryCommand(parser, help, tags.put(key, value))
|
||||
}
|
||||
|
||||
object Command {
|
||||
import DefaultParsers._
|
||||
|
||||
def command(name: String, briefHelp: String, detail: String)(f: State => State): Command = command(name, Help(name, (name, briefHelp), detail))(f)
|
||||
def command(name: String, help: Help = Help.empty)(f: State => State): Command = make(name, help)(state => success(() => f(state)))
|
||||
// Lowest-level command construction
|
||||
|
||||
def make(name: String, help: Help = Help.empty)(parser: State => Parser[() => State]): Command =
|
||||
new SimpleCommand(name, help, parser, AttributeMap.empty)
|
||||
|
||||
def make(name: String, briefHelp: (String, String), detail: String)(parser: State => Parser[() => State]): Command =
|
||||
make(name, Help(name, briefHelp, detail))(parser)
|
||||
def make(name: String, help: Help = Help.empty)(parser: State => Parser[() => State]): Command = new SimpleCommand(name, help, parser, AttributeMap.empty)
|
||||
|
||||
// General command construction
|
||||
|
||||
/** Construct a command with the given name, parser and effect. */
|
||||
def apply[T](name: String, help: Help = Help.empty)(parser: State => Parser[T])(effect: (State, T) => State): Command =
|
||||
make(name, help)(applyEffect(parser)(effect))
|
||||
|
||||
def apply[T](name: String, briefHelp: (String, String), detail: String)(parser: State => Parser[T])(effect: (State, T) => State): Command =
|
||||
apply(name, Help(name, briefHelp, detail))(parser)(effect)
|
||||
def apply[T](name: String, help: Help = Help.empty)(parser: State => Parser[T])(effect: (State, T) => State): Command =
|
||||
make(name, help)(applyEffect(parser)(effect))
|
||||
|
||||
// No-argument command construction
|
||||
|
||||
/** Construct a no-argument command with the given name and effect. */
|
||||
def command(name: String, help: Help = Help.empty)(f: State => State): Command =
|
||||
make(name, help)(state => success(() => f(state)))
|
||||
|
||||
def command(name: String, briefHelp: String, detail: String)(f: State => State): Command =
|
||||
command(name, Help(name, (name, briefHelp), detail))(f)
|
||||
|
||||
// Single-argument command construction
|
||||
|
||||
/** Construct a single-argument command with the given name and effect. */
|
||||
def single(name: String, help: Help = Help.empty)(f: (State, String) => State): Command =
|
||||
make(name, help)(state => token(trimmed(spacedAny(name)) map apply1(f, state)))
|
||||
|
||||
def single(name: String, briefHelp: (String, String), detail: String)(f: (State, String) => State): Command =
|
||||
single(name, Help(name, briefHelp, detail))(f)
|
||||
|
||||
// Multi-argument command construction
|
||||
|
||||
/** Construct a multi-argument command with the given name, tab completion display and effect. */
|
||||
def args(name: String, display: String, help: Help = Help.empty)(f: (State, Seq[String]) => State): Command =
|
||||
make(name, help)(state => spaceDelimited(display) map apply1(f, state))
|
||||
|
||||
def args(name: String, briefHelp: (String, String), detail: String, display: String)(f: (State, Seq[String]) => State): Command =
|
||||
args(name, display, Help(name, briefHelp, detail))(f)
|
||||
|
||||
def args(name: String, display: String, help: Help = Help.empty)(f: (State, Seq[String]) => State): Command =
|
||||
make(name, help)(state => spaceDelimited(display) map apply1(f, state))
|
||||
// create ArbitraryCommand
|
||||
|
||||
def single(name: String, briefHelp: (String, String), detail: String)(f: (State, String) => State): Command =
|
||||
single(name, Help(name, briefHelp, detail))(f)
|
||||
def single(name: String, help: Help = Help.empty)(f: (State, String) => State): Command =
|
||||
make(name, help)(state => token(trimmed(spacedAny(name)) map apply1(f, state)))
|
||||
def customHelp(parser: State => Parser[() => State], help: State => Help): Command =
|
||||
new ArbitraryCommand(parser, help, AttributeMap.empty)
|
||||
|
||||
def custom(parser: State => Parser[() => State], help: Help = Help.empty): Command = customHelp(parser, const(help))
|
||||
def customHelp(parser: State => Parser[() => State], help: State => Help): Command = new ArbitraryCommand(parser, help, AttributeMap.empty)
|
||||
def arb[T](parser: State => Parser[T], help: Help = Help.empty)(effect: (State, T) => State): Command = custom(applyEffect(parser)(effect), help)
|
||||
def custom(parser: State => Parser[() => State], help: Help = Help.empty): Command =
|
||||
customHelp(parser, const(help))
|
||||
|
||||
def validID(name: String) = DefaultParsers.matches(OpOrID, name)
|
||||
def arb[T](parser: State => Parser[T], help: Help = Help.empty)(effect: (State, T) => State): Command =
|
||||
custom(applyEffect(parser)(effect), help)
|
||||
|
||||
// misc Command object utilities
|
||||
|
||||
def validID(name: String): Boolean = DefaultParsers.matches(OpOrID, name)
|
||||
|
||||
def applyEffect[T](p: Parser[T])(f: T => State): Parser[() => State] = p map (t => () => f(t))
|
||||
|
||||
def applyEffect[T](parser: State => Parser[T])(effect: (State, T) => State): State => Parser[() => State] =
|
||||
s => applyEffect(parser(s))(t => effect(s, t))
|
||||
def applyEffect[T](p: Parser[T])(f: T => State): Parser[() => State] =
|
||||
p map { t => () => f(t) }
|
||||
|
||||
def combine(cmds: Seq[Command]): State => Parser[() => State] =
|
||||
{
|
||||
val (simple, arbs) = separateCommands(cmds)
|
||||
state => (simpleParser(simple)(state) /: arbs.map(_ parser state)) { _ | _ }
|
||||
}
|
||||
def combine(cmds: Seq[Command]): State => Parser[() => State] = {
|
||||
val (simple, arbs) = separateCommands(cmds)
|
||||
state => (simpleParser(simple)(state) /: arbs.map(_ parser state))(_ | _)
|
||||
}
|
||||
|
||||
private[this] def separateCommands(cmds: Seq[Command]): (Seq[SimpleCommand], Seq[ArbitraryCommand]) =
|
||||
Util.separate(cmds) { case s: SimpleCommand => Left(s); case a: ArbitraryCommand => Right(a) }
|
||||
private[this] def apply1[A, B, C](f: (A, B) => C, a: A): B => () => C =
|
||||
b => () => f(a, b)
|
||||
|
||||
private[this] def apply1[A, B, C](f: (A, B) => C, a: A): B => () => C = b => () => f(a, b)
|
||||
|
||||
def simpleParser(cmds: Seq[SimpleCommand]): State => Parser[() => State] =
|
||||
simpleParser(cmds.map(sc => (sc.name, argParser(sc))).toMap)
|
||||
private[this] def argParser(sc: SimpleCommand): State => Parser[() => State] =
|
||||
{
|
||||
def usageError = s"${sc.name} usage:" + Help.message(sc.help0, None)
|
||||
s => (Parser.softFailure(usageError, definitive = true): Parser[() => State]) | sc.parser(s)
|
||||
}
|
||||
|
||||
private[this] def argParser(sc: SimpleCommand): State => Parser[() => State] = {
|
||||
def usageError = s"${sc.name} usage:" + Help.message(sc.help0, None)
|
||||
s => (Parser.softFailure(usageError, definitive = true): Parser[() => State]) | sc.parser(s)
|
||||
}
|
||||
|
||||
def simpleParser(commandMap: Map[String, State => Parser[() => State]]): State => Parser[() => State] =
|
||||
(state: State) => token(OpOrID examples commandMap.keys.toSet) flatMap { id =>
|
||||
state => token(OpOrID examples commandMap.keys.toSet) flatMap (id =>
|
||||
(commandMap get id) match {
|
||||
case None => failure(invalidValue("command", commandMap.keys)(id))
|
||||
case Some(c) => c(state)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
def invalidValue(label: String, allowed: Iterable[String])(value: String): String =
|
||||
"Not a valid " + label + ": " + value + similar(value, allowed)
|
||||
def similar(value: String, allowed: Iterable[String]): String =
|
||||
{
|
||||
val suggested = if (value.length > 2) suggestions(value, allowed.toSeq) else Nil
|
||||
if (suggested.isEmpty) "" else suggested.mkString(" (similar: ", ", ", ")")
|
||||
}
|
||||
s"Not a valid $label: $value" + similar(value, allowed)
|
||||
|
||||
def similar(value: String, allowed: Iterable[String]): String = {
|
||||
val suggested = if (value.length > 2) suggestions(value, allowed.toSeq) else Nil
|
||||
if (suggested.isEmpty) "" else suggested.mkString(" (similar: ", ", ", ")")
|
||||
}
|
||||
|
||||
def suggestions(a: String, bs: Seq[String], maxDistance: Int = 3, maxSuggestions: Int = 3): Seq[String] =
|
||||
bs.map { b => (b, distance(a, b)) } filter (_._2 <= maxDistance) sortBy (_._2) take (maxSuggestions) map (_._1)
|
||||
bs map (b => (b, distance(a, b))) filter (_._2 <= maxDistance) sortBy (_._2) take (maxSuggestions) map (_._1)
|
||||
|
||||
def distance(a: String, b: String): Int =
|
||||
EditDistance.levenshtein(a, b, insertCost = 1, deleteCost = 1, subCost = 2, transposeCost = 1, matchCost = -1, caseCost = 1, true)
|
||||
EditDistance.levenshtein(a, b, insertCost = 1, deleteCost = 1, subCost = 2, transposeCost = 1,
|
||||
matchCost = -1, caseCost = 1, transpositions = true)
|
||||
|
||||
def spacedAny(name: String): Parser[String] = spacedC(name, any)
|
||||
|
||||
def spacedC(name: String, c: Parser[Char]): Parser[String] =
|
||||
((c & opOrIDSpaced(name)) ~ c.+) map { case (f, rem) => (f +: rem).mkString }
|
||||
}
|
||||
|
|
@ -110,19 +167,25 @@ trait Help {
|
|||
def more: Set[String]
|
||||
def ++(o: Help): Help
|
||||
}
|
||||
private final class Help0(val brief: Seq[(String, String)], val detail: Map[String, String], val more: Set[String]) extends Help {
|
||||
|
||||
private final class Help0(
|
||||
val brief: Seq[(String, String)], val detail: Map[String, String], val more: Set[String]
|
||||
) extends Help {
|
||||
def ++(h: Help): Help = new Help0(Help0.this.brief ++ h.brief, Help0.this.detail ++ h.detail, more ++ h.more)
|
||||
}
|
||||
|
||||
object Help {
|
||||
val empty: Help = briefDetail(Nil)
|
||||
|
||||
def apply(name: String, briefHelp: (String, String), detail: String): Help = apply(briefHelp, Map((name, detail)))
|
||||
def apply(name: String, briefHelp: (String, String), detail: String): Help =
|
||||
apply(briefHelp, Map((name, detail)))
|
||||
|
||||
def apply(briefHelp: (String, String), detailedHelp: Map[String, String] = Map.empty): Help =
|
||||
apply(briefHelp :: Nil, detailedHelp)
|
||||
|
||||
def apply(briefHelp: Seq[(String, String)], detailedHelp: Map[String, String]): Help =
|
||||
apply(briefHelp, detailedHelp, Set.empty[String])
|
||||
|
||||
def apply(briefHelp: Seq[(String, String)], detailedHelp: Map[String, String], more: Set[String]): Help =
|
||||
new Help0(briefHelp, detailedHelp, more)
|
||||
|
||||
|
|
@ -138,15 +201,17 @@ object Help {
|
|||
case Some(x) => detail(x, h.detail)
|
||||
case None =>
|
||||
val brief = aligned(" ", " ", h.brief).mkString("\n", "\n", "\n")
|
||||
val more = h.more.toSeq.sorted
|
||||
val more = h.more
|
||||
if (more.isEmpty)
|
||||
brief
|
||||
else
|
||||
brief + "\n" + moreMessage(more)
|
||||
brief + "\n" + moreMessage(more.toSeq.sorted)
|
||||
}
|
||||
|
||||
def moreMessage(more: Seq[String]): String =
|
||||
more.mkString("More command help available using 'help <command>' for:\n ", ", ", "\n")
|
||||
}
|
||||
|
||||
trait CommandDefinitions extends (State => State) {
|
||||
def commands: Seq[Command] = ReflectUtilities.allVals[Command](this).values.toSeq
|
||||
def apply(s: State): State = s ++ commands
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ import sbt.internal.util.complete.DefaultParsers._
|
|||
import sbt.io.IO
|
||||
|
||||
object CommandUtil {
|
||||
def readLines(files: Seq[File]): Seq[String] = files flatMap (line => IO.readLines(line)) flatMap processLine
|
||||
def processLine(s: String) = { val trimmed = s.trim; if (ignoreLine(trimmed)) None else Some(trimmed) }
|
||||
def ignoreLine(s: String) = s.isEmpty || s.startsWith("#")
|
||||
def readLines(files: Seq[File]): Seq[String] = files flatMap (IO.readLines(_)) flatMap processLine
|
||||
def processLine(s: String): Option[String] = { val s2 = s.trim; if (ignoreLine(s2)) None else Some(s2) }
|
||||
def ignoreLine(s: String): Boolean = s.isEmpty || s.startsWith("#")
|
||||
|
||||
private def canRead = (_: File).canRead
|
||||
def notReadable(files: Seq[File]): Seq[File] = files filterNot canRead
|
||||
|
|
@ -20,17 +20,18 @@ object CommandUtil {
|
|||
|
||||
// slightly better fallback in case of older launcher
|
||||
def bootDirectory(state: State): File =
|
||||
try { state.configuration.provider.scalaProvider.launcher.bootDirectory }
|
||||
catch { case e: NoSuchMethodError => new File(".").getAbsoluteFile }
|
||||
try state.configuration.provider.scalaProvider.launcher.bootDirectory
|
||||
catch { case _: NoSuchMethodError => new File(".").getAbsoluteFile }
|
||||
|
||||
def aligned(pre: String, sep: String, in: Seq[(String, String)]): Seq[String] = if (in.isEmpty) Nil else {
|
||||
val width = in.map(_._1.length).max
|
||||
in.map { case (a, b) => (pre + fill(a, width) + sep + b) }
|
||||
val width = in.iterator.map(_._1.length).max
|
||||
for ((a, b) <- in) yield pre + fill(a, width) + sep + b
|
||||
}
|
||||
def fill(s: String, size: Int) = s + " " * math.max(size - s.length, 0)
|
||||
|
||||
def fill(s: String, size: Int): String = s + " " * math.max(size - s.length, 0)
|
||||
|
||||
def withAttribute[T](s: State, key: AttributeKey[T], ifMissing: String)(f: T => State): State =
|
||||
(s get key) match {
|
||||
s get key match {
|
||||
case None =>
|
||||
s.log.error(ifMissing); s.fail
|
||||
case Some(nav) => f(nav)
|
||||
|
|
@ -41,6 +42,7 @@ object CommandUtil {
|
|||
val arg = (NotSpaceClass ~ any.*) map { case (ns, s) => (ns +: s).mkString }
|
||||
token(Space) ~> token(arg examples exampleStrings)
|
||||
}
|
||||
|
||||
def detail(selected: String, detailMap: Map[String, String]): String =
|
||||
detailMap.get(selected) match {
|
||||
case Some(exactDetail) => exactDetail
|
||||
|
|
@ -51,9 +53,11 @@ object CommandUtil {
|
|||
else
|
||||
layoutDetails(details)
|
||||
} catch {
|
||||
case pse: PatternSyntaxException => sys.error("Invalid regular expression (java.util.regex syntax).\n" + pse.getMessage)
|
||||
case pse: PatternSyntaxException =>
|
||||
sys.error("Invalid regular expression (java.util.regex syntax).\n" + pse.getMessage)
|
||||
}
|
||||
}
|
||||
|
||||
def searchHelp(selected: String, detailMap: Map[String, String]): Map[String, String] =
|
||||
{
|
||||
val pattern = Pattern.compile(selected, HelpPatternFlags)
|
||||
|
|
@ -69,9 +73,9 @@ object CommandUtil {
|
|||
Nil
|
||||
}
|
||||
}
|
||||
|
||||
def layoutDetails(details: Map[String, String]): String =
|
||||
details.map { case (k, v) => k + "\n\n " + v } mkString ("\n", "\n\n", "\n")
|
||||
|
||||
final val HelpPatternFlags = Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,28 +4,14 @@
|
|||
package sbt
|
||||
|
||||
import sbt.internal.{
|
||||
Act,
|
||||
Aggregation,
|
||||
BuildStructure,
|
||||
BuildUnit,
|
||||
CommandExchange,
|
||||
CommandStrings,
|
||||
EvaluateConfigurations,
|
||||
Inspect,
|
||||
IvyConsole,
|
||||
Load,
|
||||
LoadedBuildUnit,
|
||||
Output,
|
||||
PluginsDebug,
|
||||
ProjectNavigation,
|
||||
Script,
|
||||
SessionSettings,
|
||||
SetResult,
|
||||
SettingCompletions,
|
||||
LogManager,
|
||||
DefaultBackgroundJobService
|
||||
Act, Aggregation, BuildStructure, BuildUnit, CommandExchange, CommandStrings,
|
||||
EvaluateConfigurations, Inspect, IvyConsole, Load, LoadedBuildUnit, Output,
|
||||
PluginsDebug, ProjectNavigation, Script, SessionSettings, SetResult,
|
||||
SettingCompletions, LogManager, DefaultBackgroundJobService
|
||||
}
|
||||
import sbt.internal.util.{
|
||||
AttributeKey, AttributeMap, ConsoleOut, GlobalLogging, LineRange, MainAppender, SimpleReader, Types
|
||||
}
|
||||
import sbt.internal.util.{ AttributeKey, AttributeMap, ConsoleOut, GlobalLogging, LineRange, MainAppender, SimpleReader, Types }
|
||||
import sbt.util.{ Level, Logger }
|
||||
|
||||
import sbt.internal.util.complete.{ DefaultParsers, Parser }
|
||||
|
|
@ -124,6 +110,7 @@ final class ScriptMain extends xsbti.AppMain {
|
|||
))
|
||||
}
|
||||
}
|
||||
|
||||
final class ConsoleMain extends xsbti.AppMain {
|
||||
def run(configuration: xsbti.AppConfiguration): xsbti.MainResult =
|
||||
runManaged(initialState(
|
||||
|
|
@ -175,11 +162,17 @@ object BuiltinCommands {
|
|||
|
||||
def ConsoleCommands: Seq[Command] = Seq(ignore, exit, IvyConsole.command, setLogLevel, early, act, nop)
|
||||
def ScriptCommands: Seq[Command] = Seq(ignore, exit, Script.command, setLogLevel, early, act, nop)
|
||||
def DefaultCommands: Seq[Command] = Seq(ignore, help, completionsCommand, about, tasks, settingsCommand, loadProject, templateCommand,
|
||||
projects, project, reboot, read, history, set, sessionCommand, inspect, loadProjectImpl, loadFailed, Cross.crossBuild, Cross.switchVersion,
|
||||
Cross.crossRestoreSession, setOnFailure, clearOnFailure, stashOnFailure, popOnFailure, setLogLevel, plugin, plugins,
|
||||
ifLast, multi, shell, server, BasicCommands.client, continuous, eval, alias, append, last, lastGrep, export, boot, nop, call, exit, early, initialize, act) ++
|
||||
compatCommands
|
||||
|
||||
def DefaultCommands: Seq[Command] = Seq(
|
||||
ignore, help, completionsCommand, about, tasks, settingsCommand, loadProject,
|
||||
templateCommand, projects, project, reboot, read, history, set, sessionCommand,
|
||||
inspect, loadProjectImpl, loadFailed, Cross.crossBuild, Cross.switchVersion,
|
||||
Cross.crossRestoreSession, setOnFailure, clearOnFailure, stashOnFailure, popOnFailure,
|
||||
setLogLevel, plugin, plugins, ifLast, multi, shell, server, BasicCommands.client,
|
||||
continuous, eval, alias, append, last, lastGrep, export, boot, nop, call, exit,
|
||||
early, initialize, act
|
||||
) ++ compatCommands
|
||||
|
||||
def DefaultBootCommands: Seq[String] = LoadProject :: (IfLast + " " + Shell) :: Nil
|
||||
|
||||
def boot = Command.make(BootCommand)(bootParser)
|
||||
|
|
@ -215,6 +208,7 @@ object BuiltinCommands {
|
|||
val allPluginNames = e.structure.units.values.flatMap(u => list(u.unit)).toSeq.distinct
|
||||
if (allPluginNames.isEmpty) "" else allPluginNames.mkString("Available Plugins: ", ", ", "")
|
||||
}
|
||||
|
||||
def aboutScala(s: State, e: Extracted): String =
|
||||
{
|
||||
val scalaVersion = e.getOpt(Keys.scalaVersion)
|
||||
|
|
@ -228,6 +222,7 @@ object BuiltinCommands {
|
|||
case (None, None, None) => ""
|
||||
}
|
||||
}
|
||||
|
||||
def aboutString(s: State): String =
|
||||
{
|
||||
val (name, ver, scalaVer, about) = (sbtName(s), sbtVersion(s), scalaVersion(s), aboutProject(s))
|
||||
|
|
@ -236,14 +231,22 @@ object BuiltinCommands {
|
|||
|%s, %s plugins, and build definitions are using Scala %s
|
||||
|""".stripMargin.format(name, ver, about, name, name, scalaVer)
|
||||
}
|
||||
private[this] def selectScalaVersion(sv: Option[String], si: ScalaInstance): String = sv match { case Some(si.version) => si.version; case _ => si.actualVersion }
|
||||
|
||||
private[this] def selectScalaVersion(sv: Option[String], si: ScalaInstance): String =
|
||||
sv match {
|
||||
case Some(si.version) => si.version
|
||||
case _ => si.actualVersion
|
||||
}
|
||||
|
||||
private[this] def quiet[T](t: => T): Option[T] = try { Some(t) } catch { case e: Exception => None }
|
||||
|
||||
def settingsCommand = showSettingLike(SettingsCommand, settingsPreamble, KeyRanks.MainSettingCutoff, key => !isTask(key.manifest))
|
||||
def settingsCommand: Command =
|
||||
showSettingLike(SettingsCommand, settingsPreamble, KeyRanks.MainSettingCutoff, key => !isTask(key.manifest))
|
||||
|
||||
def tasks = showSettingLike(TasksCommand, tasksPreamble, KeyRanks.MainTaskCutoff, key => isTask(key.manifest))
|
||||
def tasks: Command =
|
||||
showSettingLike(TasksCommand, tasksPreamble, KeyRanks.MainTaskCutoff, key => isTask(key.manifest))
|
||||
|
||||
def showSettingLike(command: String, preamble: String, cutoff: Int, keep: AttributeKey[_] => Boolean) =
|
||||
def showSettingLike(command: String, preamble: String, cutoff: Int, keep: AttributeKey[_] => Boolean): Command =
|
||||
Command(command, settingsBrief(command), settingsDetailed(command))(showSettingParser(keep)) {
|
||||
case (s: State, (verbosity: Int, selected: Option[String])) =>
|
||||
if (selected.isEmpty) System.out.println(preamble)
|
||||
|
|
@ -304,20 +307,22 @@ object BuiltinCommands {
|
|||
s.copy(definedCommands = DefaultCommands)
|
||||
}
|
||||
|
||||
def initialize = Command.command(InitCommand) { s =>
|
||||
def initialize: Command = Command.command(InitCommand) { s =>
|
||||
/*"load-commands -base ~/.sbt/commands" :: */ readLines(readable(sbtRCs(s))).toList ::: s
|
||||
}
|
||||
|
||||
def eval = Command.single(EvalCommand, Help.more(EvalCommand, evalDetailed)) { (s, arg) =>
|
||||
def eval: Command = Command.single(EvalCommand, Help.more(EvalCommand, evalDetailed)) { (s, arg) =>
|
||||
if (Project.isProjectLoaded(s)) loadedEval(s, arg) else rawEval(s, arg)
|
||||
s
|
||||
}
|
||||
|
||||
private[this] def loadedEval(s: State, arg: String): Unit = {
|
||||
val extracted = Project extract s
|
||||
import extracted._
|
||||
val result = session.currentEval().eval(arg, srcName = "<eval>", imports = autoImports(extracted))
|
||||
s.log.info(s"ans: ${result.tpe} = ${result.getValue(currentLoader)}")
|
||||
}
|
||||
|
||||
private[this] def rawEval(s: State, arg: String): Unit = {
|
||||
val app = s.configuration.provider
|
||||
val classpath = app.mainClasspath ++ app.scalaProvider.jars
|
||||
|
|
@ -325,15 +330,17 @@ object BuiltinCommands {
|
|||
s.log.info(s"ans: ${result.tpe} = ${result.getValue(app.loader)}")
|
||||
}
|
||||
|
||||
def sessionCommand: Command = Command.make(SessionCommand, sessionBrief, SessionSettings.Help)(SessionSettings.command)
|
||||
def sessionCommand: Command =
|
||||
Command.make(SessionCommand, sessionBrief, SessionSettings.Help)(SessionSettings.command)
|
||||
|
||||
def reapply(newSession: SessionSettings, structure: BuildStructure, s: State): State =
|
||||
{
|
||||
s.log.info("Reapplying settings...")
|
||||
// Here, for correct behavior, we also need to re-inject a settings logger, as we'll be re-evaluating settings.
|
||||
// For correct behavior, we also need to re-inject a settings logger, as we'll be re-evaluating settings
|
||||
val loggerInject = LogManager.settingsLogger(s)
|
||||
val withLogger = newSession.appendRaw(loggerInject :: Nil)
|
||||
val newStructure = Load.reapply(withLogger.mergeSettings, structure)(Project.showContextKey(newSession, structure))
|
||||
val show = Project.showContextKey(newSession, structure)
|
||||
val newStructure = Load.reapply(withLogger.mergeSettings, structure)(show)
|
||||
Project.setProject(newSession, newStructure, s)
|
||||
}
|
||||
|
||||
|
|
@ -354,7 +361,9 @@ object BuiltinCommands {
|
|||
arg,
|
||||
LineRange(0, 0)
|
||||
)(cl)
|
||||
val setResult = if (all) SettingCompletions.setAll(extracted, settings) else SettingCompletions.setThis(s, extracted, settings, arg)
|
||||
val setResult =
|
||||
if (all) SettingCompletions.setAll(extracted, settings)
|
||||
else SettingCompletions.setThis(s, extracted, settings, arg)
|
||||
s.log.info(setResult.quietSummary)
|
||||
s.log.debug(setResult.verboseSummary)
|
||||
reapply(setResult.session, structure, s)
|
||||
|
|
@ -369,7 +378,7 @@ object BuiltinCommands {
|
|||
s
|
||||
}
|
||||
|
||||
def lastGrep = Command(LastGrepCommand, lastGrepBrief, lastGrepDetailed)(lastGrepParser) {
|
||||
def lastGrep: Command = Command(LastGrepCommand, lastGrepBrief, lastGrepDetailed)(lastGrepParser) {
|
||||
case (s, (pattern, Some(sks))) =>
|
||||
val (str, _, display) = extractLast(s)
|
||||
Output.lastGrep(sks, str.streams(s), pattern, printLast(s))(display)
|
||||
|
|
@ -469,32 +478,38 @@ object BuiltinCommands {
|
|||
for (id <- build.defined.keys.toSeq.sorted) log.info("\t" + prefix(id) + id)
|
||||
}
|
||||
|
||||
def act = Command.customHelp(Act.actParser, actHelp)
|
||||
def actHelp = (s: State) => CommandStrings.showHelp ++ CommandStrings.multiTaskHelp ++ keysHelp(s)
|
||||
def act: Command = Command.customHelp(Act.actParser, actHelp)
|
||||
def actHelp: State => Help = s => CommandStrings.showHelp ++ CommandStrings.multiTaskHelp ++ keysHelp(s)
|
||||
|
||||
def keysHelp(s: State): Help =
|
||||
if (Project.isProjectLoaded(s))
|
||||
Help.detailOnly(taskDetail(allTaskAndSettingKeys(s)))
|
||||
else
|
||||
Help.empty
|
||||
def plugins = Command.command(PluginsCommand, pluginsBrief, pluginsDetailed) { s =>
|
||||
|
||||
def plugins: Command = Command.command(PluginsCommand, pluginsBrief, pluginsDetailed) { s =>
|
||||
val helpString = PluginsDebug.helpAll(s)
|
||||
System.out.println(helpString)
|
||||
s
|
||||
}
|
||||
|
||||
val pluginParser: State => Parser[AutoPlugin] = s => {
|
||||
val autoPlugins: Map[String, AutoPlugin] = PluginsDebug.autoPluginMap(s)
|
||||
token(Space) ~> Act.knownPluginParser(autoPlugins, "plugin")
|
||||
}
|
||||
def plugin = Command(PluginCommand)(pluginParser) { (s, plugin) =>
|
||||
|
||||
def plugin: Command = Command(PluginCommand)(pluginParser) { (s, plugin) =>
|
||||
val helpString = PluginsDebug.help(plugin, s)
|
||||
System.out.println(helpString)
|
||||
s
|
||||
}
|
||||
|
||||
def projects = Command(ProjectsCommand, (ProjectsCommand, projectsBrief), projectsDetailed)(s => projectsParser(s).?) {
|
||||
case (s, Some(modifyBuilds)) => transformExtraBuilds(s, modifyBuilds)
|
||||
case (s, None) => showProjects(s); s
|
||||
}
|
||||
def projects: Command =
|
||||
Command(ProjectsCommand, (ProjectsCommand, projectsBrief), projectsDetailed)(s => projectsParser(s).?) {
|
||||
case (s, Some(modifyBuilds)) => transformExtraBuilds(s, modifyBuilds)
|
||||
case (s, None) => showProjects(s); s
|
||||
}
|
||||
|
||||
def showProjects(s: State): Unit = {
|
||||
val extracted = Project extract s
|
||||
import extracted._
|
||||
|
|
@ -502,6 +517,7 @@ object BuiltinCommands {
|
|||
listBuild(curi, structure.units(curi), true, cid, s.log)
|
||||
for ((uri, build) <- structure.units if curi != uri) listBuild(uri, build, false, cid, s.log)
|
||||
}
|
||||
|
||||
def transformExtraBuilds(s: State, f: List[URI] => List[URI]): State =
|
||||
{
|
||||
val original = Project.extraBuilds(s)
|
||||
|
|
@ -553,11 +569,11 @@ object BuiltinCommands {
|
|||
Nil
|
||||
|
||||
def loadProject: Command =
|
||||
Command(LoadProject, LoadProjectBrief, LoadProjectDetailed)(loadProjectParser) { (s, arg) =>
|
||||
Command(LoadProject, LoadProjectBrief, LoadProjectDetailed)(loadProjectParser)((s, arg) =>
|
||||
loadProjectCommands(arg) ::: s
|
||||
}
|
||||
)
|
||||
|
||||
private[this] def loadProjectParser = (s: State) => matched(Project.loadActionParser)
|
||||
private[this] def loadProjectParser: State => Parser[String] = _ => matched(Project.loadActionParser)
|
||||
private[this] def loadProjectCommand(command: String, arg: String): String = s"$command $arg".trim
|
||||
|
||||
def loadProjectImpl: Command = Command(LoadProjectImpl)(_ => Project.loadActionParser)(doLoadProject)
|
||||
|
|
|
|||
|
|
@ -361,8 +361,10 @@ object Project extends ProjectExtra {
|
|||
|
||||
def getProjectForReference(ref: Reference, structure: BuildStructure): Option[ResolvedProject] =
|
||||
ref match { case pr: ProjectRef => getProject(pr, structure); case _ => None }
|
||||
|
||||
def getProject(ref: ProjectRef, structure: BuildStructure): Option[ResolvedProject] = getProject(ref, structure.units)
|
||||
def getProject(ref: ProjectRef, structure: LoadedBuild): Option[ResolvedProject] = getProject(ref, structure.units)
|
||||
|
||||
def getProject(ref: ProjectRef, units: Map[URI, LoadedBuildUnit]): Option[ResolvedProject] =
|
||||
(units get ref.build).flatMap(_.defined get ref.project)
|
||||
|
||||
|
|
@ -371,6 +373,7 @@ object Project extends ProjectExtra {
|
|||
val previousOnUnload = orIdentity(s get Keys.onUnload.key)
|
||||
previousOnUnload(s.runExitHooks())
|
||||
}
|
||||
|
||||
def setProject(session: SessionSettings, structure: BuildStructure, s: State): State =
|
||||
{
|
||||
val unloaded = runUnloadHooks(s)
|
||||
|
|
@ -386,6 +389,7 @@ object Project extends ProjectExtra {
|
|||
def getHooks(data: Settings[Scope]): (State => State, State => State) = (getHook(Keys.onLoad, data), getHook(Keys.onUnload, data))
|
||||
|
||||
def current(state: State): ProjectRef = session(state).current
|
||||
|
||||
def updateCurrent(s: State): State =
|
||||
{
|
||||
val structure = Project.structure(s)
|
||||
|
|
@ -410,6 +414,7 @@ object Project extends ProjectExtra {
|
|||
.put(templateResolverInfos.key, trs)
|
||||
s.copy(attributes = setCond(shellPrompt.key, prompt, newAttrs), definedCommands = newDefinedCommands)
|
||||
}
|
||||
|
||||
def setCond[T](key: AttributeKey[T], vopt: Option[T], attributes: AttributeMap): AttributeMap =
|
||||
vopt match { case Some(v) => attributes.put(key, v); case None => attributes.remove(key) }
|
||||
|
||||
|
|
@ -528,20 +533,21 @@ object Project extends ProjectExtra {
|
|||
def relation(structure: BuildStructure, actual: Boolean)(implicit display: Show[ScopedKey[_]]): Relation[ScopedKey[_], ScopedKey[_]] =
|
||||
relation(structure.settings, actual)(structure.delegates, structure.scopeLocal, display)
|
||||
|
||||
private[sbt] def relation(settings: Seq[Def.Setting[_]], actual: Boolean)(implicit delegates: Scope => Seq[Scope], scopeLocal: Def.ScopeLocal, display: Show[ScopedKey[_]]): Relation[ScopedKey[_], ScopedKey[_]] =
|
||||
private[sbt] def relation(settings: Seq[Def.Setting[_]], actual: Boolean)(
|
||||
implicit delegates: Scope => Seq[Scope], scopeLocal: Def.ScopeLocal, display: Show[ScopedKey[_]]
|
||||
): Relation[ScopedKey[_], ScopedKey[_]] =
|
||||
{
|
||||
type Rel = Relation[ScopedKey[_], ScopedKey[_]]
|
||||
val cMap = Def.flattenLocals(Def.compiled(settings, actual))
|
||||
((Relation.empty: Rel) /: cMap) {
|
||||
case (r, (key, value)) =>
|
||||
r + (key, value.dependencies)
|
||||
}
|
||||
val emptyRelation = Relation.empty[ScopedKey[_], ScopedKey[_]]
|
||||
(emptyRelation /: cMap) { case (r, (key, value)) => r + (key, value.dependencies) }
|
||||
}
|
||||
|
||||
def showDefinitions(key: AttributeKey[_], defs: Seq[Scope])(implicit display: Show[ScopedKey[_]]): String =
|
||||
showKeys(defs.map(scope => ScopedKey(scope, key)))
|
||||
|
||||
def showUses(defs: Seq[ScopedKey[_]])(implicit display: Show[ScopedKey[_]]): String =
|
||||
showKeys(defs)
|
||||
|
||||
private[this] def showKeys(s: Seq[ScopedKey[_]])(implicit display: Show[ScopedKey[_]]): String =
|
||||
s.map(display.show).sorted.mkString("\n\t", "\n\t", "\n\n")
|
||||
|
||||
|
|
@ -575,20 +581,24 @@ object Project extends ProjectExtra {
|
|||
def projectReturn(s: State): List[File] = getOrNil(s, ProjectReturn)
|
||||
def inPluginProject(s: State): Boolean = projectReturn(s).length > 1
|
||||
def setProjectReturn(s: State, pr: List[File]): State = s.copy(attributes = s.attributes.put(ProjectReturn, pr))
|
||||
def loadAction(s: State, action: LoadAction.Value) = action match {
|
||||
|
||||
def loadAction(s: State, action: LoadAction.Value): (State, File) = action match {
|
||||
case Return =>
|
||||
projectReturn(s) match {
|
||||
case current :: returnTo :: rest => (setProjectReturn(s, returnTo :: rest), returnTo)
|
||||
case _ => sys.error("Not currently in a plugin definition")
|
||||
case _ /* current */ :: returnTo :: rest => (setProjectReturn(s, returnTo :: rest), returnTo)
|
||||
case _ => sys.error("Not currently in a plugin definition")
|
||||
}
|
||||
|
||||
case Current =>
|
||||
val base = s.configuration.baseDirectory
|
||||
projectReturn(s) match { case Nil => (setProjectReturn(s, base :: Nil), base); case x :: xs => (s, x) }
|
||||
|
||||
case Plugins =>
|
||||
val (newBase, oldStack) = if (Project.isProjectLoaded(s))
|
||||
(Project.extract(s).currentUnit.unit.plugins.base, projectReturn(s))
|
||||
else // support changing to the definition project if it fails to load
|
||||
(BuildPaths.projectStandard(s.baseDir), s.baseDir :: Nil)
|
||||
val (newBase, oldStack) =
|
||||
if (Project.isProjectLoaded(s))
|
||||
(Project.extract(s).currentUnit.unit.plugins.base, projectReturn(s))
|
||||
else // support changing to the definition project if it fails to load
|
||||
(BuildPaths.projectStandard(s.baseDir), s.baseDir :: Nil)
|
||||
val newS = setProjectReturn(s, newBase :: oldStack)
|
||||
(newS, newBase)
|
||||
}
|
||||
|
|
@ -603,6 +613,7 @@ object Project extends ProjectExtra {
|
|||
val mfi = EvaluateTask.minForcegcInterval(extracted, extracted.structure)
|
||||
runTask(taskKey, state, EvaluateTaskConfig(r, checkCycles, p, ch, fgc, mfi))
|
||||
}
|
||||
|
||||
def runTask[T](taskKey: ScopedKey[Task[T]], state: State, config: EvaluateTaskConfig): Option[(State, Result[T])] = {
|
||||
val extracted = Project.extract(state)
|
||||
EvaluateTask(extracted.structure, taskKey, state, extracted.currentRef, config)
|
||||
|
|
|
|||
|
|
@ -2,47 +2,43 @@ package sbt
|
|||
|
||||
import java.lang.reflect.InvocationTargetException
|
||||
import java.io.File
|
||||
|
||||
import sbt.io._, syntax._
|
||||
import sbt.util._
|
||||
import sbt.internal.util._
|
||||
import sbt.internal.util.complete.{ DefaultParsers, Parser }, DefaultParsers._
|
||||
import xsbti.AppConfiguration
|
||||
import sbt.internal.inc.classpath.ClasspathUtilities
|
||||
import BasicCommandStrings._
|
||||
import BasicKeys._
|
||||
import complete.DefaultParsers
|
||||
import DefaultParsers._
|
||||
import Command.applyEffect
|
||||
import sbt.io._
|
||||
import sbt.io.syntax._
|
||||
import sbt.librarymanagement._
|
||||
import sbt.internal.librarymanagement.IvyConfiguration
|
||||
import sbt.internal.inc.classpath.ClasspathUtilities
|
||||
import BasicCommandStrings._, BasicKeys._
|
||||
|
||||
private[sbt] object TemplateCommandUtil {
|
||||
def templateCommand = Command.make(TemplateCommand, templateBrief, templateDetailed)(templateCommandParser)
|
||||
def templateCommandParser(state: State) =
|
||||
{
|
||||
val p = (token(Space) ~> repsep(StringBasic, token(Space))) | (token(EOF) map { case _ => Nil })
|
||||
val infos = (state get templateResolverInfos) match {
|
||||
case Some(infos) => infos.toList
|
||||
case None => Nil
|
||||
}
|
||||
val log = state.globalLogging.full
|
||||
val extracted = (Project extract state)
|
||||
val (s2, ivyConf) = extracted.runTask(Keys.ivyConfiguration, state)
|
||||
val globalBase = BuildPaths.getGlobalBase(state)
|
||||
val ivyScala = extracted.get(Keys.ivyScala in Keys.updateSbtClassifiers)
|
||||
applyEffect(p)({ inputArg =>
|
||||
val arguments = inputArg.toList ++
|
||||
(state.remainingCommands.toList match {
|
||||
case exec :: Nil if exec.commandLine == "shell" => Nil
|
||||
case xs => xs map { _.commandLine }
|
||||
})
|
||||
run(infos, arguments, state.configuration, ivyConf, globalBase, ivyScala, log)
|
||||
"exit" :: s2.copy(remainingCommands = Nil)
|
||||
})
|
||||
}
|
||||
def templateCommand: Command =
|
||||
Command(TemplateCommand, templateBrief, templateDetailed)(templateCommandParser)(runTemplate)
|
||||
|
||||
private def run(infos: List[TemplateResolverInfo], arguments: List[String], config: AppConfiguration,
|
||||
ivyConf: IvyConfiguration, globalBase: File, ivyScala: Option[IvyScala], log: Logger): Unit =
|
||||
private def templateCommandParser(state: State): Parser[Seq[String]] =
|
||||
(token(Space) ~> repsep(StringBasic, token(Space))) | (token(EOF) map (_ => Nil))
|
||||
|
||||
private def runTemplate(state: State, inputArg: Seq[String]): State = {
|
||||
val infos = (state get templateResolverInfos getOrElse Nil).toList
|
||||
val log = state.globalLogging.full
|
||||
val extracted = (Project extract state)
|
||||
val (s2, ivyConf) = extracted.runTask(Keys.ivyConfiguration, state)
|
||||
val globalBase = BuildPaths.getGlobalBase(state)
|
||||
val ivyScala = extracted.get(Keys.ivyScala in Keys.updateSbtClassifiers)
|
||||
val arguments = inputArg.toList ++
|
||||
(state.remainingCommands match {
|
||||
case exec :: Nil if exec.commandLine == "shell" => Nil
|
||||
case xs => xs map (_.commandLine)
|
||||
})
|
||||
run(infos, arguments, state.configuration, ivyConf, globalBase, ivyScala, log)
|
||||
"exit" :: s2.copy(remainingCommands = Nil)
|
||||
}
|
||||
|
||||
private def run(
|
||||
infos: List[TemplateResolverInfo], arguments: List[String], config: AppConfiguration,
|
||||
ivyConf: IvyConfiguration, globalBase: File, ivyScala: Option[IvyScala], log: Logger
|
||||
): Unit =
|
||||
infos find { info =>
|
||||
val loader = infoLoader(info, config, ivyConf, globalBase, ivyScala, log)
|
||||
val hit = tryTemplate(info, arguments, loader)
|
||||
|
|
@ -54,6 +50,7 @@ private[sbt] object TemplateCommandUtil {
|
|||
case Some(_) => // do nothing
|
||||
case None => System.err.println("Template not found for: " + arguments.mkString(" "))
|
||||
}
|
||||
|
||||
private def tryTemplate(info: TemplateResolverInfo, arguments: List[String], loader: ClassLoader): Boolean =
|
||||
{
|
||||
val resultObj = call(info.implementationClass, "isDefined", loader)(
|
||||
|
|
@ -61,12 +58,21 @@ private[sbt] object TemplateCommandUtil {
|
|||
)(arguments.toArray)
|
||||
resultObj.asInstanceOf[Boolean]
|
||||
}
|
||||
|
||||
private def runTemplate(info: TemplateResolverInfo, arguments: List[String], loader: ClassLoader): Unit =
|
||||
call(info.implementationClass, "run", loader)(classOf[Array[String]])(arguments.toArray)
|
||||
private def infoLoader(info: TemplateResolverInfo, config: AppConfiguration,
|
||||
ivyConf: IvyConfiguration, globalBase: File, ivyScala: Option[IvyScala], log: Logger): ClassLoader =
|
||||
ClasspathUtilities.toLoader(classpathForInfo(info, ivyConf, globalBase, ivyScala, log), config.provider.loader)
|
||||
private def call(interfaceClassName: String, methodName: String, loader: ClassLoader)(argTypes: Class[_]*)(args: AnyRef*): AnyRef =
|
||||
|
||||
private def infoLoader(
|
||||
info: TemplateResolverInfo, config: AppConfiguration, ivyConf: IvyConfiguration, globalBase: File,
|
||||
ivyScala: Option[IvyScala], log: Logger
|
||||
): ClassLoader = {
|
||||
val cp = classpathForInfo(info, ivyConf, globalBase, ivyScala, log)
|
||||
ClasspathUtilities.toLoader(cp, config.provider.loader)
|
||||
}
|
||||
|
||||
private def call(
|
||||
interfaceClassName: String, methodName: String, loader: ClassLoader
|
||||
)(argTypes: Class[_]*)(args: AnyRef*): AnyRef =
|
||||
{
|
||||
val interfaceClass = getInterfaceClass(interfaceClassName, loader)
|
||||
val interface = interfaceClass.getDeclaredConstructor().newInstance().asInstanceOf[AnyRef]
|
||||
|
|
@ -76,10 +82,14 @@ private[sbt] object TemplateCommandUtil {
|
|||
case e: InvocationTargetException => throw e.getCause
|
||||
}
|
||||
}
|
||||
|
||||
private def getInterfaceClass(name: String, loader: ClassLoader) = Class.forName(name, true, loader)
|
||||
|
||||
// Cache files under ~/.sbt/0.13/templates/org_name_version
|
||||
private def classpathForInfo(info: TemplateResolverInfo, ivyConf: IvyConfiguration, globalBase: File, ivyScala: Option[IvyScala], log: Logger): List[File] =
|
||||
private def classpathForInfo(
|
||||
info: TemplateResolverInfo, ivyConf: IvyConfiguration, globalBase: File, ivyScala: Option[IvyScala],
|
||||
log: Logger
|
||||
): List[File] =
|
||||
{
|
||||
val lm = new DefaultLibraryManagement(ivyConf, log)
|
||||
val templatesBaseDirectory = new File(globalBase, "templates")
|
||||
|
|
|
|||
|
|
@ -18,9 +18,9 @@ object CommandStrings {
|
|||
val BootCommand = "boot"
|
||||
|
||||
val EvalCommand = "eval"
|
||||
val evalBrief = (EvalCommand + " <expression>", "Evaluates a Scala expression and prints the result and type.")
|
||||
val evalBrief = (s"$EvalCommand <expression>", "Evaluates a Scala expression and prints the result and type.")
|
||||
val evalDetailed =
|
||||
EvalCommand + """ <expression>
|
||||
s"""$EvalCommand <expression>
|
||||
|
||||
Evaluates the given Scala expression and prints the result and type."""
|
||||
|
||||
|
|
@ -31,8 +31,8 @@ object CommandStrings {
|
|||
s"""$multiTaskSyntax
|
||||
|
||||
$multiTaskBrief"""
|
||||
def multiTaskSyntax = s"""$MultiTaskCommand <task>+"""
|
||||
def multiTaskBrief = """Executes all of the specified tasks concurrently."""
|
||||
def multiTaskSyntax = s"$MultiTaskCommand <task>+"
|
||||
def multiTaskBrief = "Executes all of the specified tasks concurrently."
|
||||
|
||||
def showHelp = Help(ShowCommand, (s"$ShowCommand <key>", showBrief), showDetailed)
|
||||
def showBrief = "Displays the result of evaluating the setting or task associated with 'key'."
|
||||
|
|
@ -57,26 +57,26 @@ $ShowCommand <task>
|
|||
|
||||
val lastGrepBrief = (LastGrepCommand, "Shows lines from the last output for 'key' that match 'pattern'.")
|
||||
val lastGrepDetailed =
|
||||
LastGrepCommand + """ <pattern>
|
||||
s"""$LastGrepCommand <pattern>
|
||||
Displays lines from the logging of previous commands that match `pattern`.
|
||||
|
||||
""" + LastGrepCommand + """ <pattern> [key]
|
||||
$LastGrepCommand <pattern> [key]
|
||||
Displays lines from logging associated with `key` that match `pattern`. The key typically refers to a task (for example, test:compile). The logging that is displayed is restricted to the logging for that particular task.
|
||||
|
||||
<pattern> is a regular expression interpreted by java.util.Pattern. Matching text is highlighted (when highlighting is supported and enabled).
|
||||
See also '""" + LastCommand + "'."
|
||||
See also '$LastCommand'."""
|
||||
|
||||
val lastBrief = (LastCommand, "Displays output from a previous command or the output from a specific task.")
|
||||
val lastDetailed =
|
||||
LastCommand + """
|
||||
s"""$LastCommand
|
||||
Prints the logging for the previous command, typically at a more verbose level.
|
||||
|
||||
""" + LastCommand + """ <key>
|
||||
$LastCommand <key>
|
||||
Prints the logging associated with the provided key. The key typically refers to a task (for example, test:compile). The logging that is displayed is restricted to the logging for that particular task.
|
||||
|
||||
See also '""" + LastGrepCommand + "'."
|
||||
See also '$LastGrepCommand'."""
|
||||
|
||||
val exportBrief = (ExportCommand + " <tasks>+", "Executes tasks and displays the equivalent command lines.")
|
||||
val exportBrief = (s"$ExportCommand <tasks>+", "Executes tasks and displays the equivalent command lines.")
|
||||
val exportDetailed =
|
||||
s"""$ExportCommand [--last] <task>+
|
||||
Runs the specified tasks and prints the equivalent command lines or other exportable information for those runs.
|
||||
|
|
@ -92,7 +92,7 @@ $ShowCommand <task>
|
|||
"""
|
||||
|
||||
val InspectCommand = "inspect"
|
||||
val inspectBrief = (InspectCommand + " [uses|tree|definitions] <key>", "Prints the value for 'key', the defining scope, delegates, related definitions, and dependencies.")
|
||||
val inspectBrief = (s"$InspectCommand [uses|tree|definitions] <key>", "Prints the value for 'key', the defining scope, delegates, related definitions, and dependencies.")
|
||||
val inspectDetailed = s"""
|
||||
|$InspectCommand <key>
|
||||
|
|
||||
|
|
@ -133,7 +133,7 @@ $ShowCommand <task>
|
|||
val SetCommand = "set"
|
||||
val setBrief = (s"$SetCommand [every] <setting>", "Evaluates a Setting and applies it to the current project.")
|
||||
val setDetailed =
|
||||
SetCommand + """ [every] <setting-expression>
|
||||
s"""$SetCommand [every] <setting-expression>
|
||||
|
||||
Applies the given setting to the current project:
|
||||
1) Constructs the expression provided as an argument by compiling and loading it.
|
||||
|
|
@ -150,68 +150,82 @@ $ShowCommand <task>
|
|||
"""
|
||||
|
||||
def SessionCommand = "session"
|
||||
def sessionBrief = (SessionCommand, "Manipulates session settings. For details, run 'help " + SessionCommand + "'.")
|
||||
|
||||
def sessionBrief =
|
||||
(SessionCommand, s"Manipulates session settings. For details, run 'help $SessionCommand'.")
|
||||
|
||||
def settingsPreamble = commonPreamble("settings")
|
||||
|
||||
def tasksPreamble = commonPreamble("tasks") + """
|
||||
Tasks produce values. Use the 'show' command to run the task and print the resulting value."""
|
||||
|
||||
def commonPreamble(label: String) = """
|
||||
This is a list of %s defined for the current project.
|
||||
It does not list the scopes the %<s are defined in; use the 'inspect' command for that.""".format(label)
|
||||
def commonPreamble(label: String) = s"""
|
||||
This is a list of $label defined for the current project.
|
||||
It does not list the scopes the $label are defined in; use the 'inspect' command for that."""
|
||||
|
||||
def settingsBrief(label: String) = (label, s"Lists the $label defined for the current project.")
|
||||
|
||||
import BasicCommandStrings.HelpCommand
|
||||
|
||||
def settingsBrief(label: String) = (label, "Lists the " + label + " defined for the current project.")
|
||||
def settingsDetailed(label: String) =
|
||||
"""
|
||||
s"""
|
||||
Syntax summary
|
||||
%s [-(v|-vv|...|-V)] [<filter>]
|
||||
$label [-(v|-vv|...|-V)] [<filter>]
|
||||
|
||||
%<s
|
||||
Displays the main %<s defined directly or indirectly for the current project.
|
||||
$label
|
||||
Displays the main $label defined directly or indirectly for the current project.
|
||||
|
||||
-v
|
||||
Displays additional %<s. More 'v's increase the number of %<s displayed.
|
||||
Displays additional $label. More 'v's increase the number of $label displayed.
|
||||
|
||||
-V
|
||||
displays all %<s
|
||||
displays all $label
|
||||
|
||||
<filter>
|
||||
Restricts the %<s that are displayed. The names of %<s are searched for an exact match against the filter, in which case only the description of the exact match is displayed. Otherwise, the filter is interpreted as a regular expression and all %<s whose name or description match the regular expression are displayed. Note that this is an additional filter on top of the %<s selected by the -v style switches, so you must specify -V to search all %<s. Use the %s command to search all commands, tasks, and settings at once.
|
||||
""".format(label, BasicCommandStrings.HelpCommand)
|
||||
Restricts the $label that are displayed. The names of $label are searched for
|
||||
an exact match against the filter, in which case only the description of the
|
||||
exact match is displayed. Otherwise, the filter is interpreted as a regular
|
||||
expression and all $label whose name or description match the regular
|
||||
expression are displayed. Note that this is an additional filter on top of
|
||||
the $label selected by the -v style switches, so you must specify -V to search
|
||||
all $label. Use the $HelpCommand command to search all commands, tasks, and
|
||||
settings at once.
|
||||
"""
|
||||
|
||||
def moreAvailableMessage(label: String, search: Boolean) =
|
||||
"More %s may be %s by increasing verbosity. See '%s %s'.\n".format(label, if (search) "searched" else "viewed", BasicCommandStrings.HelpCommand, label)
|
||||
def moreAvailableMessage(label: String, search: Boolean) = {
|
||||
val verb = if (search) "searched" else "viewed"
|
||||
s"More $label may be $verb by increasing verbosity. See '$HelpCommand $label'\n"
|
||||
}
|
||||
|
||||
def aboutBrief = "Displays basic information about sbt and the build."
|
||||
def aboutDetailed = aboutBrief
|
||||
|
||||
def projectBrief = (ProjectCommand, "Displays the current project or changes to the provided `project`.")
|
||||
def projectDetailed =
|
||||
ProjectCommand +
|
||||
"""
|
||||
s"""$ProjectCommand
|
||||
|
||||
Displays the name of the current project.
|
||||
|
||||
""" + ProjectCommand + """ name
|
||||
$ProjectCommand name
|
||||
|
||||
Changes to the project with the provided name.
|
||||
This command fails if there is no project with the given name.
|
||||
|
||||
""" + ProjectCommand + """ {uri}
|
||||
$ProjectCommand {uri}
|
||||
|
||||
Changes to the root project in the build defined by `uri`.
|
||||
`uri` must have already been declared as part of the build, such as with Project.dependsOn.
|
||||
|
||||
""" + ProjectCommand + """ {uri}name
|
||||
$ProjectCommand {uri}name
|
||||
|
||||
Changes to the project `name` in the build defined by `uri`.
|
||||
`uri` must have already been declared as part of the build, such as with Project.dependsOn.
|
||||
|
||||
""" + ProjectCommand + """ /
|
||||
$ProjectCommand /
|
||||
|
||||
Changes to the initial project.
|
||||
|
||||
""" + ProjectCommand + """ ..
|
||||
$ProjectCommand ..
|
||||
|
||||
Changes to the parent project of the current project.
|
||||
If there is no parent project, the current project is unchanged.
|
||||
|
|
@ -221,15 +235,15 @@ Syntax summary
|
|||
|
||||
def projectsBrief = "Lists the names of available projects or temporarily adds/removes extra builds to the session."
|
||||
def projectsDetailed =
|
||||
ProjectsCommand + """
|
||||
s"""$ProjectsCommand
|
||||
List the names of available builds and the projects defined in those builds.
|
||||
|
||||
""" + ProjectsCommand + """ add <URI>+
|
||||
$ProjectsCommand add <URI>+
|
||||
Adds the builds at the provided URIs to this session.
|
||||
These builds may be selected using the """ + ProjectCommand + """ command.
|
||||
These builds may be selected using the sProjectCommand command.
|
||||
Alternatively, tasks from these builds may be run using the explicit syntax {URI}project/task
|
||||
|
||||
""" + ProjectsCommand + """ remove <URI>+
|
||||
$ProjectsCommand remove <URI>+
|
||||
Removes extra builds from this session.
|
||||
Builds explicitly listed in the build definition are not affected by this command.
|
||||
"""
|
||||
|
|
@ -250,8 +264,8 @@ Syntax summary
|
|||
def LoadProjectImpl = "loadp"
|
||||
def LoadProject = "reload"
|
||||
def LoadProjectBrief = (LoadProject, "(Re)loads the current project or changes to plugins project or returns from it.")
|
||||
def LoadProjectDetailed = LoadProject +
|
||||
s"""
|
||||
def LoadProjectDetailed =
|
||||
s"""$LoadProject
|
||||
|
||||
\t(Re)loads the project in the current directory.
|
||||
|
||||
|
|
@ -266,7 +280,7 @@ $LoadProject return
|
|||
def InitCommand = "initialize"
|
||||
def InitBrief = (InitCommand, "Initializes command processing.")
|
||||
def InitDetailed =
|
||||
InitCommand + """
|
||||
s"""$InitCommand
|
||||
Initializes command processing.
|
||||
|
||||
Runs the following commands.
|
||||
|
|
|
|||
|
|
@ -242,16 +242,26 @@ private[sbt] object Load {
|
|||
}
|
||||
|
||||
// Reevaluates settings after modifying them. Does not recompile or reload any build components.
|
||||
def reapply(newSettings: Seq[Setting[_]], structure: BuildStructure)(implicit display: Show[ScopedKey[_]]): BuildStructure =
|
||||
def reapply(
|
||||
newSettings: Seq[Setting[_]], structure: BuildStructure
|
||||
)(implicit display: Show[ScopedKey[_]]): BuildStructure =
|
||||
{
|
||||
val transformed = finalTransforms(newSettings)
|
||||
val newData = Def.make(transformed)(structure.delegates, structure.scopeLocal, display)
|
||||
val newIndex = structureIndex(newData, transformed, index => BuildUtil(structure.root, structure.units, index, newData), structure.units)
|
||||
def extra(index: KeyIndex) = BuildUtil(structure.root, structure.units, index, newData)
|
||||
val newIndex = structureIndex(newData, transformed, extra, structure.units)
|
||||
val newStreams = mkStreams(structure.units, structure.root, newData)
|
||||
new BuildStructure(units = structure.units, root = structure.root, settings = transformed, data = newData, index = newIndex, streams = newStreams, delegates = structure.delegates, scopeLocal = structure.scopeLocal)
|
||||
new BuildStructure(
|
||||
units = structure.units, root = structure.root, settings = transformed, data = newData,
|
||||
index = newIndex, streams = newStreams, delegates = structure.delegates,
|
||||
scopeLocal = structure.scopeLocal)
|
||||
}
|
||||
|
||||
def isProjectThis(s: Setting[_]) = s.key.scope.project match { case This | Select(ThisProject) => true; case _ => false }
|
||||
def isProjectThis(s: Setting[_]): Boolean =
|
||||
s.key.scope.project match {
|
||||
case This | Select(ThisProject) => true
|
||||
case _ => false
|
||||
}
|
||||
|
||||
def buildConfigurations(loaded: LoadedBuild, rootProject: URI => String, injectSettings: InjectSettings): Seq[Setting[_]] =
|
||||
{
|
||||
|
|
@ -291,7 +301,8 @@ private[sbt] object Load {
|
|||
() => eval
|
||||
}
|
||||
|
||||
def mkEval(unit: BuildUnit): Eval = mkEval(unit.definitions, unit.plugins, unit.plugins.pluginData.scalacOptions)
|
||||
def mkEval(unit: BuildUnit): Eval =
|
||||
mkEval(unit.definitions, unit.plugins, unit.plugins.pluginData.scalacOptions)
|
||||
|
||||
def mkEval(defs: LoadedDefinitions, plugs: LoadedPlugins, options: Seq[String]): Eval =
|
||||
mkEval(defs.target ++ plugs.classpath, defs.base, options)
|
||||
|
|
@ -335,7 +346,8 @@ private[sbt] object Load {
|
|||
BuildLoader(components, fail, s, config)
|
||||
}
|
||||
|
||||
def load(file: File, loaders: BuildLoader, extra: List[URI]): PartBuild = loadURI(IO.directoryURI(file), loaders, extra)
|
||||
def load(file: File, loaders: BuildLoader, extra: List[URI]): PartBuild =
|
||||
loadURI(IO.directoryURI(file), loaders, extra)
|
||||
|
||||
def loadURI(uri: URI, loaders: BuildLoader, extra: List[URI]): PartBuild =
|
||||
{
|
||||
|
|
|
|||
|
|
@ -9,7 +9,11 @@ import Plugins._
|
|||
import PluginsDebug._
|
||||
import java.net.URI
|
||||
|
||||
private[sbt] class PluginsDebug(val available: List[AutoPlugin], val nameToKey: Map[String, AttributeKey[_]], val provided: Relation[AutoPlugin, AttributeKey[_]]) {
|
||||
private[sbt] class PluginsDebug(
|
||||
val available: List[AutoPlugin],
|
||||
val nameToKey: Map[String, AttributeKey[_]],
|
||||
val provided: Relation[AutoPlugin, AttributeKey[_]]
|
||||
) {
|
||||
/**
|
||||
* The set of [[AutoPlugin]]s that might define a key named `keyName`.
|
||||
* Because plugins can define keys in different scopes, this should only be used as a guideline.
|
||||
|
|
@ -18,20 +22,23 @@ private[sbt] class PluginsDebug(val available: List[AutoPlugin], val nameToKey:
|
|||
case None => Set.empty
|
||||
case Some(key) => provided.reverse(key)
|
||||
}
|
||||
/** Describes alternative approaches for defining key [[keyName]] in [[context]].*/
|
||||
|
||||
/** Describes alternative approaches for defining key `keyName` in [[Context]]. */
|
||||
def toEnable(keyName: String, context: Context): List[PluginEnable] =
|
||||
providers(keyName).toList.map(plugin => pluginEnable(context, plugin))
|
||||
|
||||
/** Provides text to suggest how [[notFoundKey]] can be defined in [[context]]. */
|
||||
/** Provides text to suggest how `notFoundKey` can be defined in [[Context]]. */
|
||||
def debug(notFoundKey: String, context: Context): String =
|
||||
{
|
||||
val (activated, deactivated) = Util.separate(toEnable(notFoundKey, context)) {
|
||||
case pa: PluginActivated => Left(pa)
|
||||
case pd: EnableDeactivated => Right(pd)
|
||||
}
|
||||
val activePrefix = if (activated.nonEmpty) s"Some already activated plugins define $notFoundKey: ${activated.mkString(", ")}\n" else ""
|
||||
val activePrefix = if (activated.isEmpty) "" else
|
||||
s"Some already activated plugins define $notFoundKey: ${activated.mkString(", ")}\n"
|
||||
activePrefix + debugDeactivated(notFoundKey, deactivated)
|
||||
}
|
||||
|
||||
private[this] def debugDeactivated(notFoundKey: String, deactivated: Seq[EnableDeactivated]): String =
|
||||
{
|
||||
val (impossible, possible) = Util.separate(deactivated) {
|
||||
|
|
@ -41,23 +48,27 @@ private[sbt] class PluginsDebug(val available: List[AutoPlugin], val nameToKey:
|
|||
if (possible.nonEmpty) {
|
||||
val explained = possible.map(explainPluginEnable)
|
||||
val possibleString =
|
||||
if (explained.size > 1) explained.zipWithIndex.map { case (s, i) => s"$i. $s" }.mkString(s"Multiple plugins are available that can provide $notFoundKey:\n", "\n", "")
|
||||
if (explained.size > 1) explained.zipWithIndex.map { case (s, i) => s"$i. $s" }
|
||||
.mkString(s"Multiple plugins are available that can provide $notFoundKey:\n", "\n", "")
|
||||
else s"$notFoundKey is provided by an available (but not activated) plugin:\n${explained.mkString}"
|
||||
def impossiblePlugins = impossible.map(_.plugin.label).mkString(", ")
|
||||
val imPostfix = if (impossible.isEmpty) "" else s"\n\nThere are other available plugins that provide $notFoundKey, but they are impossible to add: $impossiblePlugins"
|
||||
val imPostfix = if (impossible
|
||||
.isEmpty) "" else s"\n\nThere are other available plugins that provide $notFoundKey, but they are " +
|
||||
s"impossible to add: $impossiblePlugins"
|
||||
possibleString + imPostfix
|
||||
} else if (impossible.isEmpty)
|
||||
s"No available plugin provides key $notFoundKey."
|
||||
else {
|
||||
val explanations = impossible.map(explainPluginEnable)
|
||||
explanations.mkString(s"Plugins are available that could provide $notFoundKey, but they are impossible to add:\n\t", "\n\t", "")
|
||||
val preamble = s"Plugins are available that could provide $notFoundKey"
|
||||
explanations.mkString(s"$preamble, but they are impossible to add:\n\t", "\n\t", "")
|
||||
}
|
||||
}
|
||||
|
||||
/** Text that suggests how to activate [[plugin]] in [[context]] if possible and if it is not already activated.*/
|
||||
/** Text that suggests how to activate [[AutoPlugin]] in [[Context]] if possible and if it is not already activated. */
|
||||
def help(plugin: AutoPlugin, context: Context): String =
|
||||
if (context.enabled.contains(plugin)) activatedHelp(plugin)
|
||||
else deactivatedHelp(plugin, context)
|
||||
if (context.enabled.contains(plugin)) activatedHelp(plugin) else deactivatedHelp(plugin, context)
|
||||
|
||||
private def activatedHelp(plugin: AutoPlugin): String =
|
||||
{
|
||||
val prefix = s"${plugin.label} is activated."
|
||||
|
|
@ -67,6 +78,7 @@ private[sbt] class PluginsDebug(val available: List[AutoPlugin], val nameToKey:
|
|||
val confsString = if (configs.isEmpty) "" else s"\nIt defines these configurations: ${multi(configs.map(_.name))}"
|
||||
prefix + keysString + confsString
|
||||
}
|
||||
|
||||
private def deactivatedHelp(plugin: AutoPlugin, context: Context): String =
|
||||
{
|
||||
val prefix = s"${plugin.label} is NOT activated."
|
||||
|
|
@ -105,6 +117,7 @@ private[sbt] object PluginsDebug {
|
|||
import extracted._
|
||||
structure.units.values.toList.flatMap(availableAutoPlugins).map(plugin => (plugin.label, plugin)).toMap
|
||||
}
|
||||
|
||||
private[this] def availableAutoPlugins(build: LoadedBuildUnit): Seq[AutoPlugin] =
|
||||
build.unit.plugins.detected.autoPlugins map { _.value }
|
||||
|
||||
|
|
@ -120,7 +133,10 @@ private[sbt] object PluginsDebug {
|
|||
lazy val debug = PluginsDebug(context.available)
|
||||
if (!pluginsThisBuild.contains(plugin)) {
|
||||
val availableInBuilds: List[URI] = perBuild.toList.filter(_._2(plugin)).map(_._1)
|
||||
s"Plugin ${plugin.label} is only available in builds:\n\t${availableInBuilds.mkString("\n\t")}\nSwitch to a project in one of those builds using `project` and rerun this command for more information."
|
||||
val s1 = s"Plugin ${plugin.label} is only available in builds:"
|
||||
val s2 = availableInBuilds.mkString("\n\t")
|
||||
val s3 = s"Switch to a project in one of those builds using `project` and rerun this command for more information."
|
||||
s"$s1\n\t$s2\n$s3"
|
||||
} else if (definesPlugin(currentProject))
|
||||
debug.activatedHelp(plugin)
|
||||
else {
|
||||
|
|
@ -128,7 +144,8 @@ private[sbt] object PluginsDebug {
|
|||
val definedInAggregated = thisAggregated.filter(ref => definesPlugin(projectForRef(ref)))
|
||||
if (definedInAggregated.nonEmpty) {
|
||||
val projectNames = definedInAggregated.map(_.project) // TODO: usually in this build, but could technically require the build to be qualified
|
||||
s"Plugin ${plugin.label} is not activated on this project, but this project aggregates projects where it is activated:\n\t${projectNames.mkString("\n\t")}"
|
||||
val s2 = projectNames.mkString("\n\t")
|
||||
s"Plugin ${plugin.label} is not activated on this project, but this project aggregates projects where it is activated:\n\t$s2"
|
||||
} else {
|
||||
val base = debug.deactivatedHelp(plugin, context)
|
||||
val aggNote = if (thisAggregated.nonEmpty) "Note: This project aggregates other projects and this" else "Note: This"
|
||||
|
|
@ -139,7 +156,7 @@ private[sbt] object PluginsDebug {
|
|||
}
|
||||
}
|
||||
|
||||
/** Precomputes information for debugging plugins. */
|
||||
/** Pre-computes information for debugging plugins. */
|
||||
def apply(available: List[AutoPlugin]): PluginsDebug =
|
||||
{
|
||||
val keyR = definedKeys(available)
|
||||
|
|
@ -154,36 +171,58 @@ private[sbt] object PluginsDebug {
|
|||
* @param deducePlugin The function used to compute the model.
|
||||
* @param available All [[AutoPlugin]]s available for consideration.
|
||||
*/
|
||||
final case class Context(initial: Plugins, enabled: Seq[AutoPlugin], deducePlugin: (Plugins, Logger) => Seq[AutoPlugin], available: List[AutoPlugin], log: Logger)
|
||||
final case class Context(
|
||||
initial: Plugins,
|
||||
enabled: Seq[AutoPlugin],
|
||||
deducePlugin: (Plugins, Logger) => Seq[AutoPlugin],
|
||||
available: List[AutoPlugin],
|
||||
log: Logger
|
||||
)
|
||||
|
||||
/** Describes the steps to activate a plugin in some context. */
|
||||
sealed abstract class PluginEnable
|
||||
|
||||
/** Describes a [[plugin]] that is already activated in the [[context]].*/
|
||||
final case class PluginActivated(plugin: AutoPlugin, context: Context) extends PluginEnable
|
||||
|
||||
sealed abstract class EnableDeactivated extends PluginEnable
|
||||
|
||||
/** Describes a [[plugin]] that cannot be activated in a [[context]] due to [[contradictions]] in requirements. */
|
||||
final case class PluginImpossible(plugin: AutoPlugin, context: Context, contradictions: Set[AutoPlugin]) extends EnableDeactivated
|
||||
final case class PluginImpossible(plugin: AutoPlugin, context: Context, contradictions: Set[AutoPlugin])
|
||||
extends EnableDeactivated
|
||||
|
||||
/**
|
||||
* Describes the requirements for activating [[plugin]] in [[context]].
|
||||
* @param context The base plugins, exclusions, and ultimately activated plugins
|
||||
* @param blockingExcludes Existing exclusions that prevent [[plugin]] from being activated and must be dropped
|
||||
* @param enablingPlugins [[AutoPlugin]]s that are not currently enabled, but need to be enabled for [[plugin]] to activate
|
||||
* @param extraEnabledPlugins Plugins that will be enabled as a result of [[plugin]] activating, but are not required for [[plugin]] to activate
|
||||
* @param enablingPlugins [[AutoPlugin]]s that are not currently enabled,
|
||||
* but need to be enabled for [[plugin]] to activate
|
||||
* @param extraEnabledPlugins Plugins that will be enabled as a result of [[plugin]] activating,
|
||||
* but are not required for [[plugin]] to activate
|
||||
* @param willRemove Plugins that will be deactivated as a result of [[plugin]] activating
|
||||
* @param deactivate Describes plugins that must be deactivated for [[plugin]] to activate. These require an explicit exclusion or dropping a transitive [[AutoPlugin]].
|
||||
* @param deactivate Describes plugins that must be deactivated for [[plugin]] to activate.
|
||||
* These require an explicit exclusion or dropping a transitive [[AutoPlugin]].
|
||||
*/
|
||||
final case class PluginRequirements(plugin: AutoPlugin, context: Context, blockingExcludes: Set[AutoPlugin], enablingPlugins: Set[AutoPlugin], extraEnabledPlugins: Set[AutoPlugin], willRemove: Set[AutoPlugin], deactivate: List[DeactivatePlugin]) extends EnableDeactivated
|
||||
final case class PluginRequirements(
|
||||
plugin: AutoPlugin,
|
||||
context: Context,
|
||||
blockingExcludes: Set[AutoPlugin],
|
||||
enablingPlugins: Set[AutoPlugin],
|
||||
extraEnabledPlugins: Set[AutoPlugin],
|
||||
willRemove: Set[AutoPlugin],
|
||||
deactivate: List[DeactivatePlugin]
|
||||
) extends EnableDeactivated
|
||||
|
||||
/**
|
||||
* Describes a [[plugin]] that must be removed in order to activate another plugin in some context.
|
||||
* The [[plugin]] can always be directly, explicitly excluded.
|
||||
* @param removeOneOf If non-empty, removing one of these [[AutoPlugin]]s will deactivate [[plugin]] without affecting the other plugin. If empty, a direct exclusion is required.
|
||||
* @param removeOneOf If non-empty, removing one of these [[AutoPlugin]]s will deactivate [[plugin]] without
|
||||
* affecting the other plugin. If empty, a direct exclusion is required.
|
||||
* @param newlySelected If false, this plugin was selected in the original context.
|
||||
*/
|
||||
final case class DeactivatePlugin(plugin: AutoPlugin, removeOneOf: Set[AutoPlugin], newlySelected: Boolean)
|
||||
|
||||
/** Determines how to enable [[plugin]] in [[context]]. */
|
||||
/** Determines how to enable [[AutoPlugin]] in [[Context]]. */
|
||||
def pluginEnable(context: Context, plugin: AutoPlugin): PluginEnable =
|
||||
if (context.enabled.contains(plugin))
|
||||
PluginActivated(plugin, context)
|
||||
|
|
@ -237,12 +276,15 @@ private[sbt] object PluginsDebug {
|
|||
// The model that results when the minimal plugins are enabled and the minimal plugins are excluded.
|
||||
// This can include more plugins than just `minRequiredPlugins` because the plugins required for `plugin`
|
||||
// might activate other plugins as well.
|
||||
val incrementalInputs = and(includeAll(minRequiredPlugins ++ initialPlugins), excludeAll(minAbsentPlugins ++ initialExcludes -- minRequiredPlugins))
|
||||
val incrementalInputs = and(
|
||||
includeAll(minRequiredPlugins ++ initialPlugins),
|
||||
excludeAll(minAbsentPlugins ++ initialExcludes -- minRequiredPlugins)
|
||||
)
|
||||
val incrementalModel = context.deducePlugin(incrementalInputs, context.log).toSet
|
||||
|
||||
// Plugins that are newly enabled as a result of selecting the plugins needed for `plugin`, but aren't strictly required for `plugin`.
|
||||
// These could be excluded and `plugin` and the user's current plugins would still be activated.
|
||||
val extraPlugins = incrementalModel.toSet -- minRequiredPlugins -- initialModel
|
||||
val extraPlugins = incrementalModel -- minRequiredPlugins -- initialModel
|
||||
|
||||
// Plugins that will no longer be enabled as a result of enabling `plugin`.
|
||||
val willRemove = initialModel -- incrementalModel
|
||||
|
|
@ -318,8 +360,10 @@ private[sbt] object PluginsDebug {
|
|||
|
||||
private[this] def excludedPluginError(transitive: Boolean)(dependency: AutoPlugin) =
|
||||
s"Required ${transitiveString(transitive)}dependency ${dependency.label} was excluded."
|
||||
|
||||
private[this] def excludedPluginsError(transitive: Boolean)(dependencies: List[AutoPlugin]) =
|
||||
s"Required ${transitiveString(transitive)}dependencies were excluded:\n\t${labels(dependencies).mkString("\n\t")}"
|
||||
|
||||
private[this] def transitiveString(transitive: Boolean) =
|
||||
if (transitive) "(transitive) " else ""
|
||||
|
||||
|
|
@ -328,6 +372,7 @@ private[sbt] object PluginsDebug {
|
|||
|
||||
private[this] def requiredPlugin(plugin: AutoPlugin) =
|
||||
s"Required plugin ${plugin.label} not present."
|
||||
|
||||
private[this] def requiredPlugins(plugins: List[AutoPlugin]) =
|
||||
s"Required plugins not present:\n\t${plugins.map(_.label).mkString("\n\t")}"
|
||||
|
||||
|
|
@ -343,6 +388,7 @@ private[sbt] object PluginsDebug {
|
|||
|
||||
private[this] def willAddPlugin(base: AutoPlugin)(plugin: AutoPlugin) =
|
||||
s"Enabling ${base.label} will also enable ${plugin.label}"
|
||||
|
||||
private[this] def willAddPlugins(base: AutoPlugin)(plugins: List[AutoPlugin]) =
|
||||
s"Enabling ${base.label} will also enable:\n\t${labels(plugins).mkString("\n\t")}"
|
||||
|
||||
|
|
@ -351,6 +397,7 @@ private[sbt] object PluginsDebug {
|
|||
|
||||
private[this] def willRemovePlugin(base: AutoPlugin)(plugin: AutoPlugin) =
|
||||
s"Enabling ${base.label} will disable ${plugin.label}"
|
||||
|
||||
private[this] def willRemovePlugins(base: AutoPlugin)(plugins: List[AutoPlugin]) =
|
||||
s"Enabling ${base.label} will disable:\n\t${labels(plugins).mkString("\n\t")}"
|
||||
|
||||
|
|
@ -359,10 +406,13 @@ private[sbt] object PluginsDebug {
|
|||
|
||||
private[this] def needToDeactivate(deactivate: List[DeactivatePlugin]): String =
|
||||
str(deactivate)(deactivate1, deactivateN)
|
||||
|
||||
private[this] def deactivateN(plugins: List[DeactivatePlugin]): String =
|
||||
plugins.map(deactivateString).mkString("These plugins need to be deactivated:\n\t", "\n\t", "")
|
||||
|
||||
private[this] def deactivate1(deactivate: DeactivatePlugin): String =
|
||||
s"Need to deactivate ${deactivateString(deactivate)}"
|
||||
|
||||
private[this] def deactivateString(d: DeactivatePlugin): String =
|
||||
{
|
||||
val removePluginsString: String =
|
||||
|
|
@ -377,8 +427,17 @@ private[sbt] object PluginsDebug {
|
|||
private[this] def pluginImpossible(plugin: AutoPlugin, contradictions: Set[AutoPlugin]): String =
|
||||
str(contradictions.toList)(pluginImpossible1(plugin), pluginImpossibleN(plugin))
|
||||
|
||||
private[this] def pluginImpossible1(plugin: AutoPlugin)(contradiction: AutoPlugin): String =
|
||||
s"There is no way to enable plugin ${plugin.label}. It (or its dependencies) requires plugin ${contradiction.label} to both be present and absent. Please report the problem to the plugin's author."
|
||||
private[this] def pluginImpossibleN(plugin: AutoPlugin)(contradictions: List[AutoPlugin]): String =
|
||||
s"There is no way to enable plugin ${plugin.label}. It (or its dependencies) requires these plugins to be both present and absent:\n\t${labels(contradictions).mkString("\n\t")}\nPlease report the problem to the plugin's author."
|
||||
private[this] def pluginImpossible1(plugin: AutoPlugin)(contradiction: AutoPlugin): String = {
|
||||
val s1 = s"There is no way to enable plugin ${plugin.label}."
|
||||
val s2 = s"It (or its dependencies) requires plugin ${contradiction.label} to both be present and absent."
|
||||
val s3 = s"Please report the problem to the plugin's author."
|
||||
s"$s1 $s2 $s3"
|
||||
}
|
||||
|
||||
private[this] def pluginImpossibleN(plugin: AutoPlugin)(contradictions: List[AutoPlugin]): String = {
|
||||
val s1 = s"There is no way to enable plugin ${plugin.label}."
|
||||
val s2 = s"It (or its dependencies) requires these plugins to be both present and absent:"
|
||||
val s3 = s"Please report the problem to the plugin's author."
|
||||
s"$s1 $s2:\n\t${labels(contradictions).mkString("\n\t")}\n$s3"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,29 +4,29 @@
|
|||
package sbt
|
||||
package internal
|
||||
|
||||
import sbt.internal.util.complete
|
||||
|
||||
import Project.updateCurrent
|
||||
import Keys.sessionSettings
|
||||
import complete.{ DefaultParsers, Parser }
|
||||
import DefaultParsers._
|
||||
import java.net.URI
|
||||
import sbt.internal.util.complete, complete.{ DefaultParsers, Parser }, DefaultParsers._
|
||||
import sbt.compiler.Eval
|
||||
import Keys.sessionSettings
|
||||
import Project.updateCurrent
|
||||
|
||||
object ProjectNavigation {
|
||||
def command(s: State): Parser[() => State] =
|
||||
if (s get sessionSettings isEmpty) failure("No project loaded") else (new ProjectNavigation(s)).command
|
||||
}
|
||||
|
||||
final class ProjectNavigation(s: State) {
|
||||
val extracted = Project extract s
|
||||
val extracted: Extracted = Project extract s
|
||||
import extracted.{ currentRef, structure, session }
|
||||
|
||||
def setProject(nuri: URI, nid: String) =
|
||||
def setProject(nuri: URI, nid: String): State =
|
||||
{
|
||||
val neval = if (currentRef.build == nuri) session.currentEval else mkEval(nuri)
|
||||
updateCurrent(s.put(sessionSettings, session.setCurrent(nuri, nid, neval)))
|
||||
}
|
||||
def mkEval(nuri: URI) = Load.lazyEval(structure.units(nuri).unit)
|
||||
def getRoot(uri: URI) = Load.getRootProject(structure.units)(uri)
|
||||
|
||||
def mkEval(nuri: URI): () => Eval = Load.lazyEval(structure.units(nuri).unit)
|
||||
def getRoot(uri: URI): String = Load.getRootProject(structure.units)(uri)
|
||||
|
||||
def apply(action: Option[ResolvedReference]): State =
|
||||
action match {
|
||||
|
|
@ -38,12 +38,13 @@ final class ProjectNavigation(s: State) {
|
|||
if(to.length > 1) gotoParent(to.length - 1, nav, s) else s */ // semantics currently undefined
|
||||
}
|
||||
|
||||
def show(): Unit = s.log.info(currentRef.project + " (in build " + currentRef.build + ")")
|
||||
def show(): Unit = s.log.info(s"${currentRef.project} (in build ${currentRef.build})")
|
||||
|
||||
def selectProject(uri: URI, to: String): State =
|
||||
if (structure.units(uri).defined.contains(to))
|
||||
setProject(uri, to)
|
||||
else
|
||||
fail("Invalid project name '" + to + "' in build " + uri + " (type 'projects' to list available projects).")
|
||||
fail(s"Invalid project name '$to' in build $uri (type 'projects' to list available projects).")
|
||||
|
||||
def changeBuild(newBuild: URI): State =
|
||||
if (structure.units contains newBuild)
|
||||
|
|
@ -51,14 +52,9 @@ final class ProjectNavigation(s: State) {
|
|||
else
|
||||
fail("Invalid build unit '" + newBuild + "' (type 'projects' to list available builds).")
|
||||
|
||||
def fail(msg: String): State =
|
||||
{
|
||||
s.log.error(msg)
|
||||
s.fail
|
||||
}
|
||||
def fail(msg: String): State = { s.log.error(msg); s.fail }
|
||||
|
||||
import complete.Parser._
|
||||
import complete.Parsers._
|
||||
import Parser._, complete.Parsers._
|
||||
|
||||
val parser: Parser[Option[ResolvedReference]] =
|
||||
{
|
||||
|
|
@ -66,6 +62,8 @@ final class ProjectNavigation(s: State) {
|
|||
val root = token('/' ^^^ rootRef)
|
||||
success(None) | some(token(Space) ~> (root | reference))
|
||||
}
|
||||
|
||||
def rootRef = ProjectRef(currentRef.build, getRoot(currentRef.build))
|
||||
|
||||
val command: Parser[() => State] = Command.applyEffect(parser)(apply)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,21 +20,24 @@ import sbt.io.IO
|
|||
/**
|
||||
* Represents (potentially) transient settings added into a build via commands/user.
|
||||
*
|
||||
* @param currentBuild
|
||||
* The current sbt build with which we scope new settings
|
||||
* @param currentProject
|
||||
* The current project with which we scope new settings.
|
||||
* @param original
|
||||
* The original list of settings for this build.
|
||||
* @param append
|
||||
* Settings which have been defined and appended that may ALSO be saved to disk.
|
||||
* @param rawAppend
|
||||
* Settings which have been defined and appended which CANNOT be saved to disk
|
||||
* @param currentEval
|
||||
* A compiler we can use to compile new setting strings.
|
||||
* @param currentBuild The current sbt build with which we scope new settings
|
||||
* @param currentProject The current project with which we scope new settings.
|
||||
* @param original The original list of settings for this build.
|
||||
* @param append Settings which have been defined and appended that may ALSO be saved to disk.
|
||||
* @param rawAppend Settings which have been defined and appended which CANNOT be saved to disk
|
||||
* @param currentEval A compiler we can use to compile new setting strings.
|
||||
*/
|
||||
final case class SessionSettings(currentBuild: URI, currentProject: Map[URI, String], original: Seq[Setting[_]], append: SessionMap, rawAppend: Seq[Setting[_]], currentEval: () => Eval) {
|
||||
assert(currentProject contains currentBuild, "Current build (" + currentBuild + ") not associated with a current project.")
|
||||
final case class SessionSettings(
|
||||
currentBuild: URI,
|
||||
currentProject: Map[URI, String],
|
||||
original: Seq[Setting[_]],
|
||||
append: SessionMap,
|
||||
rawAppend: Seq[Setting[_]],
|
||||
currentEval: () => Eval
|
||||
) {
|
||||
|
||||
assert(currentProject contains currentBuild,
|
||||
s"Current build ($currentBuild) not associated with a current project.")
|
||||
|
||||
/**
|
||||
* Modifiy the current state.
|
||||
|
|
@ -44,7 +47,8 @@ final case class SessionSettings(currentBuild: URI, currentProject: Map[URI, Str
|
|||
* @param eval The mechanism to compile new settings.
|
||||
* @return A new SessionSettings object
|
||||
*/
|
||||
def setCurrent(build: URI, project: String, eval: () => Eval): SessionSettings = copy(currentBuild = build, currentProject = currentProject.updated(build, project), currentEval = eval)
|
||||
def setCurrent(build: URI, project: String, eval: () => Eval): SessionSettings =
|
||||
copy(currentBuild = build, currentProject = currentProject.updated(build, project), currentEval = eval)
|
||||
|
||||
/**
|
||||
* @return The current ProjectRef with which we scope settings.
|
||||
|
|
@ -86,6 +90,7 @@ final case class SessionSettings(currentBuild: URI, currentProject: Map[URI, Str
|
|||
object SessionSettings {
|
||||
/** A session setting is simply a tuple of a Setting[_] and the strings which define it. */
|
||||
type SessionSetting = (Setting[_], Seq[String])
|
||||
|
||||
type SessionMap = Map[ProjectRef, Seq[SessionSetting]]
|
||||
type SbtConfigFile = (File, Seq[String])
|
||||
|
||||
|
|
@ -305,7 +310,7 @@ save, save-all
|
|||
def range: Parser[(Int, Int)] = (NatBasic ~ ('-' ~> NatBasic).?).map { case lo ~ hi => (lo, hi getOrElse lo) }
|
||||
|
||||
/** The raw implementation of the session command. */
|
||||
def command(s: State) = Command.applyEffect(parser) {
|
||||
def command(s: State): Parser[() => State] = Command.applyEffect(parser) {
|
||||
case p: Print => if (p.all) printAllSettings(s) else printSettings(s)
|
||||
case v: Save => if (v.all) saveAllSettings(s) else saveSettings(s)
|
||||
case c: Clear => if (c.all) clearAllSettings(s) else clearSettings(s)
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ private[sbt] object SettingCompletions {
|
|||
val full = for {
|
||||
defineKey <- scopedKeyParser(keyMap, settings, context)
|
||||
a <- assign(defineKey)
|
||||
deps <- valueParser(defineKey, a, inputScopedKey(keyFilter(defineKey.key)))
|
||||
_ <- valueParser(defineKey, a, inputScopedKey(keyFilter(defineKey.key)))
|
||||
} yield () // parser is currently only for completion and the parsed data structures are not used
|
||||
|
||||
matched(full) | any.+.string
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ private[sbt] case class SbtParser(file: File, lines: Seq[String]) extends Parsed
|
|||
case _ => false
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* See BugInParser
|
||||
* @param t - tree
|
||||
* @param originalStatement - original
|
||||
|
|
|
|||
Loading…
Reference in New Issue