diff options
author | Lukas Rytz <lukas.rytz@gmail.com> | 2014-09-02 16:42:22 +0200 |
---|---|---|
committer | Lukas Rytz <lukas.rytz@gmail.com> | 2014-09-02 16:42:22 +0200 |
commit | a08be99fd587f341b35e59603908376be45e153c (patch) | |
tree | 35df87d95d9431765143ee196bbf3c098723860b /src | |
parent | ad930e663046bb9973b664e8cb92218a41313ab8 (diff) | |
parent | 47908f19e064151140e32819c2edd9e68b34dd0c (diff) | |
download | scala-a08be99fd587f341b35e59603908376be45e153c.tar.gz scala-a08be99fd587f341b35e59603908376be45e153c.tar.bz2 scala-a08be99fd587f341b35e59603908376be45e153c.zip |
Merge commit '47908f1' into merge/2.11-to-2.12-is-it-really-sept-2-already-where-was-summer
Conflicts:
src/library/scala/util/matching/Regex.scala
Diffstat (limited to 'src')
94 files changed, 902 insertions, 8371 deletions
diff --git a/src/compiler/scala/tools/nsc/CompilationUnits.scala b/src/compiler/scala/tools/nsc/CompilationUnits.scala index f23bca77cd..0a356ed7b6 100644 --- a/src/compiler/scala/tools/nsc/CompilationUnits.scala +++ b/src/compiler/scala/tools/nsc/CompilationUnits.scala @@ -135,11 +135,8 @@ trait CompilationUnits { global: Global => @deprecated("Call global.currentRun.reporting.uncheckedWarning directly instead.", "2.11.2") final def uncheckedWarning(pos: Position, msg: String): Unit = currentRun.reporting.uncheckedWarning(pos, msg) - // called by ScalaDocAnalyzer, overridden by the IDE (in Reporter) - // TODO: don't use reporter to communicate comments from parser to IDE! - @deprecated("This method will be removed.", "2.11.2") - final def comment(pos: Position, msg: String): Unit = reporter.comment(pos, msg) - + @deprecated("This method will be removed. It does nothing.", "2.11.2") + final def comment(pos: Position, msg: String): Unit = {} /** Is this about a .java source file? */ lazy val isJava = source.file.name.endsWith(".java") diff --git a/src/compiler/scala/tools/nsc/CompileServer.scala b/src/compiler/scala/tools/nsc/CompileServer.scala index 1f3a4237eb..029e1c4629 100644 --- a/src/compiler/scala/tools/nsc/CompileServer.scala +++ b/src/compiler/scala/tools/nsc/CompileServer.scala @@ -152,6 +152,7 @@ class StandardCompileServer extends SocketServer { clearCompiler() case ex: Throwable => warn("Compile server encountered fatal condition: " + ex) + reporter.error(null, "Compile server encountered fatal condition: " + ex.getMessage) shutdown = true throw ex } diff --git a/src/compiler/scala/tools/nsc/CompileSocket.scala b/src/compiler/scala/tools/nsc/CompileSocket.scala index c4f06b59ec..c693fbe8e2 100644 --- a/src/compiler/scala/tools/nsc/CompileSocket.scala +++ b/src/compiler/scala/tools/nsc/CompileSocket.scala @@ -32,7 +32,8 @@ trait HasCompileSocket { if (isErrorMessage(line)) noErrors = false - compileSocket.echo(line) + // be consistent with scalac: everything goes to stderr + compileSocket.warn(line) loop() } try loop() diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 5bf0d8d9f7..3e72dc5e4a 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -45,7 +45,8 @@ class Global(var currentSettings: Settings, var reporter: Reporter) with Printers with DocComments with Positions - with Reporting { self => + with Reporting + with Parsing { self => // the mirror -------------------------------------------------- @@ -218,6 +219,14 @@ class Global(var currentSettings: Settings, var reporter: Reporter) /** Called from parser, which signals hereby that a method definition has been parsed. */ def signalParseProgress(pos: Position) {} + /** Called by ScalaDocAnalyzer when a doc comment has been parsed. */ + def signalParsedDocComment(comment: String, pos: Position) = { + // TODO: this is all very borken (only works for scaladoc comments, not regular ones) + // --> add hooks to parser and refactor Interactive global to handle comments directly + // in any case don't use reporter for parser hooks + reporter.comment(pos, comment) + } + /** Register new context; called for every created context */ def registerContext(c: analyzer.Context) { @@ -968,7 +977,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) /** A Run is a single execution of the compiler on a set of units. */ - class Run extends RunContextApi with RunReporting { + class Run extends RunContextApi with RunReporting with RunParsing { /** Have been running into too many init order issues with Run * during erroneous conditions. Moved all these vals up to the * top of the file so at least they're not trivially null. diff --git a/src/compiler/scala/tools/nsc/Parsing.scala b/src/compiler/scala/tools/nsc/Parsing.scala new file mode 100644 index 0000000000..4dd3c3f378 --- /dev/null +++ b/src/compiler/scala/tools/nsc/Parsing.scala @@ -0,0 +1,36 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2014 LAMP/EPFL, Typesafe Inc. + * @author Adriaan Moors + */ + +package scala +package tools.nsc + +import scala.reflect.internal.Positions +import scala.tools.nsc.reporters.Reporter + +/** Similar to Reporting: gather global functionality specific to parsing. + */ +trait Parsing { self : Positions with Reporting => + def currentRun: RunParsing + + trait RunParsing { + val parsing: PerRunParsing = new PerRunParsing + } + + class PerRunParsing { + // for repl + private[this] var incompleteHandler: (Position, String) => Unit = null + def withIncompleteHandler[T](handler: (Position, String) => Unit)(thunk: => T) = { + val saved = incompleteHandler + incompleteHandler = handler + try thunk + finally incompleteHandler = saved + } + + def incompleteHandled = incompleteHandler != null + def incompleteInputError(pos: Position, msg: String): Unit = + if (incompleteHandled) incompleteHandler(pos, msg) + else reporter.error(pos, msg) + } +}
\ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/Properties.scala b/src/compiler/scala/tools/nsc/Properties.scala index ed5fda9c3f..59fefba954 100644 --- a/src/compiler/scala/tools/nsc/Properties.scala +++ b/src/compiler/scala/tools/nsc/Properties.scala @@ -14,6 +14,7 @@ object Properties extends scala.util.PropertiesTrait { // settings based on jar properties def residentPromptString = scalaPropOrElse("resident.prompt", "\nnsc> ") def shellPromptString = scalaPropOrElse("shell.prompt", "\nscala> ") + def shellInterruptedString = scalaPropOrElse("shell.interrupted", ":quit\n") // derived values def isEmacsShell = propOrEmpty("env.emacs") != "" diff --git a/src/compiler/scala/tools/nsc/Reporting.scala b/src/compiler/scala/tools/nsc/Reporting.scala index 0263586418..b164f395fe 100644 --- a/src/compiler/scala/tools/nsc/Reporting.scala +++ b/src/compiler/scala/tools/nsc/Reporting.scala @@ -104,20 +104,5 @@ trait Reporting extends scala.reflect.internal.Reporting { self: ast.Positions w if (settings.fatalWarnings && reporter.hasWarnings) reporter.error(NoPosition, "No warnings can be incurred under -Xfatal-warnings.") } - - // for repl - private[this] var incompleteHandler: (Position, String) => Unit = null - def withIncompleteHandler[T](handler: (Position, String) => Unit)(thunk: => T) = { - val saved = incompleteHandler - incompleteHandler = handler - try thunk - finally incompleteHandler = saved - } - - def incompleteHandled = incompleteHandler != null - def incompleteInputError(pos: Position, msg: String): Unit = - if (incompleteHandled) incompleteHandler(pos, msg) - else reporter.error(pos, msg) - } }
\ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 883fd31dbc..8d810d456e 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -154,8 +154,8 @@ self => def unit = global.currentUnit // suppress warnings; silent abort on errors - def warning(offset: Offset, msg: String) {} - def deprecationWarning(offset: Offset, msg: String) {} + def warning(offset: Offset, msg: String): Unit = () + def deprecationWarning(offset: Offset, msg: String): Unit = () def syntaxError(offset: Offset, msg: String): Unit = throw new MalformedInput(offset, msg) def incompleteInputError(msg: String): Unit = throw new MalformedInput(source.content.length - 1, msg) @@ -204,13 +204,11 @@ self => override def newScanner() = new UnitScanner(unit, patches) - override def warning(offset: Offset, msg: String) { + override def warning(offset: Offset, msg: String): Unit = reporter.warning(o2p(offset), msg) - } - override def deprecationWarning(offset: Offset, msg: String) { + override def deprecationWarning(offset: Offset, msg: String): Unit = currentRun.reporting.deprecationWarning(o2p(offset), msg) - } private var smartParsing = false @inline private def withSmartParsing[T](body: => T): T = { @@ -226,15 +224,15 @@ self => for ((offset, msg) <- syntaxErrors) reporter.error(o2p(offset), msg) - override def syntaxError(offset: Offset, msg: String) { + override def syntaxError(offset: Offset, msg: String): Unit = { if (smartParsing) syntaxErrors += ((offset, msg)) else reporter.error(o2p(offset), msg) } - override def incompleteInputError(msg: String) { + override def incompleteInputError(msg: String): Unit = { val offset = source.content.length - 1 if (smartParsing) syntaxErrors += ((offset, msg)) - else currentRun.reporting.incompleteInputError(o2p(offset), msg) + else currentRun.parsing.incompleteInputError(o2p(offset), msg) } /** parse unit. If there are inbalanced braces, @@ -335,7 +333,7 @@ self => */ private var inScalaPackage = false private var currentPackage = "" - def resetPackage() { + def resetPackage(): Unit = { inScalaPackage = false currentPackage = "" } @@ -514,7 +512,7 @@ self => finally inFunReturnType = saved } - protected def skip(targetToken: Token) { + protected def skip(targetToken: Token): Unit = { var nparens = 0 var nbraces = 0 while (true) { @@ -544,27 +542,25 @@ self => } def warning(offset: Offset, msg: String): Unit def incompleteInputError(msg: String): Unit - private def syntaxError(pos: Position, msg: String, skipIt: Boolean) { - syntaxError(pos pointOrElse in.offset, msg, skipIt) - } def syntaxError(offset: Offset, msg: String): Unit - def syntaxError(msg: String, skipIt: Boolean) { + + private def syntaxError(pos: Position, msg: String, skipIt: Boolean): Unit = + syntaxError(pos pointOrElse in.offset, msg, skipIt) + def syntaxError(msg: String, skipIt: Boolean): Unit = syntaxError(in.offset, msg, skipIt) - } - def syntaxError(offset: Offset, msg: String, skipIt: Boolean) { + def syntaxError(offset: Offset, msg: String, skipIt: Boolean): Unit = { if (offset > lastErrorOffset) { syntaxError(offset, msg) - // no more errors on this token. - lastErrorOffset = in.offset + lastErrorOffset = in.offset // no more errors on this token. } if (skipIt) skip(UNDEF) } - def warning(msg: String) { warning(in.offset, msg) } + def warning(msg: String): Unit = warning(in.offset, msg) - def syntaxErrorOrIncomplete(msg: String, skipIt: Boolean) { + def syntaxErrorOrIncomplete(msg: String, skipIt: Boolean): Unit = { if (in.token == EOF) incompleteInputError(msg) else @@ -719,7 +715,7 @@ self => /** Convert tree to formal parameter. */ def convertToParam(tree: Tree): ValDef = atPos(tree.pos) { - def removeAsPlaceholder(name: Name) { + def removeAsPlaceholder(name: Name): Unit = { placeholderParams = placeholderParams filter (_.name != name) } def errorParam = makeParam(nme.ERROR, errorTypeTree setPos o2p(tree.pos.end)) @@ -1233,15 +1229,15 @@ self => skipIt = true)(EmptyTree) // Like Swiss cheese, with holes def stringCheese: Tree = atPos(in.offset) { - val start = in.offset + val start = in.offset val interpolator = in.name.encoded // ident() for INTERPOLATIONID val partsBuf = new ListBuffer[Tree] - val exprBuf = new ListBuffer[Tree] + val exprsBuf = new ListBuffer[Tree] in.nextToken() while (in.token == STRINGPART) { partsBuf += literal() - exprBuf += ( + exprsBuf += ( if (inPattern) dropAnyBraces(pattern()) else in.token match { case IDENTIFIER => atPos(in.offset)(Ident(ident())) @@ -1254,11 +1250,13 @@ self => } if (in.token == STRINGLIT) partsBuf += literal() + // Documenting that it is intentional that the ident is not rooted for purposes of virtualization + //val t1 = atPos(o2p(start)) { Select(Select (Ident(nme.ROOTPKG), nme.scala_), nme.StringContext) } val t1 = atPos(o2p(start)) { Ident(nme.StringContext) } val t2 = atPos(start) { Apply(t1, partsBuf.toList) } t2 setPos t2.pos.makeTransparent val t3 = Select(t2, interpolator) setPos t2.pos - atPos(start) { Apply(t3, exprBuf.toList) } + atPos(start) { Apply(t3, exprsBuf.toList) } } if (inPattern) stringCheese else withPlaceholders(stringCheese, isAny = true) // strinterpolator params are Any* by definition @@ -1266,21 +1264,21 @@ self => /* ------------- NEW LINES ------------------------------------------------- */ - def newLineOpt() { + def newLineOpt(): Unit = { if (in.token == NEWLINE) in.nextToken() } - def newLinesOpt() { + def newLinesOpt(): Unit = { if (in.token == NEWLINE || in.token == NEWLINES) in.nextToken() } - def newLineOptWhenFollowedBy(token: Offset) { + def newLineOptWhenFollowedBy(token: Offset): Unit = { // note: next is defined here because current == NEWLINE if (in.token == NEWLINE && in.next.token == token) newLineOpt() } - def newLineOptWhenFollowing(p: Token => Boolean) { + def newLineOptWhenFollowing(p: Token => Boolean): Unit = { // note: next is defined here because current == NEWLINE if (in.token == NEWLINE && p(in.next.token)) newLineOpt() } diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala index 572497ac90..9ebc94b5fc 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala @@ -113,7 +113,7 @@ trait Scanners extends ScannersCommon { case SU | CR | LF => case _ => nextChar() ; skipLineComment() } - private def maybeOpen() { + private def maybeOpen(): Unit = { putCommentChar() if (ch == '*') { putCommentChar() @@ -137,7 +137,7 @@ trait Scanners extends ScannersCommon { def skipDocComment(): Unit = skipNestedComments() def skipBlockComment(): Unit = skipNestedComments() - private def skipToCommentEnd(isLineComment: Boolean) { + private def skipToCommentEnd(isLineComment: Boolean): Unit = { nextChar() if (isLineComment) skipLineComment() else { @@ -185,7 +185,7 @@ trait Scanners extends ScannersCommon { /** append Unicode character to "cbuf" buffer */ - protected def putChar(c: Char) { + protected def putChar(c: Char): Unit = { // assert(cbuf.size < 10000, cbuf) cbuf.append(c) } @@ -196,7 +196,7 @@ trait Scanners extends ScannersCommon { protected def emitIdentifierDeprecationWarnings = true /** Clear buffer and set name and token */ - private def finishNamed(idtoken: Token = IDENTIFIER) { + private def finishNamed(idtoken: Token = IDENTIFIER): Unit = { name = newTermName(cbuf.toString) cbuf.clear() token = idtoken @@ -215,7 +215,7 @@ trait Scanners extends ScannersCommon { } /** Clear buffer and set string */ - private def setStrVal() { + private def setStrVal(): Unit = { strVal = cbuf.toString cbuf.clear() } @@ -270,7 +270,7 @@ trait Scanners extends ScannersCommon { /** Produce next token, filling TokenData fields of Scanner. */ - def nextToken() { + def nextToken(): Unit = { val lastToken = token // Adapt sepRegions according to last token (lastToken: @switch) match { @@ -341,7 +341,7 @@ trait Scanners extends ScannersCommon { prev copyFrom this val nextLastOffset = charOffset - 1 fetchToken() - def resetOffset() { + def resetOffset(): Unit = { offset = prev.offset lastOffset = prev.lastOffset } @@ -399,7 +399,7 @@ trait Scanners extends ScannersCommon { /** read next token, filling TokenData fields of Scanner. */ - protected final def fetchToken() { + protected final def fetchToken(): Unit = { offset = charOffset - 1 (ch: @switch) match { @@ -604,7 +604,7 @@ trait Scanners extends ScannersCommon { // Identifiers --------------------------------------------------------------- - private def getBackquotedIdent() { + private def getBackquotedIdent(): Unit = { nextChar() getLitChars('`') if (ch == '`') { @@ -664,7 +664,7 @@ trait Scanners extends ScannersCommon { else finishNamed() } - private def getIdentOrOperatorRest() { + private def getIdentOrOperatorRest(): Unit = { if (isIdentifierPart(ch)) getIdentRest() else ch match { @@ -688,9 +688,11 @@ trait Scanners extends ScannersCommon { setStrVal() nextChar() token = STRINGLIT - } else syntaxError("unclosed string literal") + } else unclosedStringLit() } + private def unclosedStringLit(): Unit = syntaxError("unclosed string literal") + private def getRawStringLit(): Unit = { if (ch == '\"') { nextRawChar() @@ -764,7 +766,7 @@ trait Scanners extends ScannersCommon { if (multiLine) incompleteInputError("unclosed multi-line string literal") else - syntaxError("unclosed string literal") + unclosedStringLit() } else { putChar(ch) @@ -857,7 +859,7 @@ trait Scanners extends ScannersCommon { /** read fractional part and exponent of floating point number * if one is present. */ - protected def getFraction() { + protected def getFraction(): Unit = { token = DOUBLELIT while ('0' <= ch && ch <= '9') { putChar(ch) @@ -966,14 +968,13 @@ trait Scanners extends ScannersCommon { def floatVal: Double = floatVal(negated = false) - def checkNoLetter() { + def checkNoLetter(): Unit = { if (isIdentifierPart(ch) && ch >= ' ') syntaxError("Invalid literal number") } - /** Read a number into strVal and set base - */ - protected def getNumber() { + /** Read a number into strVal and set base */ + protected def getNumber(): Unit = { val base1 = if (base < 10) 10 else base // Read 8,9's even if format is octal, produce a malformed number error afterwards. // At this point, we have already read the first digit, so to tell an innocent 0 apart @@ -1052,7 +1053,7 @@ trait Scanners extends ScannersCommon { /** Parse character literal if current character is followed by \', * or follow with given op and return a symbol literal token */ - def charLitOr(op: () => Unit) { + def charLitOr(op: () => Unit): Unit = { putChar(ch) nextChar() if (ch == '\'') { @@ -1068,21 +1069,19 @@ trait Scanners extends ScannersCommon { // Errors ----------------------------------------------------------------- - /** generate an error at the given offset - */ - def syntaxError(off: Offset, msg: String) { + /** generate an error at the given offset */ + def syntaxError(off: Offset, msg: String): Unit = { error(off, msg) token = ERROR } - /** generate an error at the current token offset - */ + /** generate an error at the current token offset */ def syntaxError(msg: String): Unit = syntaxError(offset, msg) def deprecationWarning(msg: String): Unit = deprecationWarning(offset, msg) /** signal an error where the input ended in the middle of a token */ - def incompleteInputError(msg: String) { + def incompleteInputError(msg: String): Unit = { incompleteInputError(offset, msg) token = EOF } @@ -1134,7 +1133,7 @@ trait Scanners extends ScannersCommon { /** Initialization method: read first char, then first token */ - def init() { + def init(): Unit = { nextChar() nextToken() } @@ -1261,7 +1260,7 @@ trait Scanners extends ScannersCommon { override def deprecationWarning(off: Offset, msg: String) = currentRun.reporting.deprecationWarning(unit.position(off), msg) override def error (off: Offset, msg: String) = reporter.error(unit.position(off), msg) - override def incompleteInputError(off: Offset, msg: String) = currentRun.reporting.incompleteInputError(unit.position(off), msg) + override def incompleteInputError(off: Offset, msg: String) = currentRun.parsing.incompleteInputError(unit.position(off), msg) private var bracePatches: List[BracePatch] = patches @@ -1490,6 +1489,6 @@ trait Scanners extends ScannersCommon { // when skimming through the source file trying to heal braces override def emitIdentifierDeprecationWarnings = false - override def error(offset: Offset, msg: String) {} + override def error(offset: Offset, msg: String): Unit = () } } diff --git a/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala b/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala index 64b762696e..df2073785b 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala @@ -83,7 +83,7 @@ abstract class SyntaxAnalyzer extends SubComponent with Parsers with MarkupParse private def initialUnitBody(unit: CompilationUnit): Tree = { if (unit.isJava) new JavaUnitParser(unit).parse() - else if (currentRun.reporting.incompleteHandled) newUnitParser(unit).parse() + else if (currentRun.parsing.incompleteHandled) newUnitParser(unit).parse() else newUnitParser(unit).smartParse() } diff --git a/src/compiler/scala/tools/nsc/javac/JavaScanners.scala b/src/compiler/scala/tools/nsc/javac/JavaScanners.scala index bddcf6567c..ac86dfd665 100644 --- a/src/compiler/scala/tools/nsc/javac/JavaScanners.scala +++ b/src/compiler/scala/tools/nsc/javac/JavaScanners.scala @@ -861,7 +861,7 @@ trait JavaScanners extends ast.parser.ScannersCommon { in = new JavaCharArrayReader(unit.source.content, !settings.nouescape.value, syntaxError) init() def error (pos: Int, msg: String) = reporter.error(pos, msg) - def incompleteInputError(pos: Int, msg: String) = currentRun.reporting.incompleteInputError(pos, msg) + def incompleteInputError(pos: Int, msg: String) = currentRun.parsing.incompleteInputError(pos, msg) def deprecationWarning(pos: Int, msg: String) = currentRun.reporting.deprecationWarning(pos, msg) implicit def g2p(pos: Int): Position = Position.offset(unit.source, pos) } diff --git a/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala b/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala index 6c592ead0d..5e4914fa83 100644 --- a/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala +++ b/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala @@ -30,6 +30,7 @@ abstract class AbstractReporter extends Reporter { private def isVerbose = settings.verbose.value private def noWarnings = settings.nowarnings.value private def isPromptSet = settings.prompt.value + private def isDebug = settings.debug protected def info0(pos: Position, msg: String, severity: Severity, force: Boolean) { if (severity == INFO) { @@ -46,7 +47,7 @@ abstract class AbstractReporter extends Reporter { severity.count += 1 display(pos, msg, severity) } - else if (settings.debug) { + else if (isDebug) { severity.count += 1 display(pos, "[ suppressed ] " + msg, severity) } @@ -57,6 +58,7 @@ abstract class AbstractReporter extends Reporter { } } + /** Logs a position and returns true if it was already logged. * @note Two positions are considered identical for logging if they have the same point. */ diff --git a/src/compiler/scala/tools/nsc/reporters/Reporter.scala b/src/compiler/scala/tools/nsc/reporters/Reporter.scala index 5b576a547d..3d688efae1 100644 --- a/src/compiler/scala/tools/nsc/reporters/Reporter.scala +++ b/src/compiler/scala/tools/nsc/reporters/Reporter.scala @@ -13,8 +13,8 @@ import scala.reflect.internal.util._ * This describes the internal interface for issuing information, warnings and errors. * The only abstract method in this class must be info0. * - * TODO: Move external clients (sbt/ide/partest) to reflect.internal.Reporter - * This interface should be considered private to the compiler. + * TODO: Move external clients (sbt/ide/partest) to reflect.internal.Reporter, + * and remove this class. */ abstract class Reporter extends scala.reflect.internal.Reporter { /** Informational messages. If `!force`, they may be suppressed. */ diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index ec4deb6be0..54bcc9e93e 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -1133,7 +1133,7 @@ abstract class Erasure extends AddInterfaces val tree2 = mixinTransformer.transform(tree1) // debuglog("tree after addinterfaces: \n" + tree2) - newTyper(rootContext(unit, tree, erasedTypes = true)).typed(tree2) + newTyper(rootContextPostTyper(unit, tree)).typed(tree2) } } } diff --git a/src/compiler/scala/tools/nsc/transform/TypingTransformers.scala b/src/compiler/scala/tools/nsc/transform/TypingTransformers.scala index 3feadcd9b2..dc3313e2e4 100644 --- a/src/compiler/scala/tools/nsc/transform/TypingTransformers.scala +++ b/src/compiler/scala/tools/nsc/transform/TypingTransformers.scala @@ -17,9 +17,9 @@ trait TypingTransformers { abstract class TypingTransformer(unit: CompilationUnit) extends Transformer { var localTyper: analyzer.Typer = if (phase.erasedTypes) - erasure.newTyper(erasure.rootContext(unit, EmptyTree, erasedTypes = true)).asInstanceOf[analyzer.Typer] - else - analyzer.newTyper(analyzer.rootContext(unit, EmptyTree, true)) + erasure.newTyper(erasure.rootContextPostTyper(unit, EmptyTree)).asInstanceOf[analyzer.Typer] + else // TODO: AM: should some phases use a regular rootContext instead of a post-typer one?? + analyzer.newTyper(analyzer.rootContextPostTyper(unit, EmptyTree)) protected var curTree: Tree = _ override final def atOwner[A](owner: Symbol)(trans: => A): A = atOwner(curTree, owner)(trans) diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 9715fdaf00..20e462bbce 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -27,6 +27,16 @@ trait ContextErrors { override def toString() = "[Type error at:" + errPos + "] " + errMsg } + abstract class AbsAmbiguousTypeError extends AbsTypeError + + case class AmbiguousTypeError(errPos: Position, errMsg: String) + extends AbsAmbiguousTypeError + + case class AmbiguousImplicitTypeError(underlyingTree: Tree, errMsg: String) + extends AbsAmbiguousTypeError { + def errPos = underlyingTree.pos + } + sealed abstract class TreeTypeError extends AbsTypeError { def underlyingTree: Tree def errPos = underlyingTree.pos @@ -38,9 +48,6 @@ trait ContextErrors { case class AccessTypeError(underlyingTree: Tree, errMsg: String) extends TreeTypeError - case class AmbiguousTypeError(errPos: Position, errMsg: String) - extends AbsTypeError - case class SymbolTypeError(underlyingSym: Symbol, errMsg: String) extends AbsTypeError { @@ -75,8 +82,6 @@ trait ContextErrors { s"diverging implicit expansion for type ${pt}\nstarting with ${sym.fullLocationString}" } - case class AmbiguousImplicitTypeError(underlyingTree: Tree, errMsg: String) - extends TreeTypeError case class PosAndMsgTypeError(errPos: Position, errMsg: String) extends AbsTypeError @@ -90,10 +95,6 @@ trait ContextErrors { issueTypeError(SymbolTypeError(sym, msg)) } - def issueAmbiguousTypeError(pre: Type, sym1: Symbol, sym2: Symbol, err: AmbiguousTypeError)(implicit context: Context) { - context.issueAmbiguousError(pre, sym1, sym2, err) - } - def issueTypeError(err: AbsTypeError)(implicit context: Context) { context.issue(err) } def typeErrorMsg(found: Type, req: Type) = "type mismatch" + foundReqMsg(found, req) @@ -123,6 +124,36 @@ trait ContextErrors { import ErrorUtils._ + private def MacroIncompatibleEngineError(friendlyMessage: String, internalMessage: String) = { + def debugDiagnostic = s"(internal diagnostic: $internalMessage)" + val message = if (macroDebugLite || macroDebugVerbose) s"$friendlyMessage $debugDiagnostic" else friendlyMessage + // TODO: clean this up! (This is a more explicit version of what the code use to do, to reveal the issue.) + throw new TypeError(analyzer.lastTreeToTyper.pos, message) + } + + def MacroCantExpand210xMacrosError(internalMessage: String) = + MacroIncompatibleEngineError("can't expand macros compiled by previous versions of Scala", internalMessage) + + def MacroCantExpandIncompatibleMacrosError(internalMessage: String) = + MacroIncompatibleEngineError("macro cannot be expanded, because it was compiled by an incompatible macro engine", internalMessage) + + def NoImplicitFoundError(tree: Tree, param: Symbol)(implicit context: Context): Unit = { + def errMsg = { + val paramName = param.name + val paramTp = param.tpe + def evOrParam = ( + if (paramName startsWith nme.EVIDENCE_PARAM_PREFIX) + "evidence parameter of type" + else + s"parameter $paramName:") + paramTp.typeSymbolDirect match { + case ImplicitNotFoundMsg(msg) => msg.format(paramName, paramTp) + case _ => s"could not find implicit value for $evOrParam $paramTp" + } + } + issueNormalTypeError(tree, errMsg) + } + trait TyperContextErrors { self: Typer => @@ -141,24 +172,6 @@ trait ContextErrors { setError(tree) } - def NoImplicitFoundError(tree: Tree, param: Symbol) = { - def errMsg = { - val paramName = param.name - val paramTp = param.tpe - def evOrParam = ( - if (paramName startsWith nme.EVIDENCE_PARAM_PREFIX) - "evidence parameter of type" - else - s"parameter $paramName:" - ) - paramTp.typeSymbolDirect match { - case ImplicitNotFoundMsg(msg) => msg.format(paramName, paramTp) - case _ => s"could not find implicit value for $evOrParam $paramTp" - } - } - issueNormalTypeError(tree, errMsg) - } - def AdaptTypeError(tree: Tree, found: Type, req: Type) = { // SI-3971 unwrapping to the outermost Apply helps prevent confusion with the // error message point. @@ -733,17 +746,6 @@ trait ContextErrors { NormalTypeError(expandee, "too many argument lists for " + fun) } - private def MacroIncompatibleEngineError(friendlyMessage: String, internalMessage: String) = { - def debugDiagnostic = s"(internal diagnostic: $internalMessage)" - val message = if (macroDebugLite || macroDebugVerbose) s"$friendlyMessage $debugDiagnostic" else friendlyMessage - issueNormalTypeError(lastTreeToTyper, message) - } - - def MacroCantExpand210xMacrosError(internalMessage: String) = - MacroIncompatibleEngineError("can't expand macros compiled by previous versions of Scala", internalMessage) - - def MacroCantExpandIncompatibleMacrosError(internalMessage: String) = - MacroIncompatibleEngineError("macro cannot be expanded, because it was compiled by an incompatible macro engine", internalMessage) case object MacroExpansionException extends Exception with scala.util.control.ControlThrowable @@ -883,19 +885,21 @@ trait ContextErrors { val WrongNumber, NoParams, ArgsDoNotConform = Value } - private def ambiguousErrorMsgPos(pos: Position, pre: Type, sym1: Symbol, sym2: Symbol, rest: String) = - if (sym1.hasDefault && sym2.hasDefault && sym1.enclClass == sym2.enclClass) { - val methodName = nme.defaultGetterToMethod(sym1.name) - (sym1.enclClass.pos, - "in "+ sym1.enclClass +", multiple overloaded alternatives of " + methodName + - " define default arguments") - } else { - (pos, - ("ambiguous reference to overloaded definition,\n" + - "both " + sym1 + sym1.locationString + " of type " + pre.memberType(sym1) + - "\nand " + sym2 + sym2.locationString + " of type " + pre.memberType(sym2) + - "\nmatch " + rest) - ) + private def issueAmbiguousTypeErrorUnlessErroneous(pos: Position, pre: Type, sym1: Symbol, sym2: Symbol, rest: String): Unit = + if (!(pre.isErroneous || sym1.isErroneous || sym2.isErroneous)) { + if (sym1.hasDefault && sym2.hasDefault && sym1.enclClass == sym2.enclClass) { + val methodName = nme.defaultGetterToMethod(sym1.name) + context.issueAmbiguousError(AmbiguousTypeError(sym1.enclClass.pos, + "in "+ sym1.enclClass +", multiple overloaded alternatives of " + methodName + + " define default arguments")) + } else { + context.issueAmbiguousError(AmbiguousTypeError(pos, + ("ambiguous reference to overloaded definition,\n" + + "both " + sym1 + sym1.locationString + " of type " + pre.memberType(sym1) + + "\nand " + sym2 + sym2.locationString + " of type " + pre.memberType(sym2) + + "\nmatch " + rest) + )) + } } def AccessError(tree: Tree, sym: Symbol, ctx: Context, explanation: String): AbsTypeError = @@ -952,8 +956,7 @@ trait ContextErrors { val msg0 = "argument types " + argtpes.mkString("(", ",", ")") + (if (pt == WildcardType) "" else " and expected result type " + pt) - val (pos, msg) = ambiguousErrorMsgPos(tree.pos, pre, best, firstCompeting, msg0) - issueAmbiguousTypeError(pre, best, firstCompeting, AmbiguousTypeError(pos, msg)) + issueAmbiguousTypeErrorUnlessErroneous(tree.pos, pre, best, firstCompeting, msg0) setErrorOnLastTry(lastTry, tree) } else setError(tree) // do not even try further attempts because they should all fail // even if this is not the last attempt (because of the SO's possibility on the horizon) @@ -966,8 +969,7 @@ trait ContextErrors { } def AmbiguousExprAlternativeError(tree: Tree, pre: Type, best: Symbol, firstCompeting: Symbol, pt: Type, lastTry: Boolean) = { - val (pos, msg) = ambiguousErrorMsgPos(tree.pos, pre, best, firstCompeting, "expected type " + pt) - issueAmbiguousTypeError(pre, best, firstCompeting, AmbiguousTypeError(pos, msg)) + issueAmbiguousTypeErrorUnlessErroneous(tree.pos, pre, best, firstCompeting, "expected type " + pt) setErrorOnLastTry(lastTry, tree) } diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 72ca9b879a..a79f162140 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -9,6 +9,7 @@ package typechecker import scala.collection.{ immutable, mutable } import scala.annotation.tailrec import scala.reflect.internal.util.shortClassOfInstance +import scala.tools.nsc.reporters.Reporter /** * @author Martin Odersky @@ -98,7 +99,7 @@ trait Contexts { self: Analyzer => } - def rootContext(unit: CompilationUnit, tree: Tree = EmptyTree, erasedTypes: Boolean = false): Context = { + def rootContext(unit: CompilationUnit, tree: Tree = EmptyTree, throwing: Boolean = false, checking: Boolean = false): Context = { val rootImportsContext = (startContext /: rootImports(unit))((c, sym) => c.make(gen.mkWildcardImport(sym))) // there must be a scala.xml package when xml literals were parsed in this unit @@ -113,18 +114,21 @@ trait Contexts { self: Analyzer => else rootImportsContext.make(gen.mkImport(ScalaXmlPackage, nme.TopScope, nme.dollarScope)) val c = contextWithXML.make(tree, unit = unit) - if (erasedTypes) c.setThrowErrors() else c.setReportErrors() - c(EnrichmentEnabled | ImplicitsEnabled) = !erasedTypes + + c.initRootContext(throwing, checking) c } + def rootContextPostTyper(unit: CompilationUnit, tree: Tree = EmptyTree): Context = + rootContext(unit, tree, throwing = true) + def resetContexts() { startContext.enclosingContextChain foreach { context => context.tree match { case Import(qual, _) => qual setType singleType(qual.symbol.owner.thisType, qual.symbol) case _ => } - context.reportBuffer.clearAll() + context.reporter.clearAll() } } @@ -178,7 +182,8 @@ trait Contexts { self: Analyzer => * @param _outer The next outer context. */ class Context private[typechecker](val tree: Tree, val owner: Symbol, val scope: Scope, - val unit: CompilationUnit, _outer: Context) { + val unit: CompilationUnit, _outer: Context, + private[this] var _reporter: ContextReporter = new ThrowingReporter) { private def outerIsNoContext = _outer eq null final def outer: Context = if (outerIsNoContext) NoContext else _outer @@ -254,8 +259,6 @@ trait Contexts { self: Analyzer => def macrosEnabled = this(MacrosEnabled) def enrichmentEnabled_=(value: Boolean) = this(EnrichmentEnabled) = value def enrichmentEnabled = this(EnrichmentEnabled) - def checking_=(value: Boolean) = this(Checking) = value - def checking = this(Checking) def retyping_=(value: Boolean) = this(ReTyping) = value def retyping = this(ReTyping) def inSecondTry = this(SecondTry) @@ -265,8 +268,9 @@ trait Contexts { self: Analyzer => def defaultModeForTyped: Mode = if (inTypeConstructorAllowed) Mode.NOmode else Mode.EXPRmode - /** These messages are printed when issuing an error */ - var diagnostic: List[String] = Nil + /** To enrich error messages involving default arguments. + When extending the notion, group diagnostics in an object. */ + var diagUsedDefaults: Boolean = false /** Saved type bounds for type parameters which are narrowed in a GADT. */ var savedTypeBounds: List[(Symbol, Type)] = List() @@ -310,7 +314,7 @@ trait Contexts { self: Analyzer => */ def savingUndeterminedTypeParams[A](reportAmbiguous: Boolean = ambiguousErrors)(body: => A): A = { withMode() { - this(AmbiguousErrors) = reportAmbiguous + setAmbiguousErrors(reportAmbiguous) val saved = extractUndetparams() try body finally undetparams = saved @@ -321,54 +325,59 @@ trait Contexts { self: Analyzer => // Error reporting policies and buffer. // - private var _reportBuffer: ReportBuffer = new ReportBuffer - /** A buffer for errors and warnings, used with `this.bufferErrors == true` */ - def reportBuffer = _reportBuffer - /** Discard the current report buffer, and replace with an empty one */ - def useFreshReportBuffer() = _reportBuffer = new ReportBuffer - /** Discard the current report buffer, and replace with `other` */ - def restoreReportBuffer(other: ReportBuffer) = _reportBuffer = other - - /** The first error, if any, in the report buffer */ - def firstError: Option[AbsTypeError] = reportBuffer.firstError - def errors: Seq[AbsTypeError] = reportBuffer.errors - /** Does the report buffer contain any errors? */ - def hasErrors = reportBuffer.hasErrors - - def reportErrors = this(ReportErrors) - def bufferErrors = this(BufferErrors) + // the reporter for this context + def reporter: ContextReporter = _reporter + + // if set, errors will not be reporter/thrown + def bufferErrors = reporter.isBuffering + def reportErrors = !bufferErrors + + // whether to *report* (which is separate from buffering/throwing) ambiguity errors def ambiguousErrors = this(AmbiguousErrors) - def throwErrors = contextMode.inNone(ReportErrors | BufferErrors) - - def setReportErrors(): Unit = set(enable = ReportErrors | AmbiguousErrors, disable = BufferErrors) - def setBufferErrors(): Unit = set(enable = BufferErrors, disable = ReportErrors | AmbiguousErrors) - def setThrowErrors(): Unit = this(ReportErrors | AmbiguousErrors | BufferErrors) = false - def setAmbiguousErrors(report: Boolean): Unit = this(AmbiguousErrors) = report - - /** Append the given errors to the report buffer */ - def updateBuffer(errors: Traversable[AbsTypeError]) = reportBuffer ++= errors - /** Clear all errors from the report buffer */ - def flushBuffer() { reportBuffer.clearAllErrors() } - /** Return and clear all errors from the report buffer */ - def flushAndReturnBuffer(): immutable.Seq[AbsTypeError] = { - val current = reportBuffer.errors - reportBuffer.clearAllErrors() - current - } - /** Issue and clear all warnings from the report buffer */ - def flushAndIssueWarnings() { - reportBuffer.warnings foreach { - case (pos, msg) => reporter.warning(pos, msg) + private def setAmbiguousErrors(report: Boolean): Unit = this(AmbiguousErrors) = report + + /** + * Try inference twice: once without views and once with views, + * unless views are already disabled. + */ + abstract class TryTwice { + def tryOnce(isLastTry: Boolean): Unit + + final def apply(): Unit = { + val doLastTry = + // do first try if implicits are enabled + if (implicitsEnabled) { + // We create a new BufferingReporter to + // distinguish errors that occurred before entering tryTwice + // and our first attempt in 'withImplicitsDisabled'. If the + // first attempt fails, we try with implicits on + // and the original reporter. + // immediate reporting of ambiguous errors is suppressed, so that they are buffered + inSilentMode { + try { + set(disable = ImplicitsEnabled | EnrichmentEnabled) // restored by inSilentMode + tryOnce(false) + reporter.hasErrors + } catch { + case ex: CyclicReference => throw ex + case ex: TypeError => true // recoverable cyclic references? + } + } + } else true + + // do last try if try with implicits enabled failed + // (or if it was not attempted because they were disabled) + if (doLastTry) + tryOnce(true) } - reportBuffer.clearAllWarnings() } // // Temporary mode adjustment // - @inline def withMode[T](enabled: ContextMode = NOmode, disabled: ContextMode = NOmode)(op: => T): T = { + @inline final def withMode[T](enabled: ContextMode = NOmode, disabled: ContextMode = NOmode)(op: => T): T = { val saved = contextMode set(enabled, disabled) try op @@ -402,12 +411,18 @@ trait Contexts { self: Analyzer => // See comment on FormerNonStickyModes. @inline final def withOnlyStickyModes[T](op: => T): T = withMode(disabled = FormerNonStickyModes)(op) - /** @return true if the `expr` evaluates to true within a silent Context that incurs no errors */ + // inliner note: this has to be a simple method for inlining to work -- moved the `&& !reporter.hasErrors` out @inline final def inSilentMode(expr: => Boolean): Boolean = { - withMode() { // withMode with no arguments to restore the mode mutated by `setBufferErrors`. - setBufferErrors() - try expr && !hasErrors - finally reportBuffer.clearAll() + val savedContextMode = contextMode + val savedReporter = reporter + + setAmbiguousErrors(false) + _reporter = new BufferingReporter + + try expr + finally { + contextMode = savedContextMode + _reporter = savedReporter } } @@ -423,7 +438,8 @@ trait Contexts { self: Analyzer => * `Context#imports`. */ def make(tree: Tree = tree, owner: Symbol = owner, - scope: Scope = scope, unit: CompilationUnit = unit): Context = { + scope: Scope = scope, unit: CompilationUnit = unit, + reporter: ContextReporter = this.reporter): Context = { val isTemplateOrPackage = tree match { case _: Template | _: PackageDef => true case _ => false @@ -446,16 +462,15 @@ trait Contexts { self: Analyzer => // The blank canvas val c = if (isImport) - new Context(tree, owner, scope, unit, this) with ImportContext + new Context(tree, owner, scope, unit, this, reporter) with ImportContext else - new Context(tree, owner, scope, unit, this) + new Context(tree, owner, scope, unit, this, reporter) // Fields that are directly propagated c.variance = variance - c.diagnostic = diagnostic + c.diagUsedDefaults = diagUsedDefaults c.openImplicits = openImplicits c.contextMode = contextMode // note: ConstructorSuffix, a bit within `mode`, is conditionally overwritten below. - c._reportBuffer = reportBuffer // Fields that may take on a different value in the child c.prefix = prefixInChild @@ -470,22 +485,38 @@ trait Contexts { self: Analyzer => c } + /** Use reporter (possibly buffered) for errors/warnings and enable implicit conversion **/ + def initRootContext(throwing: Boolean = false, checking: Boolean = false): Unit = { + _reporter = + if (checking) new CheckingReporter + else if (throwing) new ThrowingReporter + else new ImmediateReporter + + setAmbiguousErrors(!throwing) + this(EnrichmentEnabled | ImplicitsEnabled) = !throwing + } + def make(tree: Tree, owner: Symbol, scope: Scope): Context = // TODO SI-7345 Moving this optimization into the main overload of `make` causes all tests to fail. - // even if it is extened to check that `unit == this.unit`. Why is this? + // even if it is extended to check that `unit == this.unit`. Why is this? if (tree == this.tree && owner == this.owner && scope == this.scope) this else make(tree, owner, scope, unit) /** Make a child context that represents a new nested scope */ - def makeNewScope(tree: Tree, owner: Symbol): Context = - make(tree, owner, newNestedScope(scope)) + def makeNewScope(tree: Tree, owner: Symbol, reporter: ContextReporter = this.reporter): Context = + make(tree, owner, newNestedScope(scope), reporter = reporter) /** Make a child context that buffers errors and warnings into a fresh report buffer. */ def makeSilent(reportAmbiguousErrors: Boolean = ambiguousErrors, newtree: Tree = tree): Context = { - val c = make(newtree) - c.setBufferErrors() + // A fresh buffer so as not to leak errors/warnings into `this`. + val c = make(newtree, reporter = new BufferingReporter) c.setAmbiguousErrors(reportAmbiguousErrors) - c._reportBuffer = new ReportBuffer // A fresh buffer so as not to leak errors/warnings into `this`. + c + } + + def makeNonSilent(newtree: Tree): Context = { + val c = make(newtree, reporter = reporter.makeImmediate) + c.setAmbiguousErrors(true) c } @@ -508,7 +539,9 @@ trait Contexts { self: Analyzer => */ def makeConstructorContext = { val baseContext = enclClass.outer.nextEnclosing(!_.tree.isInstanceOf[Template]) - val argContext = baseContext.makeNewScope(tree, owner) + // must propagate reporter! + // (caught by neg/t3649 when refactoring reporting to be specified only by this.reporter and not also by this.contextMode) + val argContext = baseContext.makeNewScope(tree, owner, reporter = this.reporter) argContext.contextMode = contextMode argContext.inSelfSuperCall = true def enterElems(c: Context) { @@ -533,65 +566,16 @@ trait Contexts { self: Analyzer => // Error and warning issuance // - private def addDiagString(msg: String) = { - val ds = - if (diagnostic.isEmpty) "" - else diagnostic.mkString("\n","\n", "") - if (msg endsWith ds) msg else msg + ds - } - - private def unitError(pos: Position, msg: String): Unit = - if (checking) onTreeCheckerError(pos, msg) else reporter.error(pos, msg) - - @inline private def issueCommon(err: AbsTypeError)(pf: PartialFunction[AbsTypeError, Unit]) { - // TODO: are errors allowed to have pos == NoPosition?? - // if not, Jason suggests doing: val pos = err.errPos.orElse( { devWarning("Que?"); context.tree.pos }) - if (settings.Yissuedebug) { - log("issue error: " + err.errMsg) - (new Exception).printStackTrace() - } - if (pf isDefinedAt err) pf(err) - else if (bufferErrors) { reportBuffer += err } - else throw new TypeError(err.errPos, err.errMsg) - } - /** Issue/buffer/throw the given type error according to the current mode for error reporting. */ - def issue(err: AbsTypeError) { - issueCommon(err) { case _ if reportErrors => - unitError(err.errPos, addDiagString(err.errMsg)) - } - } - - /** Issue/buffer/throw the given implicit ambiguity error according to the current mode for error reporting. */ - def issueAmbiguousError(pre: Type, sym1: Symbol, sym2: Symbol, err: AbsTypeError) { - issueCommon(err) { case _ if ambiguousErrors => - if (!pre.isErroneous && !sym1.isErroneous && !sym2.isErroneous) - unitError(err.errPos, err.errMsg) - } - } - + private[typechecker] def issue(err: AbsTypeError) = reporter.issue(err)(this) /** Issue/buffer/throw the given implicit ambiguity error according to the current mode for error reporting. */ - def issueAmbiguousError(err: AbsTypeError) { - issueCommon(err) { case _ if ambiguousErrors => unitError(err.errPos, addDiagString(err.errMsg)) } - } - - /** Issue/throw the given `err` according to the current mode for error reporting. */ - def error(pos: Position, err: Throwable) = - if (reportErrors) unitError(pos, addDiagString(err.getMessage())) - else throw err - + private[typechecker] def issueAmbiguousError(err: AbsAmbiguousTypeError) = reporter.issueAmbiguousError(err)(this) /** Issue/throw the given error message according to the current mode for error reporting. */ - def error(pos: Position, msg: String) = { - val msg1 = addDiagString(msg) - if (reportErrors) unitError(pos, msg1) - else throw new TypeError(pos, msg1) - } - + def error(pos: Position, msg: String) = reporter.error(pos, msg) /** Issue/throw the given error message according to the current mode for error reporting. */ - def warning(pos: Position, msg: String, force: Boolean = false) { - if (reportErrors || force) reporter.warning(pos, msg) - else if (bufferErrors) reportBuffer += (pos -> msg) - } + def warning(pos: Position, msg: String) = reporter.warning(pos, msg) + def echo(pos: Position, msg: String) = reporter.echo(pos, msg) + def deprecationWarning(pos: Position, sym: Symbol, msg: String): Unit = currentRun.reporting.deprecationWarning(pos, sym, msg) @@ -601,7 +585,6 @@ trait Contexts { self: Analyzer => def featureWarning(pos: Position, featureName: String, featureDesc: String, featureTrait: Symbol, construct: => String = "", required: Boolean): Unit = currentRun.reporting.featureWarning(pos, featureName, featureDesc, featureTrait, construct, required) - def echo(pos: Position, msg: String): Unit = reporter.echo(pos, msg) // nextOuter determines which context is searched next for implicits // (after `this`, which contributes `newImplicits` below.) In @@ -1238,61 +1221,176 @@ trait Contexts { self: Analyzer => override final def toString = super.toString + " with " + s"ImportContext { $impInfo; outer.owner = ${outer.owner} }" } - /** A buffer for warnings and errors that are accumulated during speculative type checking. */ - final class ReportBuffer { + /** A reporter for use during type checking. It has multiple modes for handling errors. + * + * The default (immediate mode) is to send the error to the global reporter. + * When switched into buffering mode via makeBuffering, errors and warnings are buffered and not be reported + * (there's a special case for ambiguity errors for some reason: those are force to the reporter when context.ambiguousErrors, + * or else they are buffered -- TODO: can we simplify this?) + * + * When using the type checker after typers, an error results in a TypeError being thrown. TODO: get rid of this mode. + * + * To handle nested contexts, reporters share buffers. TODO: only buffer in BufferingReporter, emit immediately in ImmediateReporter + */ + abstract class ContextReporter(private[this] var _errorBuffer: mutable.LinkedHashSet[AbsTypeError] = null, private[this] var _warningBuffer: mutable.LinkedHashSet[(Position, String)] = null) extends Reporter { type Error = AbsTypeError type Warning = (Position, String) - private def newBuffer[A] = mutable.LinkedHashSet.empty[A] // Important to use LinkedHS for stable results. + def issue(err: AbsTypeError)(implicit context: Context): Unit = handleError(err.errPos, addDiagString(err.errMsg)) - // [JZ] Contexts, pre- the SI-7345 refactor, avoided allocating the buffers until needed. This - // is replicated here out of conservatism. - private var _errorBuffer: mutable.LinkedHashSet[Error] = _ - private def errorBuffer = {if (_errorBuffer == null) _errorBuffer = newBuffer; _errorBuffer} - def errors: immutable.Seq[Error] = errorBuffer.toVector + protected def handleError(pos: Position, msg: String): Unit + protected def handleSuppressedAmbiguous(err: AbsAmbiguousTypeError): Unit = () + protected def handleWarning(pos: Position, msg: String): Unit = reporter.warning(pos, msg) - private var _warningBuffer: mutable.LinkedHashSet[Warning] = _ - private def warningBuffer = {if (_warningBuffer == null) _warningBuffer = newBuffer; _warningBuffer} - def warnings: immutable.Seq[Warning] = warningBuffer.toVector + def makeImmediate: ContextReporter = this + def makeBuffering: ContextReporter = this + def isBuffering: Boolean = false - def +=(error: AbsTypeError): this.type = { - errorBuffer += error - this - } - def ++=(errors: Traversable[AbsTypeError]): this.type = { - errorBuffer ++= errors - this - } - def +=(warning: Warning): this.type = { - warningBuffer += warning - this + /** Emit an ambiguous error according to context.ambiguousErrors + * + * - when true, use global.reporter regardless of whether we're buffering (TODO: can we change this?) + * - else, let this context reporter decide + */ + final def issueAmbiguousError(err: AbsAmbiguousTypeError)(implicit context: Context): Unit = + if (context.ambiguousErrors) reporter.error(err.errPos, addDiagString(err.errMsg)) // force reporting... see TODO above + else handleSuppressedAmbiguous(err) + + @inline final def withFreshErrorBuffer[T](expr: => T): T = { + val previousBuffer = _errorBuffer + _errorBuffer = newBuffer + val res = expr // expr will read _errorBuffer + _errorBuffer = previousBuffer + res } - def clearAll(): this.type = { - clearAllErrors(); clearAllWarnings(); + @inline final def propagatingErrorsTo[T](target: ContextReporter)(expr: => T): T = { + val res = expr // TODO: make sure we're okay skipping the try/finally overhead + if ((this ne target) && hasErrors) { // `this eq target` in e.g., test/files/neg/divergent-implicit.scala + // assert(target.errorBuffer ne _errorBuffer) + target ++= errors + // TODO: is clearAllErrors necessary? (no tests failed when dropping it) + // NOTE: even though `this ne target`, it may still be that `target.errorBuffer eq _errorBuffer`, + // so don't clear the buffer, but null out the reference so that a new one will be created when necessary (should be never??) + // (we should refactor error buffering to avoid mutation on shared buffers) + clearAllErrors() + } + res } - def clearAllErrors(): this.type = { - errorBuffer.clear() - this - } - def clearErrors(removeF: PartialFunction[AbsTypeError, Boolean]): this.type = { - errorBuffer.retain(!PartialFunction.cond(_)(removeF)) - this + protected final def info0(pos: Position, msg: String, severity: Severity, force: Boolean): Unit = + severity match { + case ERROR => handleError(pos, msg) + case WARNING => handleWarning(pos, msg) + case INFO => reporter.echo(pos, msg) + } + + final override def hasErrors = super.hasErrors || errorBuffer.nonEmpty + + // TODO: everything below should be pushed down to BufferingReporter (related to buffering) + // Implicit relies on this most heavily, but there you know reporter.isInstanceOf[BufferingReporter] + // can we encode this statically? + + // have to pass in context because multiple contexts may share the same ReportBuffer + def reportFirstDivergentError(fun: Tree, param: Symbol, paramTp: Type)(implicit context: Context): Unit = + errors.collectFirst { + case dte: DivergentImplicitTypeError => dte + } match { + case Some(divergent) => + // DivergentImplicit error has higher priority than "no implicit found" + // no need to issue the problem again if we are still in silent mode + if (context.reportErrors) { + context.issue(divergent.withPt(paramTp)) + errorBuffer.retain { + case dte: DivergentImplicitTypeError => false + case _ => true + } + } + case _ => + NoImplicitFoundError(fun, param)(context) + } + + def retainDivergentErrorsExcept(saved: DivergentImplicitTypeError) = + errorBuffer.retain { + case err: DivergentImplicitTypeError => err ne saved + case _ => false + } + + def propagateImplicitTypeErrorsTo(target: ContextReporter) = { + errors foreach { + case err@(_: DivergentImplicitTypeError | _: AmbiguousImplicitTypeError) => + target.errorBuffer += err + case _ => + } + // debuglog("propagateImplicitTypeErrorsTo: " + errors) } - def retainErrors(leaveF: PartialFunction[AbsTypeError, Boolean]): this.type = { - errorBuffer.retain(PartialFunction.cond(_)(leaveF)) - this + + protected def addDiagString(msg: String)(implicit context: Context): String = { + val diagUsedDefaultsMsg = "Error occurred in an application involving default arguments." + if (context.diagUsedDefaults && !(msg endsWith diagUsedDefaultsMsg)) msg + "\n" + diagUsedDefaultsMsg + else msg } - def clearAllWarnings(): this.type = { - warningBuffer.clear() - this + + final def emitWarnings() = if (_warningBuffer != null) { + _warningBuffer foreach { + case (pos, msg) => reporter.warning(pos, msg) + } + _warningBuffer = null } - def hasErrors = errorBuffer.nonEmpty - def firstError = errorBuffer.headOption + // [JZ] Contexts, pre- the SI-7345 refactor, avoided allocating the buffers until needed. This + // is replicated here out of conservatism. + private def newBuffer[A] = mutable.LinkedHashSet.empty[A] // Important to use LinkedHS for stable results. + final protected def errorBuffer = { if (_errorBuffer == null) _errorBuffer = newBuffer; _errorBuffer } + final protected def warningBuffer = { if (_warningBuffer == null) _warningBuffer = newBuffer; _warningBuffer } + + final def errors: immutable.Seq[Error] = errorBuffer.toVector + final def warnings: immutable.Seq[Warning] = warningBuffer.toVector + final def firstError: Option[AbsTypeError] = errorBuffer.headOption + + // TODO: remove ++= and clearAll* entirely in favor of more high-level combinators like withFreshErrorBuffer + final private[typechecker] def ++=(errors: Traversable[AbsTypeError]): Unit = errorBuffer ++= errors + + // null references to buffers instead of clearing them, + // as the buffers may be shared between different reporters + final def clearAll(): Unit = { _errorBuffer = null; _warningBuffer = null } + final def clearAllErrors(): Unit = { _errorBuffer = null } + } + + private[typechecker] class ImmediateReporter(_errorBuffer: mutable.LinkedHashSet[AbsTypeError] = null, _warningBuffer: mutable.LinkedHashSet[(Position, String)] = null) extends ContextReporter(_errorBuffer, _warningBuffer) { + override def makeBuffering: ContextReporter = new BufferingReporter(errorBuffer, warningBuffer) + protected def handleError(pos: Position, msg: String): Unit = reporter.error(pos, msg) + } + + + private[typechecker] class BufferingReporter(_errorBuffer: mutable.LinkedHashSet[AbsTypeError] = null, _warningBuffer: mutable.LinkedHashSet[(Position, String)] = null) extends ContextReporter(_errorBuffer, _warningBuffer) { + override def isBuffering = true + + override def issue(err: AbsTypeError)(implicit context: Context): Unit = errorBuffer += err + + // this used to throw new TypeError(pos, msg) -- buffering lets us report more errors (test/files/neg/macro-basic-mamdmi) + // the old throwing behavior was relied on by diagnostics in manifestOfType + protected def handleError(pos: Position, msg: String): Unit = errorBuffer += TypeErrorWrapper(new TypeError(pos, msg)) + override protected def handleSuppressedAmbiguous(err: AbsAmbiguousTypeError): Unit = errorBuffer += err + override protected def handleWarning(pos: Position, msg: String): Unit = warningBuffer += ((pos, msg)) + + // TODO: emit all buffered errors, warnings + override def makeImmediate: ContextReporter = new ImmediateReporter(errorBuffer, warningBuffer) } + /** Used after typer (specialization relies on TypeError being thrown, among other post-typer phases). + * + * TODO: get rid of it, use ImmediateReporter and a check for reporter.hasErrors where necessary + */ + private[typechecker] class ThrowingReporter extends ContextReporter { + protected def handleError(pos: Position, msg: String): Unit = throw new TypeError(pos, msg) + } + + /** Used during a run of [[scala.tools.nsc.typechecker.TreeCheckers]]? */ + private[typechecker] class CheckingReporter extends ContextReporter { + protected def handleError(pos: Position, msg: String): Unit = onTreeCheckerError(pos, msg) + } + + class ImportInfo(val tree: Import, val depth: Int) { def pos = tree.pos def posOf(sel: ImportSelector) = tree.pos withPoint sel.namePos @@ -1385,8 +1483,6 @@ object ContextMode { def apply(bits: Int): ContextMode = new ContextMode(bits) final val NOmode: ContextMode = 0 - final val ReportErrors: ContextMode = 1 << 0 - final val BufferErrors: ContextMode = 1 << 1 final val AmbiguousErrors: ContextMode = 1 << 2 /** Are we in a secondary constructor after the this constructor call? */ @@ -1409,8 +1505,6 @@ object ContextMode { /** To selectively allow enrichment in patterns, where other kinds of implicit conversions are not allowed */ final val EnrichmentEnabled: ContextMode = 1 << 8 - /** Are we in a run of [[scala.tools.nsc.typechecker.TreeCheckers]]? */ - final val Checking: ContextMode = 1 << 9 /** Are we retypechecking arguments independently from the function applied to them? See `Typer.tryTypedApply` * TODO - iron out distinction/overlap with SecondTry. @@ -1447,17 +1541,14 @@ object ContextMode { PatternAlternative | StarPatterns | SuperInit | SecondTry | ReturnExpr | TypeConstructorAllowed ) - final val DefaultMode: ContextMode = MacrosEnabled + final val DefaultMode: ContextMode = MacrosEnabled private val contextModeNameMap = Map( - ReportErrors -> "ReportErrors", - BufferErrors -> "BufferErrors", AmbiguousErrors -> "AmbiguousErrors", ConstructorSuffix -> "ConstructorSuffix", SelfSuperCall -> "SelfSuperCall", ImplicitsEnabled -> "ImplicitsEnabled", MacrosEnabled -> "MacrosEnabled", - Checking -> "Checking", ReTyping -> "ReTyping", PatternAlternative -> "PatternAlternative", StarPatterns -> "StarPatterns", diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 73c3e6f016..b85c8e6d42 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -71,13 +71,10 @@ trait Implicits { typingStack.printTyping(tree, "typing implicit: %s %s".format(tree, context.undetparamsString)) val implicitSearchContext = context.makeImplicit(reportAmbiguous) val result = new ImplicitSearch(tree, pt, isView, implicitSearchContext, pos).bestImplicit - if (result.isFailure && saveAmbiguousDivergent && implicitSearchContext.hasErrors) { - context.updateBuffer(implicitSearchContext.reportBuffer.errors.collect { - case dte: DivergentImplicitTypeError => dte - case ate: AmbiguousImplicitTypeError => ate - }) - debuglog("update buffer: " + implicitSearchContext.reportBuffer.errors) - } + + if (result.isFailure && saveAmbiguousDivergent && implicitSearchContext.reporter.hasErrors) + implicitSearchContext.reporter.propagateImplicitTypeErrorsTo(context.reporter) + // SI-7944 undetermined type parameters that result from inference within typedImplicit land in // `implicitSearchContext.undetparams`, *not* in `context.undetparams` // Here, we copy them up to parent context (analogously to the way the errors are copied above), @@ -99,7 +96,7 @@ trait Implicits { def wrapper(inference: => SearchResult) = wrapper1(inference) val result = wrapper(inferImplicit(tree, pt, reportAmbiguous = true, isView = isView, context = context, saveAmbiguousDivergent = !silent, pos = pos)) if (result.isFailure && !silent) { - val err = context.firstError + val err = context.reporter.firstError val errPos = err.map(_.errPos).getOrElse(pos) val errMsg = err.map(_.errMsg).getOrElse("implicit search has failed. to find out the reason, turn on -Xlog-implicits") onError(errPos, errMsg) @@ -635,7 +632,7 @@ trait Implicits { } case _ => fallback } - context.firstError match { // using match rather than foreach to avoid non local return. + context.reporter.firstError match { // using match rather than foreach to avoid non local return. case Some(err) => log("implicit adapt failed: " + err.errMsg) return fail(err.errMsg) @@ -658,8 +655,8 @@ trait Implicits { } } - if (context.hasErrors) - fail("hasMatchingSymbol reported error: " + context.firstError.get.errMsg) + if (context.reporter.hasErrors) + fail("hasMatchingSymbol reported error: " + context.reporter.firstError.get.errMsg) else if (itree3.isErroneous) fail("error typechecking implicit candidate") else if (isLocalToCallsite && !hasMatchingSymbol(itree2)) @@ -677,7 +674,7 @@ trait Implicits { // #2421: check that we correctly instantiated type parameters outside of the implicit tree: checkBounds(itree3, NoPrefix, NoSymbol, undetParams, targs, "inferred ") - context.firstError match { + context.reporter.firstError match { case Some(err) => return fail("type parameters weren't correctly instantiated outside of the implicit tree: " + err.errMsg) case None => @@ -716,7 +713,7 @@ trait Implicits { case t => t } - context.firstError match { + context.reporter.firstError match { case Some(err) => fail("typing TypeApply reported errors for the implicit tree: " + err.errMsg) case None => @@ -857,13 +854,11 @@ trait Implicits { SearchFailure } else { if (search.isFailure) { - // We don't want errors that occur during checking implicit info + // Discard the divergentError we saved (if any), as well as all errors that are not of type DivergentImplicitTypeError + // We don't want errors that occur while checking the implicit info // to influence the check of further infos, but we should retain divergent implicit errors // (except for the one we already squirreled away) - val saved = divergentError.getOrElse(null) - context.reportBuffer.retainErrors { - case err: DivergentImplicitTypeError => err ne saved - } + context.reporter.retainDivergentErrorsExcept(divergentError.getOrElse(null)) } search } @@ -909,7 +904,7 @@ trait Implicits { // the first `DivergentImplicitTypeError` that is being propagated // from a nested implicit search; this one will be // re-issued if this level of the search fails. - DivergentImplicitRecovery(typedFirstPending, firstPending, context.errors) match { + DivergentImplicitRecovery(typedFirstPending, firstPending, context.reporter.errors) match { case sr if sr.isDivergent => Nil case sr if sr.isFailure => rankImplicits(otherPending, acc) case newBest => @@ -1146,7 +1141,7 @@ trait Implicits { try { val tree1 = typedPos(pos.focus)(arg) - context.firstError match { + context.reporter.firstError match { case Some(err) => processMacroExpansionError(err.errPos, err.errMsg) case None => new SearchResult(tree1, EmptyTreeTypeSubstituter, Nil) } @@ -1278,19 +1273,20 @@ trait Implicits { if (tagInScope.isEmpty) mot(tp, Nil, Nil) else { if (ReflectRuntimeUniverse == NoSymbol) { - // todo. write a test for this - context.error(pos, + // TODO: write a test for this (the next error message is already checked by neg/interop_typetags_without_classtags_arenot_manifests.scala) + // TODO: this was using context.error, and implicit search always runs in silent mode, thus it was actually throwing a TypeError + // with the new strategy-based reporting, a BufferingReporter buffers instead of throwing + // it would be good to rework this logic to fit into the regular context.error mechanism + throw new TypeError(pos, sm"""to create a manifest here, it is necessary to interoperate with the type tag `$tagInScope` in scope. |however typetag -> manifest conversion requires Scala reflection, which is not present on the classpath. |to proceed put scala-reflect.jar on your compilation classpath and recompile.""") - return SearchFailure } if (resolveClassTag(pos, tp, allowMaterialization = true) == EmptyTree) { - context.error(pos, + throw new TypeError(pos, sm"""to create a manifest here, it is necessary to interoperate with the type tag `$tagInScope` in scope. |however typetag -> manifest conversion requires a class tag for the corresponding type to be present. |to proceed add a class tag to the type `$tp` (e.g. by introducing a context bound) and recompile.""") - return SearchFailure } val cm = typed(Ident(ReflectRuntimeCurrentMirror)) val internal = gen.mkAttributedSelect(gen.mkAttributedRef(ReflectRuntimeUniverse), UniverseInternal) @@ -1346,52 +1342,66 @@ trait Implicits { * If all fails return SearchFailure */ def bestImplicit: SearchResult = { - val failstart = if (Statistics.canEnable) Statistics.startTimer(inscopeFailNanos) else null - val succstart = if (Statistics.canEnable) Statistics.startTimer(inscopeSucceedNanos) else null + val stats = Statistics.canEnable + val failstart = if (stats) Statistics.startTimer(inscopeFailNanos) else null + val succstart = if (stats) Statistics.startTimer(inscopeSucceedNanos) else null var result = searchImplicit(context.implicitss, isLocalToCallsite = true) - if (result.isFailure) { - if (Statistics.canEnable) Statistics.stopTimer(inscopeFailNanos, failstart) - } else { - if (Statistics.canEnable) Statistics.stopTimer(inscopeSucceedNanos, succstart) - if (Statistics.canEnable) Statistics.incCounter(inscopeImplicitHits) + if (stats) { + if (result.isFailure) Statistics.stopTimer(inscopeFailNanos, failstart) + else { + Statistics.stopTimer(inscopeSucceedNanos, succstart) + Statistics.incCounter(inscopeImplicitHits) + } } + if (result.isFailure) { - val previousErrs = context.flushAndReturnBuffer() - val failstart = if (Statistics.canEnable) Statistics.startTimer(oftypeFailNanos) else null - val succstart = if (Statistics.canEnable) Statistics.startTimer(oftypeSucceedNanos) else null + val failstart = if (stats) Statistics.startTimer(oftypeFailNanos) else null + val succstart = if (stats) Statistics.startTimer(oftypeSucceedNanos) else null + + // SI-6667, never search companions after an ambiguous error in in-scope implicits + val wasAmbigious = result.isAmbiguousFailure + + // TODO: encapsulate + val previousErrs = context.reporter.errors + context.reporter.clearAllErrors() - val wasAmbigious = result.isAmbiguousFailure // SI-6667, never search companions after an ambiguous error in in-scope implicits result = materializeImplicit(pt) + // `materializeImplicit` does some preprocessing for `pt` // is it only meant for manifests/tags or we need to do the same for `implicitsOfExpectedType`? if (result.isFailure && !wasAmbigious) result = searchImplicit(implicitsOfExpectedType, isLocalToCallsite = false) - if (result.isFailure) { - context.updateBuffer(previousErrs) - if (Statistics.canEnable) Statistics.stopTimer(oftypeFailNanos, failstart) - } else { - if (Statistics.canEnable) Statistics.stopTimer(oftypeSucceedNanos, succstart) - if (Statistics.canEnable) Statistics.incCounter(oftypeImplicitHits) + if (result.isFailure) + context.reporter ++= previousErrs + + if (stats) { + if (result.isFailure) Statistics.stopTimer(oftypeFailNanos, failstart) + else { + Statistics.stopTimer(oftypeSucceedNanos, succstart) + Statistics.incCounter(oftypeImplicitHits) + } } } if (result.isSuccess && isView) { def maybeInvalidConversionError(msg: String) { // We have to check context.ambiguousErrors even though we are calling "issueAmbiguousError" // which ostensibly does exactly that before issuing the error. Why? I have no idea. Test is pos/t7690. + // AM: I would guess it's because ambiguous errors will be buffered in silent mode if they are not reported if (context.ambiguousErrors) context.issueAmbiguousError(AmbiguousImplicitTypeError(tree, msg)) } pt match { case Function1(_, out) => - def prohibit(sym: Symbol) = if (sym.tpe <:< out) { - maybeInvalidConversionError(s"the result type of an implicit conversion must be more specific than ${sym.name}") - result = SearchFailure + // must inline to avoid capturing result + def prohibit(sym: Symbol) = (sym.tpe <:< out) && { + maybeInvalidConversionError(s"the result type of an implicit conversion must be more specific than ${sym.name}") + true } - prohibit(AnyRefClass) - if (settings.isScala211) prohibit(AnyValClass) + if (prohibit(AnyRefClass) || (settings.isScala211 && prohibit(AnyValClass))) + result = SearchFailure case _ => false } if (settings.isScala211 && isInvalidConversionSource(pt)) { @@ -1399,8 +1409,9 @@ trait Implicits { result = SearchFailure } } - if (result.isFailure) - debuglog("no implicits found for "+pt+" "+pt.typeSymbol.info.baseClasses+" "+implicitsOfExpectedType) + + if (result.isFailure && settings.debug) // debuglog is not inlined for some reason + log("no implicits found for "+pt+" "+pt.typeSymbol.info.baseClasses+" "+implicitsOfExpectedType) result } @@ -1422,20 +1433,19 @@ trait Implicits { val eligible = new ImplicitComputation(iss, isLocalToCallsite).eligible eligible.toList.flatMap { (ii: ImplicitInfo) => - // each ImplicitInfo contributes a distinct set of constraints (generated indirectly by typedImplicit) - // thus, start each type var off with a fresh for every typedImplicit - resetTVars() - // any previous errors should not affect us now - context.flushBuffer() - - val res = typedImplicit(ii, ptChecked = false, isLocalToCallsite) - if (res.tree ne EmptyTree) List((res, tvars map (_.constr))) - else Nil + // each ImplicitInfo contributes a distinct set of constraints (generated indirectly by typedImplicit) + // thus, start each type var off with a fresh for every typedImplicit + resetTVars() + // any previous errors should not affect us now + context.reporter.clearAllErrors() + val res = typedImplicit(ii, ptChecked = false, isLocalToCallsite) + if (res.tree ne EmptyTree) List((res, tvars map (_.constr))) + else Nil + } } - } eligibleInfos(context.implicitss, isLocalToCallsite = true) ++ eligibleInfos(implicitsOfExpectedType, isLocalToCallsite = false) - } + } } object ImplicitNotFoundMsg { diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index a3f1da60ce..fb7651ffd6 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -199,8 +199,6 @@ trait Infer extends Checkable { def getContext = context - def issue(err: AbsTypeError): Unit = context.issue(err) - def explainTypes(tp1: Type, tp2: Type) = { if (context.reportErrors) withDisambiguation(List(), tp1, tp2)(global.explainTypes(tp1, tp2)) @@ -781,7 +779,7 @@ trait Infer extends Checkable { def applicableExpectingPt(pt: Type): Boolean = { val silent = context.makeSilent(reportAmbiguousErrors = false) val result = newTyper(silent).infer.isApplicable(undetparams, ftpe, argtpes0, pt) - if (silent.hasErrors && !pt.isWildcard) + if (silent.reporter.hasErrors && !pt.isWildcard) applicableExpectingPt(WildcardType) // second try else result @@ -1266,33 +1264,36 @@ trait Infer extends Checkable { * If no alternative matches `pt`, take the parameterless one anyway. */ def inferExprAlternative(tree: Tree, pt: Type): Tree = { - def tryOurBests(pre: Type, alts: List[Symbol], isSecondTry: Boolean): Unit = { - val alts0 = alts filter (alt => isWeaklyCompatible(pre memberType alt, pt)) - val alts1 = if (alts0.isEmpty) alts else alts0 - val bests = bestAlternatives(alts1) { (sym1, sym2) => - val tp1 = pre memberType sym1 - val tp2 = pre memberType sym2 - - ( (tp2 eq ErrorType) - || isWeaklyCompatible(tp1, pt) && !isWeaklyCompatible(tp2, pt) - || isStrictlyMoreSpecific(tp1, tp2, sym1, sym2) - ) - } - // todo: missing test case for bests.isEmpty - bests match { - case best :: Nil => tree setSymbol best setType (pre memberType best) - case best :: competing :: _ if alts0.nonEmpty => - // SI-6912 Don't give up and leave an OverloadedType on the tree. - // Originally I wrote this as `if (secondTry) ... `, but `tryTwice` won't attempt the second try - // unless an error is issued. We're not issuing an error, in the assumption that it would be - // spurious in light of the erroneous expected type - if (pt.isErroneous) setError(tree) - else AmbiguousExprAlternativeError(tree, pre, best, competing, pt, isSecondTry) - case _ => if (bests.isEmpty || alts0.isEmpty) NoBestExprAlternativeError(tree, pt, isSecondTry) + val c = context + class InferTwice(pre: Type, alts: List[Symbol]) extends c.TryTwice { + def tryOnce(isSecondTry: Boolean): Unit = { + val alts0 = alts filter (alt => isWeaklyCompatible(pre memberType alt, pt)) + val alts1 = if (alts0.isEmpty) alts else alts0 + val bests = bestAlternatives(alts1) { (sym1, sym2) => + val tp1 = pre memberType sym1 + val tp2 = pre memberType sym2 + + ( (tp2 eq ErrorType) + || isWeaklyCompatible(tp1, pt) && !isWeaklyCompatible(tp2, pt) + || isStrictlyMoreSpecific(tp1, tp2, sym1, sym2) + ) + } + // todo: missing test case for bests.isEmpty + bests match { + case best :: Nil => tree setSymbol best setType (pre memberType best) + case best :: competing :: _ if alts0.nonEmpty => + // SI-6912 Don't give up and leave an OverloadedType on the tree. + // Originally I wrote this as `if (secondTry) ... `, but `tryTwice` won't attempt the second try + // unless an error is issued. We're not issuing an error, in the assumption that it would be + // spurious in light of the erroneous expected type + if (pt.isErroneous) setError(tree) + else AmbiguousExprAlternativeError(tree, pre, best, competing, pt, isSecondTry) + case _ => if (bests.isEmpty || alts0.isEmpty) NoBestExprAlternativeError(tree, pt, isSecondTry) + } } } tree.tpe match { - case OverloadedType(pre, alts) => tryTwice(tryOurBests(pre, alts, _)) ; tree + case OverloadedType(pre, alts) => (new InferTwice(pre, alts)).apply() ; tree case _ => tree } } @@ -1370,70 +1371,41 @@ trait Infer extends Checkable { * @pre tree.tpe is an OverloadedType. */ def inferMethodAlternative(tree: Tree, undetparams: List[Symbol], argtpes0: List[Type], pt0: Type): Unit = { - val OverloadedType(pre, alts) = tree.tpe - var varargsStar = false - val argtpes = argtpes0 mapConserve { - case RepeatedType(tp) => varargsStar = true ; tp - case tp => tp - } - def followType(sym: Symbol) = followApply(pre memberType sym) - def bestForExpectedType(pt: Type, isLastTry: Boolean): Unit = { - val applicable0 = alts filter (alt => context inSilentMode isApplicable(undetparams, followType(alt), argtpes, pt)) - val applicable = overloadsToConsiderBySpecificity(applicable0, argtpes, varargsStar) - val ranked = bestAlternatives(applicable)((sym1, sym2) => - isStrictlyMoreSpecific(followType(sym1), followType(sym2), sym1, sym2) - ) - ranked match { - case best :: competing :: _ => AmbiguousMethodAlternativeError(tree, pre, best, competing, argtpes, pt, isLastTry) // ambiguous - case best :: Nil => tree setSymbol best setType (pre memberType best) // success - case Nil if pt.isWildcard => NoBestMethodAlternativeError(tree, argtpes, pt, isLastTry) // failed - case Nil => bestForExpectedType(WildcardType, isLastTry) // failed, but retry with WildcardType - } - } - // This potentially makes up to four attempts: tryTwice may execute + // This potentially makes up to four attempts: tryOnce may execute // with and without views enabled, and bestForExpectedType will try again // with pt = WildcardType if it fails with pt != WildcardType. - tryTwice { isLastTry => - val pt = if (pt0.typeSymbol == UnitClass) WildcardType else pt0 - debuglog(s"infer method alt ${tree.symbol} with alternatives ${alts map pre.memberType} argtpes=$argtpes pt=$pt") - bestForExpectedType(pt, isLastTry) - } - } + val c = context + class InferMethodAlternativeTwice extends c.TryTwice { + private[this] val OverloadedType(pre, alts) = tree.tpe + private[this] var varargsStar = false + private[this] val argtpes = argtpes0 mapConserve { + case RepeatedType(tp) => varargsStar = true ; tp + case tp => tp + } - /** Try inference twice, once without views and once with views, - * unless views are already disabled. - */ - def tryTwice(infer: Boolean => Unit): Unit = { - if (context.implicitsEnabled) { - val savedContextMode = context.contextMode - var fallback = false - context.setBufferErrors() - // We cache the current buffer because it is impossible to - // distinguish errors that occurred before entering tryTwice - // and our first attempt in 'withImplicitsDisabled'. If the - // first attempt fails we try with implicits on *and* clean - // buffer but that would also flush any pre-tryTwice valid - // errors, hence some manual buffer tweaking is necessary. - val errorsToRestore = context.flushAndReturnBuffer() - try { - context.withImplicitsDisabled(infer(false)) - if (context.hasErrors) { - fallback = true - context.contextMode = savedContextMode - context.flushBuffer() - infer(true) + private def followType(sym: Symbol) = followApply(pre memberType sym) + // separate method to help the inliner + private def isAltApplicable(pt: Type)(alt: Symbol) = context inSilentMode { isApplicable(undetparams, followType(alt), argtpes, pt) && !context.reporter.hasErrors } + private def rankAlternatives(sym1: Symbol, sym2: Symbol) = isStrictlyMoreSpecific(followType(sym1), followType(sym2), sym1, sym2) + private def bestForExpectedType(pt: Type, isLastTry: Boolean): Unit = { + val applicable = overloadsToConsiderBySpecificity(alts filter isAltApplicable(pt), argtpes, varargsStar) + val ranked = bestAlternatives(applicable)(rankAlternatives) + ranked match { + case best :: competing :: _ => AmbiguousMethodAlternativeError(tree, pre, best, competing, argtpes, pt, isLastTry) // ambiguous + case best :: Nil => tree setSymbol best setType (pre memberType best) // success + case Nil if pt.isWildcard => NoBestMethodAlternativeError(tree, argtpes, pt, isLastTry) // failed + case Nil => bestForExpectedType(WildcardType, isLastTry) // failed, but retry with WildcardType } - } catch { - case ex: CyclicReference => throw ex - case ex: TypeError => // recoverable cyclic references - context.contextMode = savedContextMode - if (!fallback) infer(true) else () - } finally { - context.contextMode = savedContextMode - context.updateBuffer(errorsToRestore) + } + + private[this] val pt = if (pt0.typeSymbol == UnitClass) WildcardType else pt0 + def tryOnce(isLastTry: Boolean): Unit = { + debuglog(s"infer method alt ${tree.symbol} with alternatives ${alts map pre.memberType} argtpes=$argtpes pt=$pt") + bestForExpectedType(pt, isLastTry) } } - else infer(true) + + (new InferMethodAlternativeTwice).apply() } /** Assign `tree` the type of all polymorphic alternatives diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index 9c22688581..da7b8b09aa 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -226,7 +226,8 @@ trait Macros extends MacroRuntimes with Traces with Helpers { val Apply(_, pickledPayload) = wrapped val payload = pickledPayload.map{ case Assign(k, v) => (unpickleAtom(k), unpickleAtom(v)) }.toMap - import typer.TyperErrorGen._ + // TODO: refactor error handling: fail always throws a TypeError, + // and uses global state (analyzer.lastTreeToTyper) to determine the position for the error def fail(msg: String) = MacroCantExpandIncompatibleMacrosError(msg) def unpickle[T](field: String, clazz: Class[T]): T = { def failField(msg: String) = fail(s"$field $msg") @@ -576,7 +577,10 @@ trait Macros extends MacroRuntimes with Traces with Helpers { // also see http://groups.google.com/group/scala-internals/browse_thread/thread/492560d941b315cc val expanded1 = try onSuccess(duplicateAndKeepPositions(expanded)) finally popMacroContext() if (!hasMacroExpansionAttachment(expanded1)) linkExpandeeAndExpanded(expandee, expanded1) - if (settings.Ymacroexpand.value == settings.MacroExpand.Discard) expandee.setType(expanded1.tpe) + if (settings.Ymacroexpand.value == settings.MacroExpand.Discard) { + suppressMacroExpansion(expandee) + expandee.setType(expanded1.tpe) + } else expanded1 case Fallback(fallback) => onFallback(fallback) case Delayed(delayed) => onDelayed(delayed) @@ -621,7 +625,7 @@ trait Macros extends MacroRuntimes with Traces with Helpers { // `macroExpandApply` is called from `adapt`, where implicit conversions are disabled // therefore we need to re-enable the conversions back temporarily val result = typer.context.withImplicitsEnabled(typer.typed(tree, mode, pt)) - if (result.isErrorTyped && macroDebugVerbose) println(s"$label has failed: ${typer.context.reportBuffer.errors}") + if (result.isErrorTyped && macroDebugVerbose) println(s"$label has failed: ${typer.context.reporter.errors}") result } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 7bbd81118a..fdff2f3076 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -1494,8 +1494,7 @@ trait Namers extends MethodSynthesis { case defn: MemberDef => val ainfos = defn.mods.annotations filterNot (_ eq null) map { ann => val ctx = typer.context - val annCtx = ctx.make(ann) - annCtx.setReportErrors() + val annCtx = ctx.makeNonSilent(ann) // need to be lazy, #1782. beforeTyper to allow inferView in annotation args, SI-5892. AnnotationInfo lazily { enteringTyper(newTyper(annCtx) typedAnnotation ann) diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala index cf3f265f0c..da0e67a2a5 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala @@ -261,7 +261,7 @@ trait PatternTypers { def doTypedUnapply(tree: Tree, fun0: Tree, fun: Tree, args: List[Tree], mode: Mode, pt: Type): Tree = { def duplErrTree = setError(treeCopy.Apply(tree, fun0, args)) - def duplErrorTree(err: AbsTypeError) = { issue(err); duplErrTree } + def duplErrorTree(err: AbsTypeError) = { context.issue(err); duplErrTree } if (args.length > MaxTupleArity) return duplErrorTree(TooManyArgsPatternError(fun)) diff --git a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala index 399a4ca8d5..743bbe53bd 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala @@ -208,8 +208,7 @@ abstract class TreeCheckers extends Analyzer { } def check(unit: CompilationUnit) { informProgress("checking "+unit) - val context = rootContext(unit) - context.checking = true + val context = rootContext(unit, checking = true) tpeOfTree.clear() SymbolTracker.check(phase, unit) val checker = new TreeChecker(context) diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala index 7440f69e93..89b064e7c1 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala @@ -572,11 +572,11 @@ trait TypeDiagnostics { } else f } def apply(tree: Tree): Tree = { - // Error suppression will squash some of these warnings unless we circumvent it. + // Error suppression (in context.warning) would squash some of these warnings. // It is presumed if you are using a -Y option you would really like to hear - // the warnings you've requested. + // the warnings you've requested; thus, use reporter.warning. if (settings.warnDeadCode && context.unit.exists && treeOK(tree) && exprOK) - context.warning(tree.pos, "dead code following this construct", force = true) + reporter.warning(tree.pos, "dead code following this construct") tree } @@ -629,7 +629,7 @@ trait TypeDiagnostics { throw new FatalError("cannot redefine root "+sym) } case _ => - context0.error(ex.pos, ex) + context0.error(ex.pos, ex.msg) } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 70f44c4fc6..0621fbefe4 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -155,21 +155,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } else { mkArg = gen.mkNamedArg // don't pass the default argument (if any) here, but start emitting named arguments for the following args if (!param.hasDefault && !paramFailed) { - context.reportBuffer.errors.collectFirst { - case dte: DivergentImplicitTypeError => dte - } match { - case Some(divergent) => - // DivergentImplicit error has higher priority than "no implicit found" - // no need to issue the problem again if we are still in silent mode - if (context.reportErrors) { - context.issue(divergent.withPt(paramTp)) - context.reportBuffer.clearErrors { - case dte: DivergentImplicitTypeError => true - } - } - case _ => - NoImplicitFoundError(fun, param) - } + context.reporter.reportFirstDivergentError(fun, param, paramTp)(context) paramFailed = true } /* else { @@ -478,20 +464,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (cond) typerWithLocalContext(c)(f) else f(this) @inline - final def typerWithLocalContext[T](c: Context)(f: Typer => T): T = { - val res = f(newTyper(c)) - if (c.hasErrors) - context.updateBuffer(c.flushAndReturnBuffer()) - res - } - - @inline - final def withSavedContext[T](c: Context)(f: => T) = { - val savedErrors = c.flushAndReturnBuffer() - val res = f - c.updateBuffer(savedErrors) - res - } + final def typerWithLocalContext[T](c: Context)(f: Typer => T): T = + c.reporter.propagatingErrorsTo(context.reporter)(f(newTyper(c))) /** The typer for a label definition. If this is part of a template we * first have to enter the label definition. @@ -684,6 +658,12 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (Statistics.canEnable) Statistics.stopCounter(subtypeFailed, subtypeStart) if (Statistics.canEnable) Statistics.stopTimer(failedSilentNanos, failedSilentStart) } + @inline def wrapResult(reporter: ContextReporter, result: T) = + if (reporter.hasErrors) { + stopStats() + SilentTypeError(reporter.errors: _*) + } else SilentResultValue(result) + try { if (context.reportErrors || reportAmbiguousErrors != context.ambiguousErrors || @@ -697,20 +677,17 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper context.undetparams = context1.undetparams context.savedTypeBounds = context1.savedTypeBounds context.namedApplyBlockInfo = context1.namedApplyBlockInfo - if (context1.hasErrors) { - stopStats() - SilentTypeError(context1.errors: _*) - } else { - // If we have a successful result, emit any warnings it created. - context1.flushAndIssueWarnings() - SilentResultValue(result) - } + + // If we have a successful result, emit any warnings it created. + if (!context1.reporter.hasErrors) + context1.reporter.emitWarnings() + + wrapResult(context1.reporter, result) } else { assert(context.bufferErrors || isPastTyper, "silent mode is not available past typer") - withSavedContext(context){ - val res = op(this) - val errorsToReport = context.flushAndReturnBuffer() - if (errorsToReport.isEmpty) SilentResultValue(res) else SilentTypeError(errorsToReport.head) + + context.reporter.withFreshErrorBuffer { + wrapResult(context.reporter, op(this)) } } } catch { @@ -816,14 +793,14 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } // avoid throwing spurious DivergentImplicit errors - if (context.hasErrors) + if (context.reporter.hasErrors) setError(tree) else withCondConstrTyper(treeInfo.isSelfOrSuperConstrCall(tree))(typer1 => if (original != EmptyTree && pt != WildcardType) ( typer1 silent { tpr => val withImplicitArgs = tpr.applyImplicitArgs(tree) - if (tpr.context.hasErrors) tree // silent will wrap it in SilentTypeError anyway + if (tpr.context.reporter.hasErrors) tree // silent will wrap it in SilentTypeError anyway else tpr.typed(withImplicitArgs, mode, pt) } orElse { _ => @@ -1057,7 +1034,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val silentContext = context.makeImplicit(context.ambiguousErrors) val res = newTyper(silentContext).typed( new ApplyImplicitView(coercion, List(tree)) setPos tree.pos, mode, pt) - silentContext.firstError match { + silentContext.reporter.firstError match { case Some(err) => context.issue(err) case None => return res } @@ -2990,7 +2967,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper ConstructorsOrderError(stat) } - if (treeInfo.isPureExprForWarningPurposes(result)) context.warning(stat.pos, + if (!isPastTyper && treeInfo.isPureExprForWarningPurposes(result)) context.warning(stat.pos, "a pure expression does nothing in statement position; " + "you may be omitting necessary parentheses" ) @@ -3149,7 +3126,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper def doTypedApply(tree: Tree, fun0: Tree, args: List[Tree], mode: Mode, pt: Type): Tree = { // TODO_NMT: check the assumption that args nonEmpty def duplErrTree = setError(treeCopy.Apply(tree, fun0, args)) - def duplErrorTree(err: AbsTypeError) = { issue(err); duplErrTree } + def duplErrorTree(err: AbsTypeError) = { context.issue(err); duplErrTree } def preSelectOverloaded(fun: Tree): Tree = { if (fun.hasSymbolField && fun.symbol.isOverloaded) { @@ -3229,7 +3206,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper (arg1, arg1.tpe.deconst) }.unzip } - if (context.hasErrors) + if (context.reporter.hasErrors) setError(tree) else { inferMethodAlternative(fun, undetparams, argTpes, pt) @@ -3357,8 +3334,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper duplErrTree } else if (lencmp2 == 0) { // useful when a default doesn't match parameter type, e.g. def f[T](x:T="a"); f[Int]() - val note = "Error occurred in an application involving default arguments." - if (!(context.diagnostic contains note)) context.diagnostic = note :: context.diagnostic + context.diagUsedDefaults = true doTypedApply(tree, if (blockIsEmpty) fun else fun1, allArgs, mode, pt) } else { rollbackNamesDefaultsOwnerChanges() @@ -4331,7 +4307,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper c.retyping = true try { val res = newTyper(c).typedArgs(args, mode) - if (c.hasErrors) None else Some(res) + if (c.reporter.hasErrors) None else Some(res) } catch { case ex: CyclicReference => throw ex @@ -4395,7 +4371,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper case _ => () } } - typeErrors foreach issue + typeErrors foreach context.issue setError(treeCopy.Apply(tree, fun, args)) } @@ -4449,7 +4425,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper doTypedApply(tree, fun2, args, mode, pt) case err: SilentTypeError => onError({ - err.reportableErrors foreach issue + err.reportableErrors foreach context.issue args foreach (arg => typed(arg, mode, ErrorType)) setError(tree) }) @@ -4686,7 +4662,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper else // before failing due to access, try a dynamic call. asDynamicCall getOrElse { - issue(accessibleError.get) + context.issue(accessibleError.get) setError(tree) } case _ => diff --git a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala index 923297bafb..1643e0061f 100644 --- a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala +++ b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala @@ -142,7 +142,7 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => run.symSource(ownerClass) = NoAbstractFile // need to set file to something different from null, so that currentRun.defines works phase = run.typerPhase // need to set a phase to something <= typerPhase, otherwise implicits in typedSelect will be disabled globalPhase = run.typerPhase // amazing... looks like phase and globalPhase are different things, so we need to set them separately - currentTyper.context.setReportErrors() // need to manually set context mode, otherwise typer.silent will throw exceptions + currentTyper.context.initRootContext() // need to manually set context mode, otherwise typer.silent will throw exceptions reporter.reset() val expr3 = withContext(transform(currentTyper, expr2)) diff --git a/src/eclipse/repl/.classpath b/src/eclipse/repl/.classpath index 601a231aeb..8ff9aabfbf 100644 --- a/src/eclipse/repl/.classpath +++ b/src/eclipse/repl/.classpath @@ -1,11 +1,11 @@ <?xml version="1.0" encoding="UTF-8"?> <classpath> - <classpathentry kind="src" path="repl"/> - <classpathentry combineaccessrules="false" kind="src" path="/asm"/> - <classpathentry kind="var" path="M2_REPO/jline/jline/2.11/jline-2.11.jar"/> - <classpathentry kind="con" path="org.scala-ide.sdt.launching.SCALA_CONTAINER"/> - <classpathentry kind="con" path="org.scala-ide.sdt.launching.SCALA_COMPILER_CONTAINER"/> - <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> - <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/repl/jline-2.11.jar"/> - <classpathentry kind="output" path="build-quick-repl"/> + <classpathentry kind="src" path="repl"/> + <classpathentry combineaccessrules="false" kind="src" path="/asm"/> + <classpathentry kind="var" path="M2_REPO/jline/jline/2.12/jline-2.12.jar"/> + <!-- <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/repl/jline-2.12.jar"/> --> + <classpathentry combineaccessrules="false" kind="src" path="/scala-compiler"/> + <classpathentry combineaccessrules="false" kind="src" path="/scala-library"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> + <classpathentry kind="output" path="build-quick-repl"/> </classpath> diff --git a/src/jline/LICENSE.txt b/src/jline/LICENSE.txt deleted file mode 100644 index 1cdc44c211..0000000000 --- a/src/jline/LICENSE.txt +++ /dev/null @@ -1,33 +0,0 @@ -Copyright (c) 2002-2006, Marc Prud'hommeaux <mwp1@cornell.edu> -All rights reserved. - -Redistribution and use in source and binary forms, with or -without modification, are permitted provided that the following -conditions are met: - -Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - -Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with -the distribution. - -Neither the name of JLine nor the names of its contributors -may be used to endorse or promote products derived from this -software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO -EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, -OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED -AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING -IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED -OF THE POSSIBILITY OF SUCH DAMAGE. - diff --git a/src/jline/README.md b/src/jline/README.md deleted file mode 100644 index 829476145d..0000000000 --- a/src/jline/README.md +++ /dev/null @@ -1,24 +0,0 @@ -Description ------------ - -JLine 2.x - -License -------- - -BSD - -Building --------- - -### Requirements - -* SBT -* Java 5+ - -This is a fork with scala specific modifications. -The original repository was: git://github.com/jdillon/jline2.git - -You can now build with sbt: - - sbt update proguard diff --git a/src/jline/build.sbt b/src/jline/build.sbt deleted file mode 100644 index 873f7574f1..0000000000 --- a/src/jline/build.sbt +++ /dev/null @@ -1,49 +0,0 @@ -seq(ProguardPlugin.proguardSettings :_*) - -name := "jline" - -organization := "org.scala-lang" - -version := "2.11.0-SNAPSHOT" - -scalaVersion := "2.10.1" - -// Only need these because of weird testing jline issues. -retrieveManaged := true - -parallelExecution in Test := false - -libraryDependencies ++= Seq( - "org.fusesource.jansi" % "jansi" % "1.10", - "com.novocode" % "junit-interface" % "0.9" % "test->default" -) - -javacOptions ++= Seq("-source", "1.5", "-target", "1.5") - -proguardOptions ++= Seq( - "-dontshrink", - "-keep class *", - "-keepdirectories" -) - -proguardInJars := Nil - -makeInJarFilter ~= { prevFilter => - val jansiFilter = List( - "!META-INF/MANIFEST.MF", - "org/fusesource/hawtjni/runtime", - "org/fusesource/hawtjni/runtime/Callback.class", - "org/fusesource/hawtjni/runtime/Library.class", - "!org/fusesource/hawtjni/**", - "!META-INF/maven/org.fusesource.hawtjni", - "!META-INF/maven/org.fusesource.jansi", - "!META-INF/maven/org.fusesource.hawtjni/**", - "!META-INF/maven/org.fusesource.jansi/**" - ).mkString(",") - // In sbt 0.9.8 the scala-library.jar line was not necessary, - // but in 0.9.9 it started showing up here. Who knows. - file => - if (file startsWith "jansi-") jansiFilter - else if (file == "scala-library.jar") "!**" - else prevFilter(file) -} diff --git a/src/jline/manual-test.sh b/src/jline/manual-test.sh deleted file mode 100755 index 744e1756e8..0000000000 --- a/src/jline/manual-test.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env bash -# -# Apparently the jline bundled with sbt interferes with testing some -# changes: for instance after changing the keybindings I kept seeing -# failures until I realized what was happening and bypassed sbt, like this. -CP=lib_managed/jars/com.novocode/junit-interface/junit-interface-0.9.jar:lib_managed/jars/junit/junit-dep/junit-dep-4.8.2.jar:lib_managed/jars/org.fusesource.jansi/jansi/jansi-1.10.jar:lib_managed/jars/org.hamcrest/hamcrest-core/hamcrest-core-1.1.jar:lib_managed/jars/org.scala-tools.testing/test-interface/test-interface-0.5.jar:target/scala-2.10/test-classes:target/scala-2.10/jline_2.10-2.11.0-SNAPSHOT.min.jar - -sbt proguard -java -cp $CP org.junit.runner.JUnitCore scala.tools.jline.console.EditLineTest diff --git a/src/jline/project/build.properties b/src/jline/project/build.properties deleted file mode 100644 index 9b860e23c5..0000000000 --- a/src/jline/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=0.12.3 diff --git a/src/jline/project/plugins.sbt b/src/jline/project/plugins.sbt deleted file mode 100644 index 9c13de92d8..0000000000 --- a/src/jline/project/plugins.sbt +++ /dev/null @@ -1,3 +0,0 @@ -resolvers += Resolver.url("sbt-plugin-releases-scalasbt", url("http://repo.scala-sbt.org/scalasbt/sbt-plugin-releases/"))(Resolver.ivyStylePatterns) - -addSbtPlugin("org.scala-sbt" % "xsbt-proguard-plugin" % "0.1.3") diff --git a/src/jline/src/main/java/scala/tools/jline/AnsiWindowsTerminal.java b/src/jline/src/main/java/scala/tools/jline/AnsiWindowsTerminal.java deleted file mode 100644 index 94697137d3..0000000000 --- a/src/jline/src/main/java/scala/tools/jline/AnsiWindowsTerminal.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (C) 2009 the original author(s). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * MODIFICATIONS: methods to deal with wrapping the output stream. - */ - -package scala.tools.jline; - -import org.fusesource.jansi.AnsiConsole; -import org.fusesource.jansi.AnsiOutputStream; -import org.fusesource.jansi.WindowsAnsiOutputStream; - -import java.io.ByteArrayOutputStream; -import java.io.OutputStream; - -/** - * ANSI-supported {@link WindowsTerminal}. - * - * @since 2.0 - */ -public class AnsiWindowsTerminal - extends WindowsTerminal -{ - private final boolean ansiSupported = detectAnsiSupport(); - - @Override - public OutputStream wrapOutIfNeeded(OutputStream out) { - return wrapOutputStream(out); - } - - /** - * Returns an ansi output stream handler. We return whatever was - * passed if we determine we cannot handle ansi based on Kernel32 calls. - * - * @return an @{link AltWindowAnsiOutputStream} instance or the passed - * stream. - */ - private static OutputStream wrapOutputStream(final OutputStream stream) { - String os = System.getProperty("os.name"); - if( os.startsWith("Windows") ) { - // On windows we know the console does not interpret ANSI codes.. - try { - return new WindowsAnsiOutputStream(stream); - } catch (Throwable ignore) { - // this happens when JNA is not in the path.. or - // this happens when the stdout is being redirected to a file. - } - // Use the ANSIOutputStream to strip out the ANSI escape sequences. - return new AnsiOutputStream(stream); - } - return stream; - } - - private static boolean detectAnsiSupport() { - OutputStream out = AnsiConsole.wrapOutputStream(new ByteArrayOutputStream()); - try { - out.close(); - } - catch (Exception e) { - // ignore; - } - return out instanceof WindowsAnsiOutputStream; - } - - public AnsiWindowsTerminal() throws Exception { - super(); - } - - @Override - public boolean isAnsiSupported() { - return ansiSupported; - } - - @Override - public boolean hasWeirdWrap() { - return false; - } -} diff --git a/src/jline/src/main/java/scala/tools/jline/NoInterruptUnixTerminal.java b/src/jline/src/main/java/scala/tools/jline/NoInterruptUnixTerminal.java deleted file mode 100644 index ef7cf23c4a..0000000000 --- a/src/jline/src/main/java/scala/tools/jline/NoInterruptUnixTerminal.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2009 the original author(s). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package scala.tools.jline; - -// Based on Apache Karaf impl - -/** - * Non-interruptable (via CTRL-C) {@link UnixTerminal}. - * - * @since 2.0 - */ -public class NoInterruptUnixTerminal - extends UnixTerminal -{ - public NoInterruptUnixTerminal() throws Exception { - super(); - } - - @Override - public void init() throws Exception { - super.init(); - getSettings().set("intr undef"); - } - - @Override - public void restore() throws Exception { - getSettings().set("intr ^C"); - super.restore(); - } -} diff --git a/src/jline/src/main/java/scala/tools/jline/Terminal.java b/src/jline/src/main/java/scala/tools/jline/Terminal.java deleted file mode 100644 index 79611c244d..0000000000 --- a/src/jline/src/main/java/scala/tools/jline/Terminal.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ - -package scala.tools.jline; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -/** - * Representation of the input terminal for a platform. - * - * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> - * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> - * @since 2.0 - */ -public interface Terminal -{ - void init() throws Exception; - - void restore() throws Exception; - - void reset() throws Exception; - - boolean isSupported(); - - int getWidth(); - - int getHeight(); - - boolean isAnsiSupported(); - - /** - * When ANSI is not natively handled, the output will have to be wrapped. - */ - OutputStream wrapOutIfNeeded(OutputStream out); - - /** - * For terminals that don't wrap when character is written in last column, - * only when the next character is written. - * These are the ones that have 'am' and 'xn' termcap attributes (xterm and - * rxvt flavors falls under that category) - */ - boolean hasWeirdWrap(); - - boolean isEchoEnabled(); - - void setEchoEnabled(boolean enabled); - - int readCharacter(InputStream in) throws IOException; - - int readVirtualKey(InputStream in) throws IOException; - - InputStream getDefaultBindings(); -} diff --git a/src/jline/src/main/java/scala/tools/jline/TerminalFactory.java b/src/jline/src/main/java/scala/tools/jline/TerminalFactory.java deleted file mode 100644 index 95b7c28bd5..0000000000 --- a/src/jline/src/main/java/scala/tools/jline/TerminalFactory.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ - -package scala.tools.jline; - -import scala.tools.jline.internal.Configuration; -import scala.tools.jline.internal.Log; - -import java.text.MessageFormat; -import java.util.HashMap; -import java.util.Map; - -/** - * Creates terminal instances. - * - * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> - * @since 2.0 - */ -public class TerminalFactory -{ - public static final String JLINE_TERMINAL = "jline.terminal"; - - public static final String AUTO = "auto"; - - public static final String UNIX = "unix"; - - public static final String WIN = "win"; - - public static final String WINDOWS = "windows"; - - public static final String NONE = "none"; - - public static final String OFF = "off"; - - public static final String FALSE = "false"; - - private static final InheritableThreadLocal<Terminal> holder = new InheritableThreadLocal<Terminal>(); - - public static synchronized Terminal create() { - if (Log.TRACE) { - //noinspection ThrowableInstanceNeverThrown - Log.trace(new Throwable("CREATE MARKER")); - } - - String type = Configuration.getString(JLINE_TERMINAL); - if (type == null) { - type = AUTO; - } - - Log.debug("Creating terminal; type=", type); - - Terminal t; - try { - String tmp = type.toLowerCase(); - - if (tmp.equals(UNIX)) { - t = getFlavor(Flavor.UNIX); - } - else if (tmp.equals(WIN) | tmp.equals(WINDOWS)) { - t = getFlavor(Flavor.WINDOWS); - } - else if (tmp.equals(NONE) || tmp.equals(OFF) || tmp.equals(FALSE)) { - t = new UnsupportedTerminal(); - } - else { - if (tmp.equals(AUTO)) { - String os = Configuration.getOsName(); - Flavor flavor = Flavor.UNIX; - if (os.contains(WINDOWS)) { - flavor = Flavor.WINDOWS; - } - t = getFlavor(flavor); - } - else { - try { - t = (Terminal) Thread.currentThread().getContextClassLoader().loadClass(type).newInstance(); - } - catch (Exception e) { - throw new IllegalArgumentException(MessageFormat.format("Invalid terminal type: {0}", type), e); - } - } - } - } - catch (Exception e) { - Log.error("Failed to construct terminal; falling back to unsupported", e); - t = new UnsupportedTerminal(); - } - - Log.debug("Created Terminal: ", t); - - try { - t.init(); - } - catch (Exception e) { - Log.error("Terminal initialization failed; falling back to unsupported", e); - return new UnsupportedTerminal(); - } - - return t; - } - - public static synchronized void reset() { - holder.remove(); - } - - public static synchronized void resetIf(final Terminal t) { - if (holder.get() == t) { - reset(); - } - } - - public static enum Type - { - AUTO, - WINDOWS, - UNIX, - NONE - } - - public static synchronized void configure(final String type) { - assert type != null; - System.setProperty(JLINE_TERMINAL, type); - } - - public static synchronized void configure(final Type type) { - assert type != null; - configure(type.name().toLowerCase()); - } - - // - // Flavor Support - // - - public static enum Flavor - { - WINDOWS, - UNIX - } - - private static final Map<Flavor, Class<? extends Terminal>> FLAVORS = new HashMap<Flavor, Class<? extends Terminal>>(); - - static { - registerFlavor(Flavor.WINDOWS, AnsiWindowsTerminal.class); - registerFlavor(Flavor.UNIX, UnixTerminal.class); - } - - public static synchronized Terminal get() { - Terminal t = holder.get(); - if (t == null) { - t = create(); - holder.set(t); - } - return t; - } - - public static Terminal getFlavor(final Flavor flavor) throws Exception { - Class<? extends Terminal> type = FLAVORS.get(flavor); - if (type != null) { - return type.newInstance(); - } - - throw new InternalError(); - } - - public static void registerFlavor(final Flavor flavor, final Class<? extends Terminal> type) { - FLAVORS.put(flavor, type); - } - -}
\ No newline at end of file diff --git a/src/jline/src/main/java/scala/tools/jline/TerminalSupport.java b/src/jline/src/main/java/scala/tools/jline/TerminalSupport.java deleted file mode 100644 index 1ca12cb73f..0000000000 --- a/src/jline/src/main/java/scala/tools/jline/TerminalSupport.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ - -package scala.tools.jline; - -import scala.tools.jline.internal.Log; -import scala.tools.jline.internal.Configuration; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -/** - * Provides support for {@link Terminal} instances. - * - * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> - * @since 2.0 - */ -public abstract class TerminalSupport - implements Terminal -{ - public static String DEFAULT_KEYBINDINGS_PROPERTIES = "keybindings.properties"; - - public static final String JLINE_SHUTDOWNHOOK = "jline.shutdownhook"; - - public static final int DEFAULT_WIDTH = 80; - - public static final int DEFAULT_HEIGHT = 24; - - private Thread shutdownHook; - - private boolean shutdownHookEnabled; - - private boolean supported; - - private boolean echoEnabled; - - private boolean ansiSupported; - - protected TerminalSupport(final boolean supported) { - this.supported = supported; - this.shutdownHookEnabled = Configuration.getBoolean(JLINE_SHUTDOWNHOOK, false); - } - - public void init() throws Exception { - installShutdownHook(new RestoreHook()); - } - - public void restore() throws Exception { - TerminalFactory.resetIf(this); - removeShutdownHook(); - } - - public void reset() throws Exception { - restore(); - init(); - } - - // Shutdown hooks causes classloader leakage in sbt, - // so they are only installed if -Djline.shutdownhook is true. - protected void installShutdownHook(final Thread hook) { - if (!shutdownHookEnabled) { - Log.debug("Not install shutdown hook " + hook + " because they are disabled."); - return; - } - - assert hook != null; - - if (shutdownHook != null) { - throw new IllegalStateException("Shutdown hook already installed"); - } - - try { - Runtime.getRuntime().addShutdownHook(hook); - shutdownHook = hook; - } - catch (AbstractMethodError e) { - // JDK 1.3+ only method. Bummer. - Log.trace("Failed to register shutdown hook: ", e); - } - } - - protected void removeShutdownHook() { - if (!shutdownHookEnabled) - return; - - if (shutdownHook != null) { - try { - Runtime.getRuntime().removeShutdownHook(shutdownHook); - } - catch (AbstractMethodError e) { - // JDK 1.3+ only method. Bummer. - Log.trace("Failed to remove shutdown hook: ", e); - } - catch (IllegalStateException e) { - // The VM is shutting down, not a big deal; ignore - } - shutdownHook = null; - } - } - - public final boolean isSupported() { - return supported; - } - - public synchronized boolean isAnsiSupported() { - return ansiSupported; - } - - protected synchronized void setAnsiSupported(final boolean supported) { - this.ansiSupported = supported; - Log.debug("Ansi supported: ", supported); - } - - /** - * Subclass to change behavior if needed. - * @return the passed out - */ - public OutputStream wrapOutIfNeeded(OutputStream out) { - return out; - } - - /** - * Defaults to true which was the behaviour before this method was added. - */ - public boolean hasWeirdWrap() { - return true; - } - - public int getWidth() { - return DEFAULT_WIDTH; - } - - public int getHeight() { - return DEFAULT_HEIGHT; - } - - public synchronized boolean isEchoEnabled() { - return echoEnabled; - } - - public synchronized void setEchoEnabled(final boolean enabled) { - this.echoEnabled = enabled; - Log.debug("Echo enabled: ", enabled); - } - - public int readCharacter(final InputStream in) throws IOException { - return in.read(); - } - - public int readVirtualKey(final InputStream in) throws IOException { - return readCharacter(in); - } - - public InputStream getDefaultBindings() { - return TerminalSupport.class.getResourceAsStream(DEFAULT_KEYBINDINGS_PROPERTIES); - } - - // - // RestoreHook - // - - protected class RestoreHook - extends Thread - { - public void start() { - try { - restore(); - } - catch (Exception e) { - Log.trace("Failed to restore: ", e); - } - } - } -}
\ No newline at end of file diff --git a/src/jline/src/main/java/scala/tools/jline/UnixTerminal.java b/src/jline/src/main/java/scala/tools/jline/UnixTerminal.java deleted file mode 100644 index 94a1b98c0d..0000000000 --- a/src/jline/src/main/java/scala/tools/jline/UnixTerminal.java +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ - -package scala.tools.jline; - -import scala.tools.jline.console.Key; -import scala.tools.jline.internal.Configuration; -import scala.tools.jline.internal.Log; -import scala.tools.jline.internal.ReplayPrefixOneCharInputStream; -import scala.tools.jline.internal.TerminalLineSettings; - -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.HashMap; -import java.util.Map; - -import static scala.tools.jline.UnixTerminal.UnixKey.*; -import static scala.tools.jline.console.Key.*; - -/** - * Terminal that is used for unix platforms. Terminal initialization - * is handled by issuing the <em>stty</em> command against the - * <em>/dev/tty</em> file to disable character echoing and enable - * character input. All known unix systems (including - * Linux and Macintosh OS X) support the <em>stty</em>), so this - * implementation should work for an reasonable POSIX system. - * - * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> - * @author <a href="mailto:dwkemp@gmail.com">Dale Kemp</a> - * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> - * @author <a href="mailto:jbonofre@apache.org">Jean-Baptiste Onofré</a> - * @since 2.0 - */ -public class UnixTerminal - extends TerminalSupport -{ - private final TerminalLineSettings settings = new TerminalLineSettings(); - - private final ReplayPrefixOneCharInputStream replayStream; - - private final InputStreamReader replayReader; - - public UnixTerminal() throws Exception { - super(true); - - this.replayStream = new ReplayPrefixOneCharInputStream(Configuration.getInputEncoding()); - this.replayReader = new InputStreamReader(replayStream, replayStream.getEncoding()); - } - - protected TerminalLineSettings getSettings() { - return settings; - } - - /** - * Remove line-buffered input by invoking "stty -icanon min 1" - * against the current terminal. - */ - @Override - public void init() throws Exception { - super.init(); - - setAnsiSupported(true); - - // set the console to be character-buffered instead of line-buffered - settings.set("-icanon min 1"); - - setEchoEnabled(false); - } - - /** - * Restore the original terminal configuration, which can be used when - * shutting down the console reader. The ConsoleReader cannot be - * used after calling this method. - */ - @Override - public void restore() throws Exception { - settings.restore(); - super.restore(); - // print a newline after the terminal exits. - // this should probably be a configurable. - System.out.println(); - } - - /** - * Returns the value of <tt>stty columns</tt> param. - */ - @Override - public int getWidth() { - int w = settings.getProperty("columns"); - return w < 1 ? DEFAULT_WIDTH : w; - } - - /** - * Returns the value of <tt>stty rows>/tt> param. - */ - @Override - public int getHeight() { - int h = settings.getProperty("rows"); - return h < 1 ? DEFAULT_HEIGHT : h; - } - - @Override - public synchronized void setEchoEnabled(final boolean enabled) { - try { - if (enabled) { - settings.set("echo"); - } - else { - settings.set("-echo"); - } - super.setEchoEnabled(enabled); - } - catch (Exception e) { - Log.error("Failed to ", (enabled ? "enable" : "disable"), " echo: ", e); - } - } - - @Override - public int readVirtualKey(final InputStream in) throws IOException { - int c = readCharacter(in); - - if (Key.valueOf(c) == DELETE && settings.getProperty("erase") == DELETE.code) { - c = BACKSPACE.code; - } - - UnixKey key = UnixKey.valueOf(c); - - // in Unix terminals, arrow keys are represented by a sequence of 3 characters. E.g., the up arrow key yields 27, 91, 68 - if (key == ARROW_START) { - // also the escape key is 27 thats why we read until we have something different than 27 - // this is a bugfix, because otherwise pressing escape and than an arrow key was an undefined state - while (key == ARROW_START) { - c = readCharacter(in); - key = UnixKey.valueOf(c); - } - - if (key == ARROW_PREFIX || key == O_PREFIX) { - c = readCharacter(in); - key = UnixKey.valueOf(c); - - if (key == ARROW_UP) { - return CTRL_P.code; - } - else if (key == ARROW_DOWN) { - return CTRL_N.code; - } - else if (key == ARROW_LEFT) { - return CTRL_B.code; - } - else if (key == ARROW_RIGHT) { - return CTRL_F.code; - } - else if (key == HOME_CODE) { - return CTRL_A.code; - } - else if (key == END_CODE) { - return CTRL_E.code; - } - else if (key == DEL_THIRD) { - readCharacter(in); // read 4th & ignore - return DELETE.code; - } - } - else if (c == 'b') { // alt-b: go back a word - return CTRL_O.code; // PREV_WORD - } - else if (c == 'f') { // alt-f: go forward a word - return CTRL_T.code; // NEXT_WORD - } - else if (key == DEL) { // alt-backspace: delete previous word - return CTRL_W.code; // DELETE_PREV_WORD - } - else if (c == 'd') { // alt-d: delete next word - return CTRL_X.code; // DELETE_NEXT_WORD - } - - } - - // handle unicode characters, thanks for a patch from amyi@inf.ed.ac.uk - if (c > 128) { - // handle unicode characters longer than 2 bytes, - // thanks to Marc.Herbert@continuent.com - replayStream.setInput(c, in); - // replayReader = new InputStreamReader(replayStream, encoding); - c = replayReader.read(); - } - - return c; - } - - /** - * Unix keys. - */ - public static enum UnixKey - { - ARROW_START(27), - - ARROW_PREFIX(91), - - ARROW_LEFT(68), - - ARROW_RIGHT(67), - - ARROW_UP(65), - - ARROW_DOWN(66), - - O_PREFIX(79), - - HOME_CODE(72), - - END_CODE(70), - - DEL_THIRD(51), - - DEL_SECOND(126), - - DEL(127); - - - public final short code; - - UnixKey(final int code) { - this.code = (short) code; - } - - private static final Map<Short, UnixKey> codes; - - static { - Map<Short, UnixKey> map = new HashMap<Short, UnixKey>(); - - for (UnixKey key : UnixKey.values()) { - map.put(key.code, key); - } - - codes = map; - } - - public static UnixKey valueOf(final int code) { - return codes.get((short) code); - } - } -}
\ No newline at end of file diff --git a/src/jline/src/main/java/scala/tools/jline/UnsupportedTerminal.java b/src/jline/src/main/java/scala/tools/jline/UnsupportedTerminal.java deleted file mode 100644 index 04fe4f7f16..0000000000 --- a/src/jline/src/main/java/scala/tools/jline/UnsupportedTerminal.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ - -package scala.tools.jline; - -/** - * An unsupported terminal. - * - * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> - * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> - * @since 2.0 - */ -public class UnsupportedTerminal - extends TerminalSupport -{ - public UnsupportedTerminal() { - super(false); - setAnsiSupported(false); - setEchoEnabled(true); - } -} diff --git a/src/jline/src/main/java/scala/tools/jline/WindowsTerminal.java b/src/jline/src/main/java/scala/tools/jline/WindowsTerminal.java deleted file mode 100644 index 4c70155f59..0000000000 --- a/src/jline/src/main/java/scala/tools/jline/WindowsTerminal.java +++ /dev/null @@ -1,468 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ - -package scala.tools.jline; - -import java.io.FileDescriptor; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.HashMap; -import java.util.Map; - -import scala.tools.jline.internal.Configuration; -import org.fusesource.jansi.internal.WindowsSupport; - -import scala.tools.jline.internal.Log; -import scala.tools.jline.internal.ReplayPrefixOneCharInputStream; - -import static scala.tools.jline.WindowsTerminal.ConsoleMode.*; -import static scala.tools.jline.WindowsTerminal.WindowsKey.*; -import static scala.tools.jline.console.Key.*; - -/** - * Terminal implementation for Microsoft Windows. Terminal initialization in - * {@link #init} is accomplished by extracting the - * <em>jline_<i>version</i>.dll</em>, saving it to the system temporary - * directoy (determined by the setting of the <em>java.io.tmpdir</em> System - * property), loading the library, and then calling the Win32 APIs <a - * href="http://msdn.microsoft.com/library/default.asp? - * url=/library/en-us/dllproc/base/setconsolemode.asp">SetConsoleMode</a> and - * <a href="http://msdn.microsoft.com/library/default.asp? - * url=/library/en-us/dllproc/base/getconsolemode.asp">GetConsoleMode</a> to - * disable character echoing. - * <p/> - * <p> - * By default, the {@link #readCharacter} method will attempt to test to see if - * the specified {@link InputStream} is {@link System#in} or a wrapper around - * {@link FileDescriptor#in}, and if so, will bypass the character reading to - * directly invoke the readc() method in the JNI library. This is so the class - * can read special keys (like arrow keys) which are otherwise inaccessible via - * the {@link System#in} stream. Using JNI reading can be bypassed by setting - * the <code>jline.WindowsTerminal.directConsole</code> system property - * to <code>false</code>. - * </p> - * - * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> - * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> - * @since 2.0 - */ -public class WindowsTerminal - extends TerminalSupport -{ - public static final String JLINE_WINDOWS_TERMINAL_INPUT_ENCODING = "jline.WindowsTerminal.input.encoding"; - - public static final String JLINE_WINDOWS_TERMINAL_OUTPUT_ENCODING = "jline.WindowsTerminal.output.encoding"; - - public static final String JLINE_WINDOWS_TERMINAL_DIRECT_CONSOLE = "jline.WindowsTerminal.directConsole"; - - public static final String WINDOWSBINDINGS_PROPERTIES = "windowsbindings.properties"; - - public static final String ANSI = WindowsTerminal.class.getName() + ".ansi"; - - private boolean directConsole; - - private int originalMode; - - private final ReplayPrefixOneCharInputStream replayStream; - - private final InputStreamReader replayReader; - - public WindowsTerminal() throws Exception { - super(true); - - this.replayStream = - new ReplayPrefixOneCharInputStream(Configuration.getString(JLINE_WINDOWS_TERMINAL_INPUT_ENCODING, Configuration.getFileEncoding())); - this.replayReader = new InputStreamReader(replayStream, replayStream.getEncoding()); - } - - @Override - public void init() throws Exception { - super.init(); - - setAnsiSupported(Boolean.getBoolean(ANSI)); - - // - // FIXME: Need a way to disable direct console and sysin detection muck - // - - setDirectConsole(Boolean.getBoolean(JLINE_WINDOWS_TERMINAL_DIRECT_CONSOLE)); - - this.originalMode = getConsoleMode(); - setConsoleMode(originalMode & ~ENABLE_ECHO_INPUT.code); - setEchoEnabled(false); - } - - /** - * Restore the original terminal configuration, which can be used when - * shutting down the console reader. The ConsoleReader cannot be - * used after calling this method. - */ - @Override - public void restore() throws Exception { - // restore the old console mode - setConsoleMode(originalMode); - super.restore(); - } - - @Override - public int getWidth() { - int w = getWindowsTerminalWidth(); - return w < 1 ? DEFAULT_WIDTH : w; - } - - @Override - public int getHeight() { - int h = getWindowsTerminalHeight(); - return h < 1 ? DEFAULT_HEIGHT : h; - } - - @Override - public void setEchoEnabled(final boolean enabled) { - // Must set these four modes at the same time to make it work fine. - if (enabled) { - setConsoleMode(getConsoleMode() | - ENABLE_ECHO_INPUT.code | - ENABLE_LINE_INPUT.code | - ENABLE_PROCESSED_INPUT.code | - ENABLE_WINDOW_INPUT.code); - } - else { - setConsoleMode(getConsoleMode() & - ~(ENABLE_LINE_INPUT.code | - ENABLE_ECHO_INPUT.code | - ENABLE_PROCESSED_INPUT.code | - ENABLE_WINDOW_INPUT.code)); - } - super.setEchoEnabled(enabled); - } - - /** - * Whether or not to allow the use of the JNI console interaction. - */ - public void setDirectConsole(final boolean flag) { - this.directConsole = flag; - Log.debug("Direct console: ", flag); - } - - /** - * Whether or not to allow the use of the JNI console interaction. - */ - public Boolean getDirectConsole() { - return directConsole; - } - - - @Override - public int readCharacter(final InputStream in) throws IOException { - // if we can detect that we are directly wrapping the system - // input, then bypass the input stream and read directly (which - // allows us to access otherwise unreadable strokes, such as - // the arrow keys) - - if (directConsole || isSystemIn(in)) { - return readByte(); - } - else { - return super.readCharacter(in); - } - } - - private boolean isSystemIn(final InputStream in) throws IOException { - assert in != null; - - if (in == System.in) { - return true; - } - else if (in instanceof FileInputStream && ((FileInputStream) in).getFD() == FileDescriptor.in) { - return true; - } - - return false; - } - - @Override - public int readVirtualKey(final InputStream in) throws IOException { - int indicator = readCharacter(in); - - // in Windows terminals, arrow keys are represented by - // a sequence of 2 characters. E.g., the up arrow - // key yields 224, 72 - if (indicator == SPECIAL_KEY_INDICATOR.code || indicator == NUMPAD_KEY_INDICATOR.code) { - int c = readCharacter(in); - WindowsKey key = WindowsKey.valueOf(c); - if (key == null) - return 0; - - switch (key) { - case UP_ARROW_KEY: - return CTRL_P.code; // translate UP -> CTRL-P - - case LEFT_ARROW_KEY: - return CTRL_B.code; // translate LEFT -> CTRL-B - - case RIGHT_ARROW_KEY: - return CTRL_F.code; // translate RIGHT -> CTRL-F - - case DOWN_ARROW_KEY: - return CTRL_N.code; // translate DOWN -> CTRL-N - - case DELETE_KEY: - return CTRL_QM.code; // translate DELETE -> CTRL-? - - case HOME_KEY: - return CTRL_A.code; - - case END_KEY: - return CTRL_E.code; - - case PAGE_UP_KEY: - return CTRL_K.code; - - case PAGE_DOWN_KEY: - return CTRL_L.code; - - case ESCAPE_KEY: - return CTRL_OB.code; // translate ESCAPE -> CTRL-[ - - case INSERT_KEY: - return CTRL_C.code; - - default: - return 0; - } - } - else if (indicator > 128) { - // handle unicode characters longer than 2 bytes, - // thanks to Marc.Herbert@continuent.com - replayStream.setInput(indicator, in); - // replayReader = new InputStreamReader(replayStream, encoding); - indicator = replayReader.read(); - - } - - return indicator; - } - - @Override - public InputStream getDefaultBindings() { - return WindowsTerminal.class.getResourceAsStream(WINDOWSBINDINGS_PROPERTIES); - } - - // - // Native Bits - // - private int getConsoleMode() { - return WindowsSupport.getConsoleMode(); - } - - private void setConsoleMode(int mode) { - WindowsSupport.setConsoleMode(mode); - } - - private int readByte() { - return WindowsSupport.readByte(); - } - - private int getWindowsTerminalWidth() { - return WindowsSupport.getWindowsTerminalWidth(); - } - - private int getWindowsTerminalHeight() { - return WindowsSupport.getWindowsTerminalHeight(); - } - - /** - * Console mode - * <p/> - * Constants copied <tt>wincon.h</tt>. - */ - public static enum ConsoleMode - { - /** - * The ReadFile or ReadConsole function returns only when a carriage return - * character is read. If this mode is disable, the functions return when one - * or more characters are available. - */ - ENABLE_LINE_INPUT(2), - - /** - * Characters read by the ReadFile or ReadConsole function are written to - * the active screen buffer as they are read. This mode can be used only if - * the ENABLE_LINE_INPUT mode is also enabled. - */ - ENABLE_ECHO_INPUT(4), - - /** - * CTRL+C is processed by the system and is not placed in the input buffer. - * If the input buffer is being read by ReadFile or ReadConsole, other - * control keys are processed by the system and are not returned in the - * ReadFile or ReadConsole buffer. If the ENABLE_LINE_INPUT mode is also - * enabled, backspace, carriage return, and linefeed characters are handled - * by the system. - */ - ENABLE_PROCESSED_INPUT(1), - - /** - * User interactions that change the size of the console screen buffer are - * reported in the console's input buffee. Information about these events - * can be read from the input buffer by applications using - * theReadConsoleInput function, but not by those using ReadFile - * orReadConsole. - */ - ENABLE_WINDOW_INPUT(8), - - /** - * If the mouse pointer is within the borders of the console window and the - * window has the keyboard focus, mouse events generated by mouse movement - * and button presses are placed in the input buffer. These events are - * discarded by ReadFile or ReadConsole, even when this mode is enabled. - */ - ENABLE_MOUSE_INPUT(16), - - /** - * When enabled, text entered in a console window will be inserted at the - * current cursor location and all text following that location will not be - * overwritten. When disabled, all following text will be overwritten. An OR - * operation must be performed with this flag and the ENABLE_EXTENDED_FLAGS - * flag to enable this functionality. - */ - ENABLE_PROCESSED_OUTPUT(1), - - /** - * This flag enables the user to use the mouse to select and edit text. To - * enable this option, use the OR to combine this flag with - * ENABLE_EXTENDED_FLAGS. - */ - ENABLE_WRAP_AT_EOL_OUTPUT(2),; - - public final int code; - - ConsoleMode(final int code) { - this.code = code; - } - } - - /** - * Windows keys. - * <p/> - * Constants copied <tt>wincon.h</tt>. - */ - public static enum WindowsKey - { - /** - * On windows terminals, this character indicates that a 'special' key has - * been pressed. This means that a key such as an arrow key, or delete, or - * home, etc. will be indicated by the next character. - */ - SPECIAL_KEY_INDICATOR(224), - - /** - * On windows terminals, this character indicates that a special key on the - * number pad has been pressed. - */ - NUMPAD_KEY_INDICATOR(0), - - /** - * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR, - * this character indicates an left arrow key press. - */ - LEFT_ARROW_KEY(75), - - /** - * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR - * this character indicates an - * right arrow key press. - */ - RIGHT_ARROW_KEY(77), - - /** - * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR - * this character indicates an up - * arrow key press. - */ - UP_ARROW_KEY(72), - - /** - * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR - * this character indicates an - * down arrow key press. - */ - DOWN_ARROW_KEY(80), - - /** - * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR - * this character indicates that - * the delete key was pressed. - */ - DELETE_KEY(83), - - /** - * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR - * this character indicates that - * the home key was pressed. - */ - HOME_KEY(71), - - /** - * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR - * this character indicates that - * the end key was pressed. - */ - END_KEY(79), - - /** - * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR - * this character indicates that - * the page up key was pressed. - */ - PAGE_UP_KEY(73), - - /** - * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR - * this character indicates that - * the page down key was pressed. - */ - PAGE_DOWN_KEY(81), - - /** - * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR - * this character indicates that - * the insert key was pressed. - */ - INSERT_KEY(82), - - /** - * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR, - * this character indicates that the escape key was pressed. - */ - ESCAPE_KEY(0),; - - public final int code; - - WindowsKey(final int code) { - this.code = code; - } - - private static final Map<Integer, WindowsKey> codes; - - static { - Map<Integer, WindowsKey> map = new HashMap<Integer, WindowsKey>(); - - for (WindowsKey key : WindowsKey.values()) { - map.put(key.code, key); - } - - codes = map; - } - - public static WindowsKey valueOf(final int code) { - return codes.get(code); - } - } -} diff --git a/src/jline/src/main/java/scala/tools/jline/console/ConsoleReader.java b/src/jline/src/main/java/scala/tools/jline/console/ConsoleReader.java deleted file mode 100644 index a375b84a5c..0000000000 --- a/src/jline/src/main/java/scala/tools/jline/console/ConsoleReader.java +++ /dev/null @@ -1,2185 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ - -package scala.tools.jline.console; - -import scala.tools.jline.Terminal; -import scala.tools.jline.TerminalFactory; -import scala.tools.jline.console.completer.CandidateListCompletionHandler; -import scala.tools.jline.console.completer.Completer; -import scala.tools.jline.console.completer.CompletionHandler; -import scala.tools.jline.console.history.History; -import scala.tools.jline.console.history.MemoryHistory; -import scala.tools.jline.internal.Configuration; -import scala.tools.jline.internal.Log; -import org.fusesource.jansi.AnsiOutputStream; - -import java.awt.Toolkit; -import java.awt.datatransfer.Clipboard; -import java.awt.datatransfer.DataFlavor; -import java.awt.datatransfer.Transferable; -import java.awt.datatransfer.UnsupportedFlavorException; -import java.awt.event.ActionListener; -import java.io.*; -import java.util.*; - -/** - * A reader for console applications. It supports custom tab-completion, - * saveable command history, and command line editing. On some platforms, - * platform-specific commands will need to be issued before the reader will - * function properly. See {@link jline.Terminal#init} for convenience - * methods for issuing platform-specific setup commands. - * - * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> - * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> - */ -public class ConsoleReader -{ - public static final String JLINE_NOBELL = "jline.nobell"; - - public static final String JLINE_EXPANDEVENTS = "jline.expandevents"; - - public static final char BACKSPACE = '\b'; - - public static final char RESET_LINE = '\r'; - - public static final char KEYBOARD_BELL = '\07'; - - public static final char NULL_MASK = 0; - - public static final int TAB_WIDTH = 4; - - private static final ResourceBundle - resources = ResourceBundle.getBundle(CandidateListCompletionHandler.class.getName()); - - private final Terminal terminal; - - private InputStream in; - - private final Writer out; - - private final CursorBuffer buf = new CursorBuffer(); - - private String prompt; - - private boolean bellEnabled = true; - - private boolean expandEvents = false; - - private Character mask; - - private Character echoCharacter; - - private StringBuffer searchTerm = null; - - private String previousSearchTerm = ""; - - private int searchIndex = -1; - - public ConsoleReader(final InputStream in, final OutputStream out, final InputStream bindings, final Terminal term) throws - IOException - { - this.in = in; - this.terminal = term != null ? term : TerminalFactory.get(); - this.out = new PrintWriter(getTerminal().wrapOutIfNeeded(out)); - this.keyBindings = loadKeyBindings(bindings); - - setBellEnabled(!Configuration.getBoolean(JLINE_NOBELL, false)); - setExpandEvents(Configuration.getBoolean(JLINE_EXPANDEVENTS, false)); - } - - /** - * @deprecated use {@link #ConsoleReader(InputStream, OutputStream, InputStream, Terminal)} - * to let the terminal wrap the output stream if needed. - */ - public ConsoleReader(final InputStream in, final Writer out, final InputStream bindings, final Terminal term) throws - IOException - { - this.in = in; - this.out = out; - this.terminal = term != null ? term : TerminalFactory.get(); - this.keyBindings = loadKeyBindings(bindings); - - setBellEnabled(!Configuration.getBoolean(JLINE_NOBELL, false)); - } - - /** - * @deprecated use {@link #ConsoleReader(InputStream, OutputStream, InputStream, Terminal)} - * to let the terminal wrap the output stream if needed. - */ - public ConsoleReader(final InputStream in, final Writer out, final Terminal term) throws IOException { - this(in, out, null, term); - } - - /** - * @deprecated use {@link #ConsoleReader(InputStream, OutputStream, InputStream, Terminal)} - * to let the terminal wrap the output stream if needed. - */ - public ConsoleReader(final InputStream in, final Writer out) throws IOException - { - this(in, out, null, null); - } - - /** - * Create a new reader using {@link FileDescriptor#in} for input and - * {@link System#out} for output. - * <p/> - * {@link FileDescriptor#in} is used because it has a better chance of not being buffered. - */ - public ConsoleReader() throws IOException { - this(new FileInputStream(FileDescriptor.in), System.out, null, null ); - } - - // FIXME: Only used for tests - - void setInput(final InputStream in) { - this.in = in; - } - - public InputStream getInput() { - return in; - } - - public Writer getOutput() { - return out; - } - - public Terminal getTerminal() { - return terminal; - } - - public CursorBuffer getCursorBuffer() { - return buf; - } - - public void setBellEnabled(final boolean enabled) { - this.bellEnabled = enabled; - } - - public boolean isBellEnabled() { - return bellEnabled; - } - - public void setExpandEvents(final boolean expand) { - this.expandEvents = expand; - } - - public boolean getExpandEvents() { - return expandEvents; - } - - public void setPrompt(final String prompt) { - this.prompt = prompt; - } - - public String getPrompt() { - return prompt; - } - - /** - * Set the echo character. For example, to have "*" entered when a password is typed: - * <p/> - * <pre> - * myConsoleReader.setEchoCharacter(new Character('*')); - * </pre> - * <p/> - * Setting the character to - * <p/> - * <pre> - * null - * </pre> - * <p/> - * will restore normal character echoing. Setting the character to - * <p/> - * <pre> - * new Character(0) - * </pre> - * <p/> - * will cause nothing to be echoed. - * - * @param c the character to echo to the console in place of the typed character. - */ - public void setEchoCharacter(final Character c) { - this.echoCharacter = c; - } - - /** - * Returns the echo character. - */ - public Character getEchoCharacter() { - return echoCharacter; - } - - /** - * Erase the current line. - * - * @return false if we failed (e.g., the buffer was empty) - */ - protected final boolean resetLine() throws IOException { - if (buf.cursor == 0) { - return false; - } - - backspaceAll(); - - return true; - } - - int getCursorPosition() { - // FIXME: does not handle anything but a line with a prompt absolute position - String prompt = getPrompt(); - return ((prompt == null) ? 0 : stripAnsi(lastLine(prompt)).length()) + buf.cursor; - } - - /** - * Returns the text after the last '\n'. - * prompt is returned if no '\n' characters are present. - * null is returned if prompt is null. - */ - private String lastLine(String str) { - if (str == null) return ""; - int last = str.lastIndexOf("\n"); - - if (last >= 0) { - return str.substring(last + 1, str.length()); - } - - return str; - } - - private String stripAnsi(String str) { - if (str == null) return ""; - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - AnsiOutputStream aos = new AnsiOutputStream(baos); - aos.write(str.getBytes()); - aos.flush(); - return baos.toString(); - } catch (IOException e) { - return str; - } - } - - /** - * Move the cursor position to the specified absolute index. - */ - public final boolean setCursorPosition(final int position) throws IOException { - return moveCursor(position - buf.cursor) != 0; - } - - /** - * Set the current buffer's content to the specified {@link String}. The - * visual console will be modified to show the current buffer. - * - * @param buffer the new contents of the buffer. - */ - private void setBuffer(final String buffer) throws IOException { - // don't bother modifying it if it is unchanged - if (buffer.equals(buf.buffer.toString())) { - return; - } - - // obtain the difference between the current buffer and the new one - int sameIndex = 0; - - for (int i = 0, l1 = buffer.length(), l2 = buf.buffer.length(); (i < l1) - && (i < l2); i++) { - if (buffer.charAt(i) == buf.buffer.charAt(i)) { - sameIndex++; - } - else { - break; - } - } - - int diff = buf.cursor - sameIndex; - if (diff < 0) { // we can't backspace here so try from the end of the buffer - moveToEnd(); - diff = buf.buffer.length() - sameIndex; - } - - backspace(diff); // go back for the differences - killLine(); // clear to the end of the line - buf.buffer.setLength(sameIndex); // the new length - putString(buffer.substring(sameIndex)); // append the differences - } - - private void setBuffer(final CharSequence buffer) throws IOException { - setBuffer(String.valueOf(buffer)); - } - - /** - * Output put the prompt + the current buffer - */ - public final void drawLine() throws IOException { - String prompt = getPrompt(); - if (prompt != null) { - print(prompt); - } - - print(buf.buffer.toString()); - - if (buf.length() != buf.cursor) { // not at end of line - back(buf.length() - buf.cursor - 1); - } - // force drawBuffer to check for weird wrap (after clear screen) - drawBuffer(); - } - - /** - * Clear the line and redraw it. - */ - public final void redrawLine() throws IOException { - print(RESET_LINE); -// flush(); - drawLine(); - } - - /** - * Clear the buffer and add its contents to the history. - * - * @return the former contents of the buffer. - */ - final String finishBuffer() throws IOException { // FIXME: Package protected because used by tests - String str = buf.buffer.toString(); - - if (expandEvents) { - str = expandEvents(str); - } - - // we only add it to the history if the buffer is not empty - // and if mask is null, since having a mask typically means - // the string was a password. We clear the mask after this call - if (str.length() > 0) { - if (mask == null && isHistoryEnabled()) { - history.add(str); - } - else { - mask = null; - } - } - - history.moveToEnd(); - - buf.buffer.setLength(0); - buf.cursor = 0; - - return str; - } - - /** - * Expand event designator such as !!, !#, !3, etc... - * See http://www.gnu.org/software/bash/manual/html_node/Event-Designators.html - * - * @param str - * @return - */ - protected String expandEvents(String str) throws IOException { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < str.length(); i++) { - char c = str.charAt(i); - switch (c) { - case '!': - if (i + 1 < str.length()) { - c = str.charAt(++i); - boolean neg = false; - String rep = null; - int i1, idx; - switch (c) { - case '!': - if (history.size() == 0) { - throw new IllegalArgumentException("!!: event not found"); - } - rep = history.get(history.index() - 1).toString(); - break; - case '#': - sb.append(sb.toString()); - break; - case '?': - i1 = str.indexOf('?', i + 1); - if (i1 < 0) { - i1 = str.length(); - } - String sc = str.substring(i + 1, i1); - i = i1; - idx = searchBackwards(sc); - if (idx < 0) { - throw new IllegalArgumentException("!?" + sc + ": event not found"); - } else { - rep = history.get(idx).toString(); - } - break; - case ' ': - case '\t': - sb.append('!'); - sb.append(c); - break; - case '-': - neg = true; - i++; - // fall through - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - i1 = i; - for (; i < str.length(); i++) { - c = str.charAt(i); - if (c < '0' || c > '9') { - break; - } - } - idx = 0; - try { - idx = Integer.parseInt(str.substring(i1, i)); - } catch (NumberFormatException e) { - throw new IllegalArgumentException((neg ? "!-" : "!") + str.substring(i1, i) + ": event not found"); - } - if (neg) { - if (idx < history.size()) { - rep = (history.get(history.index() - idx)).toString(); - } else { - throw new IllegalArgumentException((neg ? "!-" : "!") + str.substring(i1, i) + ": event not found"); - } - } else { - if (idx >= history.index() - history.size() && idx < history.index()) { - rep = (history.get(idx)).toString(); - } else { - throw new IllegalArgumentException((neg ? "!-" : "!") + str.substring(i1, i) + ": event not found"); - } - } - break; - default: - String ss = str.substring(i); - i = str.length(); - idx = searchBackwards(ss, history.index(), true); - if (idx < 0) { - throw new IllegalArgumentException("!" + ss + ": event not found"); - } else { - rep = history.get(idx).toString(); - } - break; - } - if (rep != null) { - sb.append(rep); - } - } else { - sb.append(c); - } - break; - case '^': - if (i == 0) { - int i1 = str.indexOf('^', i + 1); - int i2 = str.indexOf('^', i1 + 1); - if (i2 < 0) { - i2 = str.length(); - } - if (i1 > 0 && i2 > 0) { - String s1 = str.substring(i + 1, i1); - String s2 = str.substring(i1 + 1, i2); - String s = history.get(history.index() - 1).toString().replace(s1, s2); - sb.append(s); - i = i2 + 1; - break; - } - } - sb.append(c); - break; - default: - sb.append(c); - break; - } - } - String result = sb.toString(); - if (!str.equals(result)) { - print(result); - println(); - flush(); - } - return result; - - } - - /** - * Write out the specified string to the buffer and the output stream. - */ - public final void putString(final CharSequence str) throws IOException { - buf.write(str); - print(str); - drawBuffer(); - } - - /** - * Output the specified character, both to the buffer and the output stream. - */ - private void putChar(final int c, final boolean print) throws IOException { - buf.write((char) c); - - if (print) { - if (mask == null) { - // no masking - print(c); - } - else if (mask == NULL_MASK) { - // Don't print anything - } - else { - print(mask); - } - - drawBuffer(); - } - } - - /** - * Redraw the rest of the buffer from the cursor onwards. This is necessary - * for inserting text into the buffer. - * - * @param clear the number of characters to clear after the end of the buffer - */ - private void drawBuffer(final int clear) throws IOException { - // debug ("drawBuffer: " + clear); - if (buf.cursor == buf.length() && clear == 0) { - } else { - char[] chars = buf.buffer.substring(buf.cursor).toCharArray(); - if (mask != null) { - Arrays.fill(chars, mask); - } - if (getTerminal().hasWeirdWrap()) { - // need to determine if wrapping will occur: - int width = getTerminal().getWidth(); - int pos = getCursorPosition(); - for (int i = 0; i < chars.length; i++) { - print(chars[i]); - if ((pos + i + 1) % width == 0) { - print(32); // move cursor to next line by printing dummy space - print(13); // CR / not newline. - } - } - } else { - print(chars); - } - clearAhead(clear, chars.length); - if (getTerminal().isAnsiSupported()) { - if (chars.length > 0) { - back(chars.length); - } - } else { - back(chars.length); - } - } - if (getTerminal().hasWeirdWrap()) { - int width = getTerminal().getWidth(); - // best guess on whether the cursor is in that weird location... - // Need to do this without calling ansi cursor location methods - // otherwise it breaks paste of wrapped lines in xterm. - if (getCursorPosition() > 0 && (getCursorPosition() % width == 0) - && buf.cursor == buf.length() && clear == 0) { - // the following workaround is reverse-engineered from looking - // at what bash sent to the terminal in the same situation - print(32); // move cursor to next line by printing dummy space - print(13); // CR / not newline. - } - } - } - - /** - * Redraw the rest of the buffer from the cursor onwards. This is necessary - * for inserting text into the buffer. - */ - private void drawBuffer() throws IOException { - drawBuffer(0); - } - - /** - * Clear ahead the specified number of characters without moving the cursor. - * - * @param num the number of characters to clear - * @param delta the difference between the internal cursor and the screen - * cursor - if > 0, assume some stuff was printed and weird wrap has to be - * checked - */ - private void clearAhead(final int num, int delta) throws IOException { - if (num == 0) { - return; - } - - if (getTerminal().isAnsiSupported()) { - int width = getTerminal().getWidth(); - int screenCursorCol = getCursorPosition() + delta; - // clear current line - printAnsiSequence("K"); - // if cursor+num wraps, then we need to clear the line(s) below too - int curCol = screenCursorCol % width; - int endCol = (screenCursorCol + num - 1) % width; - int lines = num / width; - if (endCol < curCol) lines++; - for (int i = 0; i < lines; i++) { - printAnsiSequence("B"); - printAnsiSequence("2K"); - } - for (int i = 0; i < lines; i++) { - printAnsiSequence("A"); - } - return; - } - - // print blank extra characters - print(' ', num); - - // we need to flush here so a "clever" console doesn't just ignore the redundancy - // of a space followed by a backspace. -// flush(); - - // reset the visual cursor - back(num); - -// flush(); - } - - /** - * Move the visual cursor backwards without modifying the buffer cursor. - */ - protected void back(final int num) throws IOException { - if (num == 0) return; - if (getTerminal().isAnsiSupported()) { - int width = getTerminal().getWidth(); - int cursor = getCursorPosition(); - int realCursor = cursor + num; - int realCol = realCursor % width; - int newCol = cursor % width; - int moveup = num / width; - int delta = realCol - newCol; - if (delta < 0) moveup++; - if (moveup > 0) { - printAnsiSequence(moveup + "A"); - } - printAnsiSequence((1 + newCol) + "G"); - return; - } - print(BACKSPACE, num); -// flush(); - } - - /** - * Flush the console output stream. This is important for printout out single characters (like a backspace or - * keyboard) that we want the console to handle immediately. - */ - public void flush() throws IOException { - out.flush(); - } - - private int backspaceAll() throws IOException { - return backspace(Integer.MAX_VALUE); - } - - /** - * Issue <em>num</em> backspaces. - * - * @return the number of characters backed up - */ - private int backspace(final int num) throws IOException { - if (buf.cursor == 0) { - return 0; - } - - int count = 0; - - int termwidth = getTerminal().getWidth(); - int lines = getCursorPosition() / termwidth; - count = moveCursor(-1 * num) * -1; - buf.buffer.delete(buf.cursor, buf.cursor + count); - if (getCursorPosition() / termwidth != lines) { - if (getTerminal().isAnsiSupported()) { - // debug("doing backspace redraw: " + getCursorPosition() + " on " + termwidth + ": " + lines); - printAnsiSequence("K"); - // if cursor+num wraps, then we need to clear the line(s) below too - // last char printed is one pos less than cursor so we subtract - // one -/* - // TODO: fixme (does not work - test with reverse search with wrapping line and CTRL-E) - int endCol = (getCursorPosition() + num - 1) % termwidth; - int curCol = getCursorPosition() % termwidth; - if (endCol < curCol) lines++; - for (int i = 1; i < lines; i++) { - printAnsiSequence("B"); - printAnsiSequence("2K"); - } - for (int i = 1; i < lines; i++) { - printAnsiSequence("A"); - } - return count; -*/ - } - } - drawBuffer(count); - - return count; - } - - /** - * Issue a backspace. - * - * @return true if successful - */ - public boolean backspace() throws IOException { - return backspace(1) == 1; - } - - protected boolean moveToEnd() throws IOException { - return moveCursor(buf.length() - buf.cursor) > 0; - } - - /** - * Delete the character at the current position and redraw the remainder of the buffer. - */ - private boolean deleteCurrentCharacter() throws IOException { - if (buf.length() == 0 || buf.cursor == buf.length()) { - return false; - } - - buf.buffer.deleteCharAt(buf.cursor); - drawBuffer(1); - return true; - } - - private boolean previousWord() throws IOException { - while (isDelimiter(buf.charLeftOfCursor()) && (moveCursor(-1) != 0)) { - // nothing - } - - while (!isDelimiter(buf.charLeftOfCursor()) && (moveCursor(-1) != 0)) { - // nothing - } - - return true; - } - - private boolean nextWord() throws IOException { - while (isDelimiter(buf.charAtCursor()) && (moveCursor(1) != 0)) { - // nothing - } - - while (!isDelimiter(buf.charAtCursor()) && (moveCursor(1) != 0)) { - // nothing - } - - return true; - } - - private boolean deletePreviousWord() throws IOException { - while (isDelimiter(buf.charLeftOfCursor()) && backspace()) { - // nothing - } - - while (!isDelimiter(buf.charLeftOfCursor()) && backspace()) { - // nothing - } - - return true; - } - - private boolean deleteNextWord() throws IOException { - while (isDelimiter(buf.charAtCursor()) && deleteCurrentCharacter()) { - // nothing - } - - while (!isDelimiter(buf.charAtCursor()) && deleteCurrentCharacter()) { - // nothing - } - - return true; - } - - /** - * Move the cursor <i>where</i> characters. - * - * @param num If less than 0, move abs(<i>where</i>) to the left, otherwise move <i>where</i> to the right. - * @return The number of spaces we moved - */ - public int moveCursor(final int num) throws IOException { - int where = num; - - if ((buf.cursor == 0) && (where <= 0)) { - return 0; - } - - if ((buf.cursor == buf.buffer.length()) && (where >= 0)) { - return 0; - } - - if ((buf.cursor + where) < 0) { - where = -buf.cursor; - } - else if ((buf.cursor + where) > buf.buffer.length()) { - where = buf.buffer.length() - buf.cursor; - } - - moveInternal(where); - - return where; - } - - /** - * Move the cursor <i>where</i> characters, without checking the current buffer. - * - * @param where the number of characters to move to the right or left. - */ - private void moveInternal(final int where) throws IOException { - // debug ("move cursor " + where + " (" - // + buf.cursor + " => " + (buf.cursor + where) + ")"); - buf.cursor += where; - - if (getTerminal().isAnsiSupported()) { - if (where < 0) { - back(Math.abs(where)); - } else { - int width = getTerminal().getWidth(); - int cursor = getCursorPosition(); - int oldLine = (cursor - where) / width; - int newLine = cursor / width; - if (newLine > oldLine) { - if (getTerminal().hasWeirdWrap()) { - // scroll up if at bottom - // note: - // on rxvt cywgin getTerminal().getHeight() is incorrect - // MacOs xterm does not seem to support scrolling - if (getCurrentAnsiRow() == getTerminal().getHeight()) { - printAnsiSequence((newLine - oldLine) + "S"); - } - } - printAnsiSequence((newLine - oldLine) + "B"); - } - printAnsiSequence(1 +(cursor % width) + "G"); - } -// flush(); - return; - } - - char c; - - if (where < 0) { - int len = 0; - for (int i = buf.cursor; i < buf.cursor - where; i++) { - if (buf.buffer.charAt(i) == '\t') { - len += TAB_WIDTH; - } - else { - len++; - } - } - - char chars[] = new char[len]; - Arrays.fill(chars, BACKSPACE); - out.write(chars); - - return; - } - else if (buf.cursor == 0) { - return; - } - else if (mask != null) { - c = mask; - } - else { - print(buf.buffer.substring(buf.cursor - where, buf.cursor).toCharArray()); - return; - } - - // null character mask: don't output anything - if (mask == NULL_MASK) { - return; - } - - print(c, Math.abs(where)); - } - - // FIXME: replace() is not used - - public final boolean replace(final int num, final String replacement) { - buf.buffer.replace(buf.cursor - num, buf.cursor, replacement); - try { - moveCursor(-num); - drawBuffer(Math.max(0, num - replacement.length())); - moveCursor(replacement.length()); - } - catch (IOException e) { - e.printStackTrace(); - return false; - } - return true; - } - - // - // Key reading - // - - /** - * Read a character from the console. - * - * @return the character, or -1 if an EOF is received. - */ - public final int readVirtualKey() throws IOException { - int c = getTerminal().readVirtualKey(in); - - Log.trace("Keystroke: ", c); - - // clear any echo characters - clearEcho(c); - - return c; - } - - /** - * Clear the echoed characters for the specified character code. - */ - private int clearEcho(final int c) throws IOException { - // if the terminal is not echoing, then ignore - if (!getTerminal().isEchoEnabled()) { - return 0; - } - - // otherwise, clear - int num = countEchoCharacters((char) c); - back(num); - drawBuffer(num); - - return num; - } - - private int countEchoCharacters(final char c) { - // tabs as special: we need to determine the number of spaces - // to cancel based on what out current cursor position is - if (c == 9) { - int tabStop = 8; // will this ever be different? - int position = getCursorPosition(); - - return tabStop - (position % tabStop); - } - - return getPrintableCharacters(c).length(); - } - - /** - * Return the number of characters that will be printed when the specified - * character is echoed to the screen - * - * Adapted from cat by Torbjorn Granlund, as repeated in stty by David MacKenzie. - */ - private StringBuilder getPrintableCharacters(final char ch) { - StringBuilder sbuff = new StringBuilder(); - - if (ch >= 32) { - if (ch < 127) { - sbuff.append(ch); - } - else if (ch == 127) { - sbuff.append('^'); - sbuff.append('?'); - } - else { - sbuff.append('M'); - sbuff.append('-'); - - if (ch >= (128 + 32)) { - if (ch < (128 + 127)) { - sbuff.append((char) (ch - 128)); - } - else { - sbuff.append('^'); - sbuff.append('?'); - } - } - else { - sbuff.append('^'); - sbuff.append((char) (ch - 128 + 64)); - } - } - } - else { - sbuff.append('^'); - sbuff.append((char) (ch + 64)); - } - - return sbuff; - } - - public final int readCharacter(final char... allowed) throws IOException { - // if we restrict to a limited set and the current character is not in the set, then try again. - char c; - - Arrays.sort(allowed); // always need to sort before binarySearch - - while (Arrays.binarySearch(allowed, c = (char) readVirtualKey()) < 0) { - // nothing - } - - return c; - } - - // - // Key Bindings - // - - public static final String JLINE_COMPLETION_THRESHOLD = "jline.completion.threshold"; - - public static final String JLINE_KEYBINDINGS = "jline.keybindings"; - - public static final String JLINEBINDINGS_PROPERTIES = ".jlinebindings.properties"; - - /** - * The map for logical operations. - */ - private final short[] keyBindings; - - private short[] loadKeyBindings(InputStream input) throws IOException { - if (input == null) { - try { - File file = new File(Configuration.getUserHome(), JLINEBINDINGS_PROPERTIES); - - String path = Configuration.getString(JLINE_KEYBINDINGS); - if (path != null) { - file = new File(path); - } - - if (file.isFile()) { - Log.debug("Loading user bindings from: ", file); - input = new FileInputStream(file); - } - } - catch (Exception e) { - Log.error("Failed to load user bindings", e); - } - } - - if (input == null) { - Log.debug("Using default bindings"); - input = getTerminal().getDefaultBindings(); - } - - short[] keyBindings = new short[Character.MAX_VALUE * 2]; - - Arrays.fill(keyBindings, Operation.UNKNOWN.code); - - // Loads the key bindings. Bindings file is in the format: - // - // keycode: operation name - - if (input != null) { - input = new BufferedInputStream(input); - Properties p = new Properties(); - p.load(input); - input.close(); - - for (Object key : p.keySet()) { - String val = (String) key; - - try { - short code = Short.parseShort(val); - String name = p.getProperty(val); - Operation op = Operation.valueOf(name); - keyBindings[code] = op.code; - } - catch (NumberFormatException e) { - Log.error("Failed to convert binding code: ", val, e); - } - } - - // hardwired arrow key bindings - // keybindings[VK_UP] = PREV_HISTORY; - // keybindings[VK_DOWN] = NEXT_HISTORY; - // keybindings[VK_LEFT] = PREV_CHAR; - // keybindings[VK_RIGHT] = NEXT_CHAR; - } - - return keyBindings; - } - - int getKeyForAction(final short logicalAction) { - for (int i = 0; i < keyBindings.length; i++) { - if (keyBindings[i] == logicalAction) { - return i; - } - } - - return -1; - } - - int getKeyForAction(final Operation op) { - assert op != null; - return getKeyForAction(op.code); - } - - public void printBindings() { - System.out.println("printBindings(): keyBindings.length = " + keyBindings.length); - for (int i = 0; i < keyBindings.length; i++) { - if (keyBindings[i] != Operation.UNKNOWN.code) { - System.out.println("keyBindings[" + i + "] = " + keyBindings[i]); - } - } - } - - /** - * Reads the console input and returns an array of the form [raw, key binding]. - */ - private int[] readBinding() throws IOException { - int c = readVirtualKey(); - - if (c == -1) { - return null; - } - - // extract the appropriate key binding - short code = keyBindings[c]; - - Log.trace("Translated: ", c, " -> ", code); - - return new int[]{c, code}; - } - - // - // Line Reading - // - - /** - * Read the next line and return the contents of the buffer. - */ - public String readLine() throws IOException { - return readLine((String) null); - } - - /** - * Read the next line with the specified character mask. If null, then - * characters will be echoed. If 0, then no characters will be echoed. - */ - public String readLine(final Character mask) throws IOException { - return readLine(null, mask); - } - - public String readLine(final String prompt) throws IOException { - return readLine(prompt, null); - } - - /** - * Read a line from the <i>in</i> {@link InputStream}, and return the line - * (without any trailing newlines). - * - * @param prompt The prompt to issue to the console, may be null. - * @return A line that is read from the terminal, or null if there was null input (e.g., <i>CTRL-D</i> - * was pressed). - */ - public String readLine(String prompt, final Character mask) throws IOException { - // prompt may be null - // mask may be null - - // FIXME: This blows, each call to readLine will reset the console's state which doesn't seem very nice. - this.mask = mask; - if (prompt != null) { - setPrompt(prompt); - } - else { - prompt = getPrompt(); - } - - try { - if (!getTerminal().isSupported()) { - beforeReadLine(prompt, mask); - } - - if (prompt != null && prompt.length() > 0) { - out.write(prompt); - out.flush(); - } - - // if the terminal is unsupported, just use plain-java reading - if (!getTerminal().isSupported()) { - return readLine(in); - } - - String originalPrompt = this.prompt; - - final int NORMAL = 1; - final int SEARCH = 2; - int state = NORMAL; - - boolean success = true; - - while (true) { - int[] next = readBinding(); - - if (next == null) { - return null; - } - - int c = next[0]; - // int code = next[1]; - Operation code = Operation.valueOf(next[1]); - - if (c == -1) { - return null; - } - - // Search mode. - // - // Note that we have to do this first, because if there is a command - // not linked to a search command, we leave the search mode and fall - // through to the normal state. - if (state == SEARCH) { - int cursorDest = -1; - - switch (code) { - // This doesn't work right now, it seems CTRL-G is not passed - // down correctly. :( - case ABORT: - state = NORMAL; - break; - - case SEARCH_PREV: - if (searchTerm.length() == 0) { - searchTerm.append(previousSearchTerm); - } - - if (searchIndex == -1) { - searchIndex = searchBackwards(searchTerm.toString()); - } else { - searchIndex = searchBackwards(searchTerm.toString(), searchIndex); - } - break; - - case DELETE_PREV_CHAR: - if (searchTerm.length() > 0) { - searchTerm.deleteCharAt(searchTerm.length() - 1); - searchIndex = searchBackwards(searchTerm.toString()); - } - break; - - case UNKNOWN: - searchTerm.appendCodePoint(c); - searchIndex = searchBackwards(searchTerm.toString()); - break; - - default: - // Set buffer and cursor position to the found string. - if (searchIndex != -1) { - history.moveTo(searchIndex); - // set cursor position to the found string - cursorDest = history.current().toString().indexOf(searchTerm.toString()); - } - state = NORMAL; - break; - } - - // if we're still in search mode, print the search status - if (state == SEARCH) { - if (searchTerm.length() == 0) { - printSearchStatus("", ""); - searchIndex = -1; - } else { - if (searchIndex == -1) { - beep(); - } else { - printSearchStatus(searchTerm.toString(), history.get(searchIndex).toString()); - } - } - } - // otherwise, restore the line - else { - restoreLine(originalPrompt, cursorDest); - } - } - - if (state == NORMAL) { - switch (code) { - case EXIT: // ctrl-d - if (buf.buffer.length() == 0) { - return null; - } else { - success = deleteCurrentCharacter(); - } - break; - - case COMPLETE: // tab - success = complete(); - break; - - case MOVE_TO_BEG: - success = setCursorPosition(0); - break; - - case KILL_LINE: // CTRL-K - success = killLine(); - break; - - case CLEAR_SCREEN: // CTRL-L - success = clearScreen(); - break; - - case KILL_LINE_PREV: // CTRL-U - success = resetLine(); - break; - - case NEWLINE: // enter - moveToEnd(); - println(); // output newline - flush(); - return finishBuffer(); - - case DELETE_PREV_CHAR: // backspace - success = backspace(); - break; - - case DELETE_NEXT_CHAR: // delete - success = deleteCurrentCharacter(); - break; - - case MOVE_TO_END: - success = moveToEnd(); - break; - - case PREV_CHAR: - success = moveCursor(-1) != 0; - break; - - case NEXT_CHAR: - success = moveCursor(1) != 0; - break; - - case NEXT_HISTORY: - success = moveHistory(true); - break; - - case PREV_HISTORY: - success = moveHistory(false); - break; - - case ABORT: - case REDISPLAY: - break; - - case PASTE: - success = paste(); - break; - - case DELETE_PREV_WORD: - success = deletePreviousWord(); - break; - - case DELETE_NEXT_WORD: - success = deleteNextWord(); - break; - - case PREV_WORD: - success = previousWord(); - break; - - case NEXT_WORD: - success = nextWord(); - break; - - case START_OF_HISTORY: - success = history.moveToFirst(); - if (success) { - setBuffer(history.current()); - } - break; - - case END_OF_HISTORY: - success = history.moveToLast(); - if (success) { - setBuffer(history.current()); - } - break; - - case CLEAR_LINE: - moveInternal(-(buf.cursor)); - killLine(); - break; - - case INSERT: - buf.setOverTyping(!buf.isOverTyping()); - break; - - case SEARCH_PREV: // CTRL-R - if (searchTerm != null) { - previousSearchTerm = searchTerm.toString(); - } - searchTerm = new StringBuffer(buf.buffer); - state = SEARCH; - if (searchTerm.length() > 0) { - searchIndex = searchBackwards(searchTerm.toString()); - if (searchIndex == -1) { - beep(); - } - printSearchStatus(searchTerm.toString(), - searchIndex > -1 ? history.get(searchIndex).toString() : ""); - } else { - searchIndex = -1; - printSearchStatus("", ""); - } - break; - - case UNKNOWN: - default: - if (c != 0) { // ignore null chars - ActionListener action = triggeredActions.get((char) c); - if (action != null) { - action.actionPerformed(null); - } - else { - putChar(c, true); - } - } - else { - success = false; - } - } - - if (!success) { - beep(); - } - - flush(); - } - } - } - finally { - if (!getTerminal().isSupported()) { - afterReadLine(); - } - } - } - - /** - * Read a line for unsupported terminals. - */ - private String readLine(final InputStream in) throws IOException { - StringBuilder buff = new StringBuilder(); - - while (true) { - int i = in.read(); - - if (i == -1 || i == '\n' || i == '\r') { - return buff.toString(); - } - - buff.append((char) i); - } - - // return new BufferedReader (new InputStreamReader (in)).readLine (); - } - - // - // Completion - // - - private final List<Completer> completers = new LinkedList<Completer>(); - - private CompletionHandler completionHandler = new CandidateListCompletionHandler(); - - /** - * Add the specified {@link jline.console.completer.Completer} to the list of handlers for tab-completion. - * - * @param completer the {@link jline.console.completer.Completer} to add - * @return true if it was successfully added - */ - public boolean addCompleter(final Completer completer) { - return completers.add(completer); - } - - /** - * Remove the specified {@link jline.console.completer.Completer} from the list of handlers for tab-completion. - * - * @param completer The {@link Completer} to remove - * @return True if it was successfully removed - */ - public boolean removeCompleter(final Completer completer) { - return completers.remove(completer); - } - - /** - * Returns an unmodifiable list of all the completers. - */ - public Collection<Completer> getCompleters() { - return Collections.unmodifiableList(completers); - } - - public void setCompletionHandler(final CompletionHandler handler) { - assert handler != null; - this.completionHandler = handler; - } - - public CompletionHandler getCompletionHandler() { - return this.completionHandler; - } - - /** - * Use the completers to modify the buffer with the appropriate completions. - * - * @return true if successful - */ - protected boolean complete() throws IOException { - // debug ("tab for (" + buf + ")"); - if (completers.size() == 0) { - return false; - } - - List<CharSequence> candidates = new LinkedList<CharSequence>(); - String bufstr = buf.buffer.toString(); - int cursor = buf.cursor; - - int position = -1; - - for (Completer comp : completers) { - if ((position = comp.complete(bufstr, cursor, candidates)) != -1) { - break; - } - } - - return candidates.size() != 0 && getCompletionHandler().complete(this, candidates, position); - } - - /** - * The number of tab-completion candidates above which a warning will be - * prompted before showing all the candidates. - */ - private int autoprintThreshold = Integer.getInteger(JLINE_COMPLETION_THRESHOLD, 100); // same default as bash - - /** - * @param threshold the number of candidates to print without issuing a warning. - */ - public void setAutoprintThreshold(final int threshold) { - this.autoprintThreshold = threshold; - } - - /** - * @return the number of candidates to print without issuing a warning. - */ - public int getAutoprintThreshold() { - return autoprintThreshold; - } - - private boolean paginationEnabled; - - /** - * Whether to use pagination when the number of rows of candidates exceeds the height of the terminal. - */ - public void setPaginationEnabled(final boolean enabled) { - this.paginationEnabled = enabled; - } - - /** - * Whether to use pagination when the number of rows of candidates exceeds the height of the terminal. - */ - public boolean isPaginationEnabled() { - return paginationEnabled; - } - - // - // History - // - - private History history = new MemoryHistory(); - - public void setHistory(final History history) { - this.history = history; - } - - public History getHistory() { - return history; - } - - private boolean historyEnabled = true; - - /** - * Whether or not to add new commands to the history buffer. - */ - public void setHistoryEnabled(final boolean enabled) { - this.historyEnabled = enabled; - } - - /** - * Whether or not to add new commands to the history buffer. - */ - public boolean isHistoryEnabled() { - return historyEnabled; - } - - /** - * Move up or down the history tree. - */ - private boolean moveHistory(final boolean next) throws IOException { - if (next && !history.next()) { - return false; - } - else if (!next && !history.previous()) { - return false; - } - - setBuffer(history.current()); - - return true; - } - - // - // Printing - // - - public static final String CR = System.getProperty("line.separator"); - - /** - * Output the specified character to the output stream without manipulating the current buffer. - */ - private void print(final int c) throws IOException { - if (c == '\t') { - char chars[] = new char[TAB_WIDTH]; - Arrays.fill(chars, ' '); - out.write(chars); - return; - } - - out.write(c); - } - - /** - * Output the specified characters to the output stream without manipulating the current buffer. - */ - private void print(final char... buff) throws IOException { - int len = 0; - for (char c : buff) { - if (c == '\t') { - len += TAB_WIDTH; - } - else { - len++; - } - } - - char chars[]; - if (len == buff.length) { - chars = buff; - } - else { - chars = new char[len]; - int pos = 0; - for (char c : buff) { - if (c == '\t') { - Arrays.fill(chars, pos, pos + TAB_WIDTH, ' '); - pos += TAB_WIDTH; - } - else { - chars[pos] = c; - pos++; - } - } - } - - out.write(chars); - } - - private void print(final char c, final int num) throws IOException { - if (num == 1) { - print(c); - } - else { - char[] chars = new char[num]; - Arrays.fill(chars, c); - print(chars); - } - } - - /** - * Output the specified string to the output stream (but not the buffer). - */ - public final void print(final CharSequence s) throws IOException { - assert s != null; - print(s.toString().toCharArray()); - } - - public final void println(final CharSequence s) throws IOException { - assert s != null; - print(s.toString().toCharArray()); - println(); - } - - /** - * Output a platform-dependent newline. - */ - public final void println() throws IOException { - print(CR); -// flush(); - } - - // - // Actions - // - - /** - * Issue a delete. - * - * @return true if successful - */ - public final boolean delete() throws IOException { - return delete(1) == 1; - } - - // FIXME: delete(int) only used by above + the return is always 1 and num is ignored - - /** - * Issue <em>num</em> deletes. - * - * @return the number of characters backed up - */ - private int delete(final int num) throws IOException { - // TODO: Try to use jansi for this - - /* Commented out because of DWA-2949: - if (buf.cursor == 0) { - return 0; - } - */ - - buf.buffer.delete(buf.cursor, buf.cursor + 1); - drawBuffer(1); - - return 1; - } - - /** - * Kill the buffer ahead of the current cursor position. - * - * @return true if successful - */ - public boolean killLine() throws IOException { - int cp = buf.cursor; - int len = buf.buffer.length(); - - if (cp >= len) { - return false; - } - - int num = buf.buffer.length() - cp; - clearAhead(num, 0); - - for (int i = 0; i < num; i++) { - buf.buffer.deleteCharAt(len - i - 1); - } - - return true; - } - - /** - * Clear the screen by issuing the ANSI "clear screen" code. - */ - public boolean clearScreen() throws IOException { - if (!getTerminal().isAnsiSupported()) { - return false; - } - - // send the ANSI code to clear the screen - printAnsiSequence("2J"); - - // then send the ANSI code to go to position 1,1 - printAnsiSequence("1;1H"); - - redrawLine(); - - return true; - } - - /** - * Issue an audible keyboard bell, if {@link #isBellEnabled} return true. - */ - public void beep() throws IOException { - if (isBellEnabled()) { - print(KEYBOARD_BELL); - // need to flush so the console actually beeps - flush(); - } - } - - /** - * Paste the contents of the clipboard into the console buffer - * - * @return true if clipboard contents pasted - */ - public boolean paste() throws IOException { - Clipboard clipboard; - try { // May throw ugly exception on system without X - clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); - } - catch (Exception e) { - return false; - } - - if (clipboard == null) { - return false; - } - - Transferable transferable = clipboard.getContents(null); - - if (transferable == null) { - return false; - } - - try { - Object content = transferable.getTransferData(DataFlavor.plainTextFlavor); - - // This fix was suggested in bug #1060649 at - // http://sourceforge.net/tracker/index.php?func=detail&aid=1060649&group_id=64033&atid=506056 - // to get around the deprecated DataFlavor.plainTextFlavor, but it - // raises a UnsupportedFlavorException on Mac OS X - - if (content == null) { - try { - content = new DataFlavor().getReaderForText(transferable); - } - catch (Exception e) { - // ignore - } - } - - if (content == null) { - return false; - } - - String value; - - if (content instanceof Reader) { - // TODO: we might want instead connect to the input stream - // so we can interpret individual lines - value = ""; - String line; - - BufferedReader read = new BufferedReader((Reader) content); - while ((line = read.readLine()) != null) { - if (value.length() > 0) { - value += "\n"; - } - - value += line; - } - } - else { - value = content.toString(); - } - - if (value == null) { - return true; - } - - putString(value); - - return true; - } - catch (UnsupportedFlavorException e) { - Log.error("Paste failed: ", e); - - return false; - } - } - - // - // Triggered Actions - // - - private final Map<Character, ActionListener> triggeredActions = new HashMap<Character, ActionListener>(); - - /** - * Adding a triggered Action allows to give another curse of action if a character passed the pre-processing. - * <p/> - * Say you want to close the application if the user enter q. - * addTriggerAction('q', new ActionListener(){ System.exit(0); }); would do the trick. - */ - public void addTriggeredAction(final char c, final ActionListener listener) { - triggeredActions.put(c, listener); - } - - // - // Formatted Output - // - - /** - * Output the specified {@link Collection} in proper columns. - */ - public void printColumns(final Collection<? extends CharSequence> items) throws IOException { - if (items == null || items.isEmpty()) { - return; - } - - int width = getTerminal().getWidth(); - int height = getTerminal().getHeight(); - - int maxWidth = 0; - for (CharSequence item : items) { - maxWidth = Math.max(maxWidth, item.length()); - } - Log.debug("Max width: ", maxWidth); - - int showLines; - if (isPaginationEnabled()) { - showLines = height - 1; // page limit - } - else { - showLines = Integer.MAX_VALUE; - } - - StringBuilder buff = new StringBuilder(); - for (CharSequence item : items) { - if ((buff.length() + maxWidth) > width) { - println(buff); - buff.setLength(0); - - if (--showLines == 0) { - // Overflow - print(resources.getString("display-more")); - flush(); - int c = readVirtualKey(); - if (c == '\r' || c == '\n') { - // one step forward - showLines = 1; - } - else if (c != 'q') { - // page forward - showLines = height - 1; - } - - back(resources.getString("display-more").length()); - if (c == 'q') { - // cancel - break; - } - } - } - - // NOTE: toString() is important here due to AnsiString being retarded - buff.append(item.toString()); - for (int i = 0; i < (maxWidth + 3 - item.length()); i++) { - buff.append(' '); - } - } - - if (buff.length() > 0) { - println(buff); - } - } - - // - // Non-supported Terminal Support - // - - private Thread maskThread; - - private void beforeReadLine(final String prompt, final Character mask) { - if (mask != null && maskThread == null) { - final String fullPrompt = "\r" + prompt - + " " - + " " - + " " - + "\r" + prompt; - - maskThread = new Thread() - { - public void run() { - while (!interrupted()) { - try { - Writer out = getOutput(); - out.write(fullPrompt); - out.flush(); - sleep(3); - } - catch (IOException e) { - return; - } - catch (InterruptedException e) { - return; - } - } - } - }; - - maskThread.setPriority(Thread.MAX_PRIORITY); - maskThread.setDaemon(true); - maskThread.start(); - } - } - - private void afterReadLine() { - if (maskThread != null && maskThread.isAlive()) { - maskThread.interrupt(); - } - - maskThread = null; - } - - /** - * Erases the current line with the existing prompt, then redraws the line - * with the provided prompt and buffer - * @param prompt - * the new prompt - * @param buffer - * the buffer to be drawn - * @param cursorDest - * where you want the cursor set when the line has been drawn. - * -1 for end of line. - * */ - public void resetPromptLine(String prompt, String buffer, int cursorDest) throws IOException { - // move cursor to end of line - moveToEnd(); - - // backspace all text, including prompt - buf.buffer.append(this.prompt); - buf.cursor += this.prompt.length(); - this.prompt = ""; - backspaceAll(); - - this.prompt = prompt; - redrawLine(); - setBuffer(buffer); - - // move cursor to destination (-1 will move to end of line) - if (cursorDest < 0) cursorDest = buffer.length(); - setCursorPosition(cursorDest); - - flush(); - } - - public void printSearchStatus(String searchTerm, String match) throws IOException { - String prompt = "(reverse-i-search)`" + searchTerm + "': "; - String buffer = match; - int cursorDest = match.indexOf(searchTerm); - resetPromptLine(prompt, buffer, cursorDest); - } - - public void restoreLine(String originalPrompt, int cursorDest) throws IOException { - // TODO move cursor to matched string - String prompt = lastLine(originalPrompt); - String buffer = buf.buffer.toString(); - resetPromptLine(prompt, buffer, cursorDest); - } - - // - // History search - // - /** - * Search backward in history from a given position. - * - * @param searchTerm substring to search for. - * @param startIndex the index from which on to search - * @return index where this substring has been found, or -1 else. - */ - public int searchBackwards(String searchTerm, int startIndex) { - return searchBackwards(searchTerm, startIndex, false); - } - - /** - * Search backwards in history from the current position. - * - * @param searchTerm substring to search for. - * @return index where the substring has been found, or -1 else. - */ - public int searchBackwards(String searchTerm) { - return searchBackwards(searchTerm, history.index()); - } - - - public int searchBackwards(String searchTerm, int startIndex, boolean startsWith) { - ListIterator<History.Entry> it = history.entries(startIndex); - while (it.hasPrevious()) { - History.Entry e = it.previous(); - if (startsWith) { - if (e.value().toString().startsWith(searchTerm)) { - return e.index(); - } - } else { - if (e.value().toString().contains(searchTerm)) { - return e.index(); - } - } - } - return -1; - } - - // - // Helpers - // - - /** - * Checks to see if the specified character is a delimiter. We consider a - * character a delimiter if it is anything but a letter or digit. - * - * @param c The character to test - * @return True if it is a delimiter - */ - private boolean isDelimiter(final char c) { - return !Character.isLetterOrDigit(c); - } - - private void printAnsiSequence(String sequence) throws IOException { - print(27); - print('['); - print(sequence); - flush(); // helps with step debugging - } - - // return column position, reported by the terminal - private int getCurrentPosition() { - // check for ByteArrayInputStream to disable for unit tests - if (getTerminal().isAnsiSupported() && !(in instanceof ByteArrayInputStream)) { - try { - printAnsiSequence("6n"); - flush(); - StringBuffer b = new StringBuffer(8); - // position is sent as <ESC>[{ROW};{COLUMN}R - int r; - while((r = in.read()) > -1 && r != 'R') { - if (r != 27 && r != '[') { - b.append((char) r); - } - } - String[] pos = b.toString().split(";"); - return Integer.parseInt(pos[1]); - } catch (Exception x) { - // no luck - } - } - - return -1; // TODO: throw exception instead? - } - - // return row position, reported by the terminal - // needed to know whether to scroll up on cursor move in last col for weird - // wrapping terminals - not tested for anything else - private int getCurrentAnsiRow() { - // check for ByteArrayInputStream to disable for unit tests - if (getTerminal().isAnsiSupported() && !(in instanceof ByteArrayInputStream)) { - try { - printAnsiSequence("6n"); - flush(); - StringBuffer b = new StringBuffer(8); - // position is sent as <ESC>[{ROW};{COLUMN}R - int r; - while((r = in.read()) > -1 && r != 'R') { - if (r != 27 && r != '[') { - b.append((char) r); - } - } - String[] pos = b.toString().split(";"); - return Integer.parseInt(pos[0]); - } catch (Exception x) { - // no luck - } - } - - return -1; // TODO: throw exception instead? - } -} diff --git a/src/jline/src/main/java/scala/tools/jline/console/CursorBuffer.java b/src/jline/src/main/java/scala/tools/jline/console/CursorBuffer.java deleted file mode 100644 index 7993def002..0000000000 --- a/src/jline/src/main/java/scala/tools/jline/console/CursorBuffer.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ - -package scala.tools.jline.console; - -/** - * A holder for a {@link StringBuilder} that also contains the current cursor position. - * - * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> - * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> - * @since 2.0 - */ -public class CursorBuffer -{ - private boolean overTyping = false; - - public int cursor = 0; - - public final StringBuilder buffer = new StringBuilder(); - - public boolean isOverTyping() { - return overTyping; - } - - public void setOverTyping(final boolean b) { - overTyping = b; - } - - public int length() { - return buffer.length(); - } - - /** - * Gets the character to the left of the cursor. - */ - public char charLeftOfCursor() { - if (cursor <= 0) { - return 0; - } - - return buffer.charAt(cursor - 1); - } - - /** - * Gets the character at the cursor. - */ - public char charAtCursor() { - if (cursor < 0 || cursor >= buffer.length()) { - return 0; - } - return buffer.charAt(cursor); - } - - /** - * Write the specific character into the buffer, setting the cursor position - * ahead one. The text may overwrite or insert based on the current setting - * of {@link #isOverTyping}. - * - * @param c the character to insert - */ - public void write(final char c) { - buffer.insert(cursor++, c); - if (isOverTyping() && cursor < buffer.length()) { - buffer.deleteCharAt(cursor); - } - } - - /** - * Insert the specified chars into the buffer, setting the cursor to the end of the insertion point. - */ - public void write(final CharSequence str) { - assert str != null; - - if (buffer.length() == 0) { - buffer.append(str); - } - else { - buffer.insert(cursor, str); - } - - cursor += str.length(); - - if (isOverTyping() && cursor < buffer.length()) { - buffer.delete(cursor, (cursor + str.length())); - } - } - - public boolean clear() { - if (buffer.length() == 0) { - return false; - } - - buffer.delete(0, buffer.length()); - cursor = 0; - return true; - } - - @Override - public String toString() { - return buffer.toString(); - } -} diff --git a/src/jline/src/main/java/scala/tools/jline/console/Key.java b/src/jline/src/main/java/scala/tools/jline/console/Key.java deleted file mode 100644 index 2e713a7da2..0000000000 --- a/src/jline/src/main/java/scala/tools/jline/console/Key.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ - -package scala.tools.jline.console; - -import java.util.HashMap; -import java.util.Map; - -/** - * Map from key name to key codes. - * - * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> - * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> - * @see java.awt.event.KeyEvent - * @since 2.0 - */ -public enum Key -{ - CTRL_A(1), - - CTRL_B(2), - - CTRL_C(3), - - CTRL_D(4), - - CTRL_E(5), - - CTRL_F(6), - - CTRL_G(7), - - CTRL_K(11), - - CTRL_L(12), - - CTRL_N(14), - - CTRL_O(15), - - CTRL_P(16), - - CTRL_T(20), - - CTRL_W(23), - - CTRL_X(24), - - CTRL_OB(27), - - CTRL_QM(127), - - BACKSPACE('\b'), - - DELETE(127),; - - public final short code; - - Key(final int code) { - this.code = (short) code; - } - - private static final Map<Short, Key> codes; - - static { - Map<Short, Key> map = new HashMap<Short, Key>(); - - for (Key op : Key.values()) { - map.put(op.code, op); - } - - codes = map; - } - - public static Key valueOf(final int code) { - return codes.get((short) code); - } -}
\ No newline at end of file diff --git a/src/jline/src/main/java/scala/tools/jline/console/Operation.java b/src/jline/src/main/java/scala/tools/jline/console/Operation.java deleted file mode 100644 index 59ee878d45..0000000000 --- a/src/jline/src/main/java/scala/tools/jline/console/Operation.java +++ /dev/null @@ -1,291 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ - -package scala.tools.jline.console; - -import java.util.HashMap; -import java.util.Map; - -/** - * Map for console operation to virtual key bindings. - * - * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> - * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> - * @see java.awt.event.KeyEvent - * @since 2.0 - */ -public enum Operation -{ - /** - * Unknown operation. - */ - UNKNOWN(-99), - - /** - * Operation that moves to the beginning of the buffer. - */ - MOVE_TO_BEG(-1), - - /** - * Operation that moves to the end of the buffer. - */ - MOVE_TO_END(-3), - - /** - * Operation that moved to the previous character in the buffer. - */ - PREV_CHAR(-4), - - /** - * Operation that issues a newline. - */ - NEWLINE(-6), - - /** - * Operation that deletes the buffer from the current character to the end. - */ - KILL_LINE(-7), - - /** - * Operation that clears the screen. - */ - CLEAR_SCREEN(-8), - - /** - * Operation that sets the buffer to the next history item. - */ - NEXT_HISTORY(-9), - - /** - * Operation that sets the buffer to the previous history item. - */ - PREV_HISTORY(-11), - - /** - * Operation that redisplays the current buffer. - */ - REDISPLAY(-13), - - /** - * Operation that deletes the buffer from the cursor to the beginning. - */ - KILL_LINE_PREV(-15), - - /** - * Operation that deletes the previous word in the buffer. - */ - DELETE_PREV_WORD(-16), - - /** - * Operation that moves to the next character in the buffer. - */ - NEXT_CHAR(-19), - - /** - * Operation that moves to the previous character in the buffer. - */ - REPEAT_PREV_CHAR(-20), - - /** - * Operation that searches backwards in the command history. - */ - SEARCH_PREV(-21), - - /** - * Operation that repeats the character. - */ - REPEAT_NEXT_CHAR(-24), - - /** - * Operation that searches forward in the command history. - */ - SEARCH_NEXT(-25), - - /** - * Operation that moved to the previous whitespace. - */ - PREV_SPACE_WORD(-27), - - /** - * Operation that moved to the end of the current word. - */ - TO_END_WORD(-29), - - /** - * Operation that - */ - REPEAT_SEARCH_PREV(-34), - - /** - * Operation that - */ - PASTE_PREV(-36), - - /** - * Operation that - */ - REPLACE_MODE(-37), - - /** - * Operation that - */ - SUBSTITUTE_LINE(-38), - - /** - * Operation that - */ - TO_PREV_CHAR(-39), - - /** - * Operation that - */ - NEXT_SPACE_WORD(-40), - - /** - * Operation that - */ - DELETE_PREV_CHAR(-41), - - /** - * Operation that - */ - ADD(-42), - - /** - * Operation that - */ - PREV_WORD(-43), - - /** - * Operation that - */ - CHANGE_META(-44), - - /** - * Operation that - */ - DELETE_META(-45), - - /** - * Operation that - */ - END_WORD(-46), - - /** - * Operation that toggles insert/overtype - */ - INSERT(-48), - - /** - * Operation that - */ - REPEAT_SEARCH_NEXT(-49), - - /** - * Operation that - */ - PASTE_NEXT(-50), - - /** - * Operation that - */ - REPLACE_CHAR(-51), - - /** - * Operation that - */ - SUBSTITUTE_CHAR(-52), - - /** - * Operation that - */ - TO_NEXT_CHAR(-53), - - /** - * Operation that undoes the previous operation. - */ - UNDO(-54), - - /** - * Operation that moved to the next word. - */ - NEXT_WORD(-55), - - /** - * Operation that deletes the previous character. - */ - DELETE_NEXT_CHAR(-56), - - /** - * Operation that toggles between uppercase and lowercase. - */ - CHANGE_CASE(-57), - - /** - * Operation that performs completion operation on the current word. - */ - COMPLETE(-58), - - /** - * Operation that exits the command prompt. - */ - EXIT(-59), - - /** - * Operation that pastes the contents of the clipboard into the line - */ - PASTE(-60), - - /** - * Operation that moves the current History to the beginning. - */ - START_OF_HISTORY(-61), - - /** - * Operation that moves the current History to the end. - */ - END_OF_HISTORY(-62), - - /** - * Operation that clears whatever text is on the current line. - */ - CLEAR_LINE(-63), - - /** - * Cancel search - */ - ABORT(-64), - - /** - * Delete next word - */ - DELETE_NEXT_WORD(-65), - - ; - - public final short code; - - Operation(final int code) { - this.code = (short) code; - } - - private static final Map<Short, Operation> codes; - - static { - Map<Short, Operation> map = new HashMap<Short, Operation>(); - - for (Operation op : Operation.values()) { - map.put(op.code, op); - } - - codes = map; - } - - public static Operation valueOf(final int code) { - return codes.get((short) code); - } -}
\ No newline at end of file diff --git a/src/jline/src/main/java/scala/tools/jline/console/completer/AggregateCompleter.java b/src/jline/src/main/java/scala/tools/jline/console/completer/AggregateCompleter.java deleted file mode 100644 index 3170bd1c68..0000000000 --- a/src/jline/src/main/java/scala/tools/jline/console/completer/AggregateCompleter.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package scala.tools.jline.console.completer; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.LinkedList; -import java.util.List; - -/** - * Completer which contains multiple completers and aggregates them together. - * - * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> - * @since 2.3 - */ -public class AggregateCompleter - implements Completer -{ - private final List<Completer> completers = new ArrayList<Completer>(); - - public AggregateCompleter() { - // empty - } - - public AggregateCompleter(final Collection<Completer> completers) { - assert completers != null; - this.completers.addAll(completers); - } - - public AggregateCompleter(final Completer... completers) { - this(Arrays.asList(completers)); - } - - public Collection<Completer> getCompleters() { - return completers; - } - - public int complete(final String buffer, final int cursor, final List<CharSequence> candidates) { - // buffer could be null - assert candidates != null; - - List<Completion> completions = new ArrayList<Completion>(completers.size()); - - // Run each completer, saving its completion results - int max = -1; - for (Completer completer : completers) { - Completion completion = new Completion(candidates); - completion.complete(completer, buffer, cursor); - - // Compute the max cursor position - max = Math.max(max, completion.cursor); - - completions.add(completion); - } - - // Append candidates from completions which have the same cursor position as max - for (Completion completion : completions) { - if (completion.cursor == max) { - candidates.addAll(completion.candidates); - } - } - - return max; - } - - @Override - public String toString() { - return getClass().getSimpleName() + "{" + - "completers=" + completers + - '}'; - } - - private class Completion - { - public final List<CharSequence> candidates; - - public int cursor; - - public Completion(final List<CharSequence> candidates) { - assert candidates != null; - this.candidates = new LinkedList<CharSequence>(candidates); - } - - public void complete(final Completer completer, final String buffer, final int cursor) { - assert completer != null; - - this.cursor = completer.complete(buffer, cursor, candidates); - } - } -} diff --git a/src/jline/src/main/java/scala/tools/jline/console/completer/ArgumentCompleter.java b/src/jline/src/main/java/scala/tools/jline/console/completer/ArgumentCompleter.java deleted file mode 100644 index 6f60029a1d..0000000000 --- a/src/jline/src/main/java/scala/tools/jline/console/completer/ArgumentCompleter.java +++ /dev/null @@ -1,398 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ - -package scala.tools.jline.console.completer; - -import scala.tools.jline.internal.Log; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.LinkedList; -import java.util.List; - -/** - * A {@link Completer} implementation that invokes a child completer using the appropriate <i>separator</i> argument. - * This can be used instead of the individual completers having to know about argument parsing semantics. - * - * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> - * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> - * @since 2.3 - */ -public class ArgumentCompleter - implements Completer -{ - private final ArgumentDelimiter delimiter; - - private final List<Completer> completers = new ArrayList<Completer>(); - - private boolean strict = true; - - /** - * Create a new completer with the specified argument delimiter. - * - * @param delimiter The delimiter for parsing arguments - * @param completers The embedded completers - */ - public ArgumentCompleter(final ArgumentDelimiter delimiter, final Collection<Completer> completers) { - assert delimiter != null; - this.delimiter = delimiter; - assert completers != null; - this.completers.addAll(completers); - } - - /** - * Create a new completer with the specified argument delimiter. - * - * @param delimiter The delimiter for parsing arguments - * @param completers The embedded completers - */ - public ArgumentCompleter(final ArgumentDelimiter delimiter, final Completer... completers) { - this(delimiter, Arrays.asList(completers)); - } - - /** - * Create a new completer with the default {@link WhitespaceArgumentDelimiter}. - * - * @param completers The embedded completers - */ - public ArgumentCompleter(final Completer... completers) { - this(new WhitespaceArgumentDelimiter(), completers); - } - - /** - * Create a new completer with the default {@link WhitespaceArgumentDelimiter}. - * - * @param completers The embedded completers - */ - public ArgumentCompleter(final List<Completer> completers) { - this(new WhitespaceArgumentDelimiter(), completers); - } - - /** - * If true, a completion at argument index N will only succeed - * if all the completions from 0-(N-1) also succeed. - */ - public void setStrict(final boolean strict) { - this.strict = strict; - } - - /** - * Returns whether a completion at argument index N will success - * if all the completions from arguments 0-(N-1) also succeed. - * - * @return True if strict. - * @since 2.3 - */ - public boolean isStrict() { - return this.strict; - } - - /** - * @since 2.3 - */ - public ArgumentDelimiter getDelimiter() { - return delimiter; - } - - /** - * @since 2.3 - */ - public List<Completer> getCompleters() { - return completers; - } - - public int complete(final String buffer, final int cursor, final List<CharSequence> candidates) { - // buffer can be null - assert candidates != null; - - ArgumentDelimiter delim = getDelimiter(); - ArgumentList list = delim.delimit(buffer, cursor); - int argpos = list.getArgumentPosition(); - int argIndex = list.getCursorArgumentIndex(); - - if (argIndex < 0) { - return -1; - } - - List<Completer> completers = getCompleters(); - Completer completer; - - // if we are beyond the end of the completers, just use the last one - if (argIndex >= completers.size()) { - completer = completers.get(completers.size() - 1); - } - else { - completer = completers.get(argIndex); - } - - // ensure that all the previous completers are successful before allowing this completer to pass (only if strict). - for (int i = 0; isStrict() && (i < argIndex); i++) { - Completer sub = completers.get(i >= completers.size() ? (completers.size() - 1) : i); - String[] args = list.getArguments(); - String arg = (args == null || i >= args.length) ? "" : args[i]; - - List<CharSequence> subCandidates = new LinkedList<CharSequence>(); - - if (sub.complete(arg, arg.length(), subCandidates) == -1) { - return -1; - } - - if (subCandidates.size() == 0) { - return -1; - } - } - - int ret = completer.complete(list.getCursorArgument(), argpos, candidates); - - if (ret == -1) { - return -1; - } - - int pos = ret + list.getBufferPosition() - argpos; - - // Special case: when completing in the middle of a line, and the area under the cursor is a delimiter, - // then trim any delimiters from the candidates, since we do not need to have an extra delimiter. - // - // E.g., if we have a completion for "foo", and we enter "f bar" into the buffer, and move to after the "f" - // and hit TAB, we want "foo bar" instead of "foo bar". - - if ((cursor != buffer.length()) && delim.isDelimiter(buffer, cursor)) { - for (int i = 0; i < candidates.size(); i++) { - CharSequence val = candidates.get(i); - - while (val.length() > 0 && delim.isDelimiter(val, val.length() - 1)) { - val = val.subSequence(0, val.length() - 1); - } - - candidates.set(i, val); - } - } - - Log.trace("Completing ", buffer, " (pos=", cursor, ") with: ", candidates, ": offset=", pos); - - return pos; - } - - /** - * The {@link ArgumentCompleter.ArgumentDelimiter} allows custom breaking up of a {@link String} into individual - * arguments in order to dispatch the arguments to the nested {@link Completer}. - * - * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> - */ - public static interface ArgumentDelimiter - { - /** - * Break the specified buffer into individual tokens that can be completed on their own. - * - * @param buffer The buffer to split - * @param pos The current position of the cursor in the buffer - * @return The tokens - */ - ArgumentList delimit(CharSequence buffer, int pos); - - /** - * Returns true if the specified character is a whitespace parameter. - * - * @param buffer The complete command buffer - * @param pos The index of the character in the buffer - * @return True if the character should be a delimiter - */ - boolean isDelimiter(CharSequence buffer, int pos); - } - - /** - * Abstract implementation of a delimiter that uses the {@link #isDelimiter} method to determine if a particular - * character should be used as a delimiter. - * - * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> - */ - public abstract static class AbstractArgumentDelimiter - implements ArgumentDelimiter - { - // TODO: handle argument quoting and escape characters - - private char[] quoteChars = {'\'', '"'}; - - private char[] escapeChars = {'\\'}; - - public void setQuoteChars(final char[] chars) { - this.quoteChars = chars; - } - - public char[] getQuoteChars() { - return this.quoteChars; - } - - public void setEscapeChars(final char[] chars) { - this.escapeChars = chars; - } - - public char[] getEscapeChars() { - return this.escapeChars; - } - - public ArgumentList delimit(final CharSequence buffer, final int cursor) { - List<String> args = new LinkedList<String>(); - StringBuilder arg = new StringBuilder(); - int argpos = -1; - int bindex = -1; - - for (int i = 0; (buffer != null) && (i <= buffer.length()); i++) { - // once we reach the cursor, set the - // position of the selected index - if (i == cursor) { - bindex = args.size(); - // the position in the current argument is just the - // length of the current argument - argpos = arg.length(); - } - - if ((i == buffer.length()) || isDelimiter(buffer, i)) { - if (arg.length() > 0) { - args.add(arg.toString()); - arg.setLength(0); // reset the arg - } - } - else { - arg.append(buffer.charAt(i)); - } - } - - return new ArgumentList(args.toArray(new String[args.size()]), bindex, argpos, cursor); - } - - /** - * Returns true if the specified character is a whitespace parameter. Check to ensure that the character is not - * escaped by any of {@link #getQuoteChars}, and is not escaped by ant of the {@link #getEscapeChars}, and - * returns true from {@link #isDelimiterChar}. - * - * @param buffer The complete command buffer - * @param pos The index of the character in the buffer - * @return True if the character should be a delimiter - */ - public boolean isDelimiter(final CharSequence buffer, final int pos) { - return !isQuoted(buffer, pos) && !isEscaped(buffer, pos) && isDelimiterChar(buffer, pos); - } - - public boolean isQuoted(final CharSequence buffer, final int pos) { - return false; - } - - public boolean isEscaped(final CharSequence buffer, final int pos) { - if (pos <= 0) { - return false; - } - - for (int i = 0; (escapeChars != null) && (i < escapeChars.length); - i++) { - if (buffer.charAt(pos) == escapeChars[i]) { - return !isEscaped(buffer, pos - 1); // escape escape - } - } - - return false; - } - - /** - * Returns true if the character at the specified position if a delimiter. This method will only be called if - * the character is not enclosed in any of the {@link #getQuoteChars}, and is not escaped by ant of the - * {@link #getEscapeChars}. To perform escaping manually, override {@link #isDelimiter} instead. - */ - public abstract boolean isDelimiterChar(CharSequence buffer, int pos); - } - - /** - * {@link ArgumentCompleter.ArgumentDelimiter} implementation that counts all whitespace (as reported by - * {@link Character#isWhitespace}) as being a delimiter. - * - * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> - */ - public static class WhitespaceArgumentDelimiter - extends AbstractArgumentDelimiter - { - /** - * The character is a delimiter if it is whitespace, and the - * preceding character is not an escape character. - */ - @Override - public boolean isDelimiterChar(final CharSequence buffer, final int pos) { - return Character.isWhitespace(buffer.charAt(pos)); - } - } - - /** - * The result of a delimited buffer. - * - * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> - */ - public static class ArgumentList - { - private String[] arguments; - - private int cursorArgumentIndex; - - private int argumentPosition; - - private int bufferPosition; - - /** - * @param arguments The array of tokens - * @param cursorArgumentIndex The token index of the cursor - * @param argumentPosition The position of the cursor in the current token - * @param bufferPosition The position of the cursor in the whole buffer - */ - public ArgumentList(final String[] arguments, final int cursorArgumentIndex, final int argumentPosition, final int bufferPosition) { - assert arguments != null; - - this.arguments = arguments; - this.cursorArgumentIndex = cursorArgumentIndex; - this.argumentPosition = argumentPosition; - this.bufferPosition = bufferPosition; - } - - public void setCursorArgumentIndex(final int i) { - this.cursorArgumentIndex = i; - } - - public int getCursorArgumentIndex() { - return this.cursorArgumentIndex; - } - - public String getCursorArgument() { - if ((cursorArgumentIndex < 0) || (cursorArgumentIndex >= arguments.length)) { - return null; - } - - return arguments[cursorArgumentIndex]; - } - - public void setArgumentPosition(final int pos) { - this.argumentPosition = pos; - } - - public int getArgumentPosition() { - return this.argumentPosition; - } - - public void setArguments(final String[] arguments) { - this.arguments = arguments; - } - - public String[] getArguments() { - return this.arguments; - } - - public void setBufferPosition(final int pos) { - this.bufferPosition = pos; - } - - public int getBufferPosition() { - return this.bufferPosition; - } - } -} diff --git a/src/jline/src/main/java/scala/tools/jline/console/completer/CandidateListCompletionHandler.java b/src/jline/src/main/java/scala/tools/jline/console/completer/CandidateListCompletionHandler.java deleted file mode 100644 index fa5bfd2777..0000000000 --- a/src/jline/src/main/java/scala/tools/jline/console/completer/CandidateListCompletionHandler.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ - -package scala.tools.jline.console.completer; - -import scala.tools.jline.console.ConsoleReader; -import scala.tools.jline.console.CursorBuffer; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.ResourceBundle; -import java.util.Set; - -/** - * A {@link CompletionHandler} that deals with multiple distinct completions - * by outputting the complete list of possibilities to the console. This - * mimics the behavior of the - * <a href="http://www.gnu.org/directory/readline.html">readline</a> library. - * - * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> - * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> - * @since 2.3 - */ -public class CandidateListCompletionHandler - implements CompletionHandler -{ - // TODO: handle quotes and escaped quotes && enable automatic escaping of whitespace - - public boolean complete(final ConsoleReader reader, final List<CharSequence> candidates, final int pos) throws - IOException - { - CursorBuffer buf = reader.getCursorBuffer(); - - // if there is only one completion, then fill in the buffer - if (candidates.size() == 1) { - CharSequence value = candidates.get(0); - - // fail if the only candidate is the same as the current buffer - if (value.equals(buf.toString())) { - return false; - } - - setBuffer(reader, value, pos); - - return true; - } - else if (candidates.size() > 1) { - String value = getUnambiguousCompletions(candidates); - setBuffer(reader, value, pos); - } - - printCandidates(reader, candidates); - - // redraw the current console buffer - reader.drawLine(); - - return true; - } - - public static void setBuffer(final ConsoleReader reader, final CharSequence value, final int offset) throws - IOException - { - while ((reader.getCursorBuffer().cursor > offset) && reader.backspace()) { - // empty - } - - reader.putString(value); - reader.setCursorPosition(offset + value.length()); - } - - /** - * Print out the candidates. If the size of the candidates is greater than the - * {@link ConsoleReader#getAutoprintThreshold}, they prompt with a warning. - * - * @param candidates the list of candidates to print - */ - public static void printCandidates(final ConsoleReader reader, Collection<CharSequence> candidates) throws - IOException - { - Set<CharSequence> distinct = new HashSet<CharSequence>(candidates); - - if (distinct.size() > reader.getAutoprintThreshold()) { - //noinspection StringConcatenation - reader.print(Messages.DISPLAY_CANDIDATES.format(candidates.size())); - reader.flush(); - - int c; - - String noOpt = Messages.DISPLAY_CANDIDATES_NO.format(); - String yesOpt = Messages.DISPLAY_CANDIDATES_YES.format(); - char[] allowed = {yesOpt.charAt(0), noOpt.charAt(0)}; - - while ((c = reader.readCharacter(allowed)) != -1) { - String tmp = new String(new char[]{(char) c}); - - if (noOpt.startsWith(tmp)) { - reader.println(); - return; - } - else if (yesOpt.startsWith(tmp)) { - break; - } - else { - reader.beep(); - } - } - } - - // copy the values and make them distinct, without otherwise affecting the ordering. Only do it if the sizes differ. - if (distinct.size() != candidates.size()) { - Collection<CharSequence> copy = new ArrayList<CharSequence>(); - - for (CharSequence next : candidates) { - if (!copy.contains(next)) { - copy.add(next); - } - } - - candidates = copy; - } - - reader.println(); - reader.printColumns(candidates); - } - - /** - * Returns a root that matches all the {@link String} elements of the specified {@link List}, - * or null if there are no commonalities. For example, if the list contains - * <i>foobar</i>, <i>foobaz</i>, <i>foobuz</i>, the method will return <i>foob</i>. - */ - private String getUnambiguousCompletions(final List<CharSequence> candidates) { - if (candidates == null || candidates.isEmpty()) { - return null; - } - - // convert to an array for speed - String[] strings = candidates.toArray(new String[candidates.size()]); - - String first = strings[0]; - StringBuilder candidate = new StringBuilder(); - - for (int i = 0; i < first.length(); i++) { - if (startsWith(first.substring(0, i + 1), strings)) { - candidate.append(first.charAt(i)); - } - else { - break; - } - } - - return candidate.toString(); - } - - /** - * @return true is all the elements of <i>candidates</i> start with <i>starts</i> - */ - private boolean startsWith(final String starts, final String[] candidates) { - for (String candidate : candidates) { - if (!candidate.startsWith(starts)) { - return false; - } - } - - return true; - } - - private static enum Messages - { - DISPLAY_CANDIDATES, - DISPLAY_CANDIDATES_YES, - DISPLAY_CANDIDATES_NO,; - - private static final - ResourceBundle - bundle = - ResourceBundle.getBundle(CandidateListCompletionHandler.class.getName(), Locale.getDefault()); - - public String format(final Object... args) { - if (bundle == null) - return ""; - else - return String.format(bundle.getString(name()), args); - } - } -} diff --git a/src/jline/src/main/java/scala/tools/jline/console/completer/Completer.java b/src/jline/src/main/java/scala/tools/jline/console/completer/Completer.java deleted file mode 100644 index 52d33847f2..0000000000 --- a/src/jline/src/main/java/scala/tools/jline/console/completer/Completer.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ - -package scala.tools.jline.console.completer; - -import java.util.List; - -/** - * A completer is the mechanism by which tab-completion candidates will be resolved. - * - * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> - * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> - * @since 2.3 - */ -public interface Completer -{ - // - // FIXME: Check if we can use CharSequece for buffer? - // - - /** - * Populates <i>candidates</i> with a list of possible completions for the <i>buffer</i>. - * - * The <i>candidates</i> list will not be sorted before being displayed to the user: thus, the - * complete method should sort the {@link List} before returning. - * - * @param buffer The buffer - * @param cursor The current position of the cursor in the <i>buffer</i> - * @param candidates The {@link List} of candidates to populate - * @return The index of the <i>buffer</i> for which the completion will be relative - */ - int complete(String buffer, int cursor, List<CharSequence> candidates); -} diff --git a/src/jline/src/main/java/scala/tools/jline/console/completer/CompletionHandler.java b/src/jline/src/main/java/scala/tools/jline/console/completer/CompletionHandler.java deleted file mode 100644 index 030dc84205..0000000000 --- a/src/jline/src/main/java/scala/tools/jline/console/completer/CompletionHandler.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ - -package scala.tools.jline.console.completer; - -import scala.tools.jline.console.ConsoleReader; - -import java.io.IOException; -import java.util.List; - -/** - * Handler for dealing with candidates for tab-completion. - * - * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> - * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> - * @since 2.3 - */ -public interface CompletionHandler -{ - boolean complete(ConsoleReader reader, List<CharSequence> candidates, int position) throws IOException; -} diff --git a/src/jline/src/main/java/scala/tools/jline/console/completer/EnumCompleter.java b/src/jline/src/main/java/scala/tools/jline/console/completer/EnumCompleter.java deleted file mode 100644 index 5ad049b857..0000000000 --- a/src/jline/src/main/java/scala/tools/jline/console/completer/EnumCompleter.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2009 the original author(s). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package scala.tools.jline.console.completer; - -/** - * {@link Completer} for {@link Enum} names. - * - * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> - * @since 2.3 - */ -public class EnumCompleter - extends StringsCompleter -{ - public EnumCompleter(Class<? extends Enum> source) { - assert source != null; - - for (Enum<?> n : source.getEnumConstants()) { - this.getStrings().add(n.name().toLowerCase()); - } - } -}
\ No newline at end of file diff --git a/src/jline/src/main/java/scala/tools/jline/console/completer/FileNameCompleter.java b/src/jline/src/main/java/scala/tools/jline/console/completer/FileNameCompleter.java deleted file mode 100644 index 6556138769..0000000000 --- a/src/jline/src/main/java/scala/tools/jline/console/completer/FileNameCompleter.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ - -package scala.tools.jline.console.completer; - -import scala.tools.jline.internal.Configuration; - -import java.io.File; -import java.util.List; - -/** - * A file name completer takes the buffer and issues a list of - * potential completions. - * <p/> - * This completer tries to behave as similar as possible to - * <i>bash</i>'s file name completion (using GNU readline) - * with the following exceptions: - * <p/> - * <ul> - * <li>Candidates that are directories will end with "/"</li> - * <li>Wildcard regular expressions are not evaluated or replaced</li> - * <li>The "~" character can be used to represent the user's home, - * but it cannot complete to other users' homes, since java does - * not provide any way of determining that easily</li> - * </ul> - * - * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> - * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> - * @since 2.3 - */ -public class FileNameCompleter - implements Completer -{ - // TODO: Handle files with spaces in them - - private static final boolean OS_IS_WINDOWS; - - static { - String os = Configuration.getOsName(); - OS_IS_WINDOWS = os.contains("windows"); - } - - public int complete(String buffer, final int cursor, final List<CharSequence> candidates) { - // buffer can be null - assert candidates != null; - - if (buffer == null) { - buffer = ""; - } - - if (OS_IS_WINDOWS) { - buffer = buffer.replace('/', '\\'); - } - - String translated = buffer; - - File homeDir = getUserHome(); - - // Special character: ~ maps to the user's home directory - if (translated.startsWith("~" + separator())) { - translated = homeDir.getPath() + translated.substring(1); - } - else if (translated.startsWith("~")) { - translated = homeDir.getParentFile().getAbsolutePath(); - } - else if (!(translated.startsWith(separator()))) { - String cwd = getUserDir().getAbsolutePath(); - translated = cwd + separator() + translated; - } - - File file = new File(translated); - final File dir; - - if (translated.endsWith(separator())) { - dir = file; - } - else { - dir = file.getParentFile(); - } - - File[] entries = dir == null ? new File[0] : dir.listFiles(); - - return matchFiles(buffer, translated, entries, candidates); - } - - protected String separator() { - return File.separator; - } - - protected File getUserHome() { - return Configuration.getUserHome(); - } - - protected File getUserDir() { - return new File("."); - } - - protected int matchFiles(final String buffer, final String translated, final File[] files, final List<CharSequence> candidates) { - if (files == null) { - return -1; - } - - int matches = 0; - - // first pass: just count the matches - for (File file : files) { - if (file.getAbsolutePath().startsWith(translated)) { - matches++; - } - } - for (File file : files) { - if (file.getAbsolutePath().startsWith(translated)) { - CharSequence name = file.getName() + (matches == 1 && file.isDirectory() ? separator() : " "); - candidates.add(render(file, name).toString()); - } - } - - final int index = buffer.lastIndexOf(separator()); - - return index + separator().length(); - } - - protected CharSequence render(final File file, final CharSequence name) { - assert file != null; - assert name != null; - - return name; - } -} diff --git a/src/jline/src/main/java/scala/tools/jline/console/completer/NullCompleter.java b/src/jline/src/main/java/scala/tools/jline/console/completer/NullCompleter.java deleted file mode 100644 index 93cf563bcd..0000000000 --- a/src/jline/src/main/java/scala/tools/jline/console/completer/NullCompleter.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package scala.tools.jline.console.completer; - -import java.util.List; - -/** - * Null completer. - * - * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> - * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> - * @since 2.3 - */ -public final class NullCompleter - implements Completer -{ - public static final NullCompleter INSTANCE = new NullCompleter(); - - public int complete(final String buffer, final int cursor, final List<CharSequence> candidates) { - return -1; - } -}
\ No newline at end of file diff --git a/src/jline/src/main/java/scala/tools/jline/console/completer/StringsCompleter.java b/src/jline/src/main/java/scala/tools/jline/console/completer/StringsCompleter.java deleted file mode 100644 index 2abfdd0340..0000000000 --- a/src/jline/src/main/java/scala/tools/jline/console/completer/StringsCompleter.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package scala.tools.jline.console.completer; - -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.SortedSet; -import java.util.TreeSet; - -/** - * Completer for a set of strings. - * - * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> - * @since 2.3 - */ -public class StringsCompleter - implements Completer -{ - private final SortedSet<String> strings = new TreeSet<String>(); - - public StringsCompleter() { - // empty - } - - public StringsCompleter(final Collection<String> strings) { - assert strings != null; - getStrings().addAll(strings); - } - - public StringsCompleter(final String... strings) { - this(Arrays.asList(strings)); - } - - public Collection<String> getStrings() { - return strings; - } - - public int complete(final String buffer, final int cursor, final List<CharSequence> candidates) { - // buffer could be null - assert candidates != null; - - if (buffer == null) { - candidates.addAll(strings); - } - else { - for (String match : strings.tailSet(buffer)) { - if (!match.startsWith(buffer)) { - break; - } - - candidates.add(match); - } - } - - if (candidates.size() == 1) { - candidates.set(0, candidates.get(0) + " "); - } - - return candidates.isEmpty() ? -1 : 0; - } -}
\ No newline at end of file diff --git a/src/jline/src/main/java/scala/tools/jline/console/completer/package-info.java b/src/jline/src/main/java/scala/tools/jline/console/completer/package-info.java deleted file mode 100644 index 8150710cfc..0000000000 --- a/src/jline/src/main/java/scala/tools/jline/console/completer/package-info.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2010 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Console completer support. - * - * @since 2.3 - */ -package scala.tools.jline.console.completer;
\ No newline at end of file diff --git a/src/jline/src/main/java/scala/tools/jline/console/history/FileHistory.java b/src/jline/src/main/java/scala/tools/jline/console/history/FileHistory.java deleted file mode 100644 index 5eccba3ce5..0000000000 --- a/src/jline/src/main/java/scala/tools/jline/console/history/FileHistory.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ - -package scala.tools.jline.console.history; - -import scala.tools.jline.internal.Log; - -import java.io.BufferedOutputStream; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileOutputStream; -import java.io.FileReader; -import java.io.Flushable; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.PrintStream; -import java.io.Reader; -import java.util.ListIterator; - -/** - * {@link History} using a file for persistent backing. - * <p/> - * Implementers should install shutdown hook to call {@link FileHistory#flush} - * to save history to disk. - * - * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> - * @since 2.0 - */ -public class FileHistory - extends MemoryHistory - implements PersistentHistory, Flushable -{ - private final File file; - - public FileHistory(final File file) throws IOException { - assert file != null; - this.file = file; - load(file); - } - - public File getFile() { - return file; - } - - public void load(final File file) throws IOException { - assert file != null; - if (file.exists()) { - Log.trace("Loading history from: ", file); - load(new FileReader(file)); - } - } - - public void load(final InputStream input) throws IOException { - assert input != null; - load(new InputStreamReader(input)); - } - - public void load(final Reader reader) throws IOException { - assert reader != null; - BufferedReader input = new BufferedReader(reader); - - String item; - while ((item = input.readLine()) != null) { - add(item); - } - } - - public void flush() throws IOException { - Log.trace("Flushing history"); - - if (!file.exists()) { - File dir = file.getParentFile(); - if (!dir.exists() && !dir.mkdirs()) { - Log.warn("Failed to create directory: ", dir); - } - if (!file.createNewFile()) { - Log.warn("Failed to create file: ", file); - } - } - - PrintStream out = new PrintStream(new BufferedOutputStream(new FileOutputStream(file))); - try { - for (Entry entry : this) { - out.println(entry.value()); - } - } - finally { - out.close(); - } - } - - public void purge() throws IOException { - Log.trace("Purging history"); - - clear(); - - if (!file.delete()) { - Log.warn("Failed to delete history file: ", file); - } - } -}
\ No newline at end of file diff --git a/src/jline/src/main/java/scala/tools/jline/console/history/History.java b/src/jline/src/main/java/scala/tools/jline/console/history/History.java deleted file mode 100644 index d8602f2150..0000000000 --- a/src/jline/src/main/java/scala/tools/jline/console/history/History.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ - -package scala.tools.jline.console.history; - -import java.util.Iterator; -import java.util.ListIterator; - -/** - * Console history. - * - * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> - * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> - * @since 2.3 - */ -public interface History - extends Iterable<History.Entry> -{ - int size(); - - boolean isEmpty(); - - int index(); - - void clear(); - - CharSequence get(int index); - - void add(CharSequence line); - - void replace(CharSequence item); - - // - // Entries - // - - interface Entry - { - int index(); - - CharSequence value(); - } - - ListIterator<Entry> entries(int index); - - ListIterator<Entry> entries(); - - Iterator<Entry> iterator(); - - // - // Navigation - // - - CharSequence current(); - - boolean previous(); - - boolean next(); - - boolean moveToFirst(); - - boolean moveToLast(); - - boolean moveTo(int index); - - void moveToEnd(); -} diff --git a/src/jline/src/main/java/scala/tools/jline/console/history/MemoryHistory.java b/src/jline/src/main/java/scala/tools/jline/console/history/MemoryHistory.java deleted file mode 100644 index 3af936428a..0000000000 --- a/src/jline/src/main/java/scala/tools/jline/console/history/MemoryHistory.java +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ - -package scala.tools.jline.console.history; - -import java.util.Iterator; -import java.util.LinkedList; -import java.util.ListIterator; -import java.util.NoSuchElementException; - -/** - * Non-persistent {@link History}. - * - * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> - * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> - * @since 2.3 - */ -public class MemoryHistory - implements History -{ - public static final int DEFAULT_MAX_SIZE = 500; - - private final LinkedList<CharSequence> items = new LinkedList<CharSequence>(); - - private int maxSize = DEFAULT_MAX_SIZE; - - private boolean ignoreDuplicates = true; - - private boolean autoTrim = false; - - // NOTE: These are all ideas from looking at the Bash man page: - - // TODO: Add ignore space? (lines starting with a space are ignored) - - // TODO: Add ignore patterns? - - // TODO: Add history timestamp? - - // TODO: Add erase dups? - - private int offset = 0; - - private int index = 0; - - public void setMaxSize(final int maxSize) { - this.maxSize = maxSize; - maybeResize(); - } - - public int getMaxSize() { - return maxSize; - } - - public boolean isIgnoreDuplicates() { - return ignoreDuplicates; - } - - public void setIgnoreDuplicates(final boolean flag) { - this.ignoreDuplicates = flag; - } - - public boolean isAutoTrim() { - return autoTrim; - } - - public void setAutoTrim(final boolean flag) { - this.autoTrim = flag; - } - - public int size() { - return items.size(); - } - - public boolean isEmpty() { - return items.isEmpty(); - } - - public int index() { - return offset + index; - } - - public void clear() { - items.clear(); - offset = 0; - index = 0; - } - - public CharSequence get(final int index) { - return items.get(index - offset); - } - - public void add(CharSequence item) { - assert item != null; - - if (isAutoTrim()) { - item = String.valueOf(item).trim(); - } - - if (isIgnoreDuplicates()) { - if (!items.isEmpty() && item.equals(items.getLast())) { - return; - } - } - - items.add(item); - - maybeResize(); - } - - public void replace(final CharSequence item) { - items.removeLast(); - add(item); - } - - private void maybeResize() { - while (size() > getMaxSize()) { - items.removeFirst(); - offset++; - } - - index = size(); - } - - public ListIterator<Entry> entries(final int index) { - return new EntriesIterator(index - offset); - } - - public ListIterator<Entry> entries() { - return entries(offset); - } - - public Iterator<Entry> iterator() { - return entries(); - } - - private static class EntryImpl - implements Entry - { - private final int index; - - private final CharSequence value; - - public EntryImpl(int index, CharSequence value) { - this.index = index; - this.value = value; - } - - public int index() { - return index; - } - - public CharSequence value() { - return value; - } - - @Override - public String toString() { - return String.format("%d: %s", index, value); - } - } - - private class EntriesIterator - implements ListIterator<Entry> - { - private final ListIterator<CharSequence> source; - - private EntriesIterator(final int index) { - source = items.listIterator(index); - } - - public Entry next() { - if (!source.hasNext()) { - throw new NoSuchElementException(); - } - return new EntryImpl(offset + source.nextIndex(), source.next()); - } - - public Entry previous() { - if (!source.hasPrevious()) { - throw new NoSuchElementException(); - } - return new EntryImpl(offset + source.previousIndex(), source.previous()); - } - - public int nextIndex() { - return offset + source.nextIndex(); - } - - public int previousIndex() { - return offset + source.previousIndex(); - } - - public boolean hasNext() { - return source.hasNext(); - } - - public boolean hasPrevious() { - return source.hasPrevious(); - } - - public void remove() { - throw new UnsupportedOperationException(); - } - - public void set(final Entry entry) { - throw new UnsupportedOperationException(); - } - - public void add(final Entry entry) { - throw new UnsupportedOperationException(); - } - } - - // - // Navigation - // - - /** - * This moves the history to the last entry. This entry is one position - * before the moveToEnd() position. - * - * @return Returns false if there were no history entries or the history - * index was already at the last entry. - */ - public boolean moveToLast() { - int lastEntry = size() - 1; - if (lastEntry >= 0 && lastEntry != index) { - index = size() - 1; - return true; - } - - return false; - } - - /** - * Move to the specified index in the history - * @param index - * @return - */ - public boolean moveTo(int index) { - index -= offset; - if (index >= 0 && index < size() ) { - this.index = index; - return true; - } - return false; - } - - /** - * Moves the history index to the first entry. - * - * @return Return false if there are no entries in the history or if the - * history is already at the beginning. - */ - public boolean moveToFirst() { - if (size() > 0 && index != 0) { - index = 0; - return true; - } - - return false; - } - - /** - * Move to the end of the history buffer. This will be a blank entry, after - * all of the other entries. - */ - public void moveToEnd() { - index = size(); - } - - /** - * Return the content of the current buffer. - */ - public CharSequence current() { - if (index >= size()) { - return ""; - } - - return items.get(index); - } - - /** - * Move the pointer to the previous element in the buffer. - * - * @return true if we successfully went to the previous element - */ - public boolean previous() { - if (index <= 0) { - return false; - } - - index--; - - return true; - } - - /** - * Move the pointer to the next element in the buffer. - * - * @return true if we successfully went to the next element - */ - public boolean next() { - if (index >= size()) { - return false; - } - - index++; - - return true; - } - - -}
\ No newline at end of file diff --git a/src/jline/src/main/java/scala/tools/jline/console/history/PersistentHistory.java b/src/jline/src/main/java/scala/tools/jline/console/history/PersistentHistory.java deleted file mode 100644 index 916532e7fc..0000000000 --- a/src/jline/src/main/java/scala/tools/jline/console/history/PersistentHistory.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ - -package scala.tools.jline.console.history; - -import java.io.IOException; - -/** - * Persistent {@link History}. - * - * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> - * @since 2.3 - */ -public interface PersistentHistory - extends History -{ - /** - * Flush all items to persistent storage. - * - * @throws IOException Flush failed - */ - void flush() throws IOException; - - /** - * Purge persistent storage and {@link #clear}. - * - * @throws IOException Purge failed - */ - void purge() throws IOException; -}
\ No newline at end of file diff --git a/src/jline/src/main/java/scala/tools/jline/console/history/package-info.java b/src/jline/src/main/java/scala/tools/jline/console/history/package-info.java deleted file mode 100644 index 4635752898..0000000000 --- a/src/jline/src/main/java/scala/tools/jline/console/history/package-info.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2009 the original author(s). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Console history support. - * - * @since 2.0 - */ -package scala.tools.jline.console.history;
\ No newline at end of file diff --git a/src/jline/src/main/java/scala/tools/jline/console/package-info.java b/src/jline/src/main/java/scala/tools/jline/console/package-info.java deleted file mode 100644 index 9f284e9c05..0000000000 --- a/src/jline/src/main/java/scala/tools/jline/console/package-info.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2009 the original author(s). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Console support. - * - * @since 2.0 - */ -package scala.tools.jline.console;
\ No newline at end of file diff --git a/src/jline/src/main/java/scala/tools/jline/internal/Configuration.java b/src/jline/src/main/java/scala/tools/jline/internal/Configuration.java deleted file mode 100644 index 5350d6c19e..0000000000 --- a/src/jline/src/main/java/scala/tools/jline/internal/Configuration.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package scala.tools.jline.internal; - -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.Properties; - -/** - * Provides access to configuration values. - * - * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> - * @since 2.4 - */ -public final class Configuration -{ - public static final String JLINE_RC = ".jline.rc"; - - private static final Properties userprops; - - static { - Properties props = new Properties(); - - File file = new File(getUserHome(), JLINE_RC); - if (file.exists() && file.canRead()) { - try { - InputStream input = new BufferedInputStream(new FileInputStream(file)); - try { - props.load(input); - Log.debug("Loaded user configuration: ", file); - } - finally { - input.close(); - } - } - catch (IOException e) { - Log.warn("Unable to read user configuration: ", file, e); - } - } - else { - Log.trace("User configuration file missing or unreadable: ", file); - } - - userprops = props; - } - - private static boolean isEmpty(final String value) { - return value == null || value.trim().length() == 0; - } - - public static String getString(final String name, final String defaultValue) { - assert name != null; - - String value; - - // Check sysprops first, it always wins - value = System.getProperty(name); - - if (isEmpty(value)) { - // Next try userprops - value = userprops.getProperty(name); - - if (isEmpty(value)) { - // else use the default - value = defaultValue; - } - } - - return value; - } - - public static String getString(final String name) { - return getString(name, null); - } - - public static Boolean getBoolean(final String name, final Boolean defaultValue) { - String value = getString(name); - if (isEmpty(value)) { - return defaultValue; - } - return Boolean.valueOf(value); - } - - public static Boolean getBoolean(final String name) { - return getBoolean(name, null); - } - - // - // System property helpers - // - - public static File getUserHome() { - return new File(System.getProperty("user.home")); - } - - public static String getOsName() { - return System.getProperty("os.name").toLowerCase(); - } - - public static String getFileEncoding() { - return System.getProperty("file.encoding"); - } - - public static String getInputEncoding() { - return System.getProperty("input.encoding", "UTF-8"); - } -}
\ No newline at end of file diff --git a/src/jline/src/main/java/scala/tools/jline/internal/Log.java b/src/jline/src/main/java/scala/tools/jline/internal/Log.java deleted file mode 100644 index b226a10532..0000000000 --- a/src/jline/src/main/java/scala/tools/jline/internal/Log.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package scala.tools.jline.internal; - -import java.io.PrintStream; - -/** - * Internal logger. - * - * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> - * @since 2.0 - */ -public final class Log -{ - ///CLOVER:OFF - - public static enum Level - { - TRACE, - DEBUG, - INFO, - WARN, - ERROR - } - - @SuppressWarnings({"StringConcatenation"}) - public static final boolean DEBUG = Boolean.getBoolean(Log.class.getName() + ".debug"); - - @SuppressWarnings({"StringConcatenation"}) - public static final boolean TRACE = Boolean.getBoolean(Log.class.getName() + ".trace"); - - private static PrintStream output = System.err; - - public static PrintStream getOutput() { - return output; - } - - public static void setOutput(final PrintStream out) { - assert out != null; - output = out; - } - - private static void print(final Object message) { - if (message instanceof Throwable) { - ((Throwable) message).printStackTrace(); - } - else if (message.getClass().isArray()) { - Object[] array = (Object[]) message; - - for (int i = 0; i < array.length; i++) { - output.print(array[i]); - if (i + 1 < array.length) { - output.print(","); - } - } - } - else { - output.print(message); - } - } - - private static void log(final Level level, final Object[] messages) { - //noinspection SynchronizeOnNonFinalField - synchronized (output) { - output.format("[%s] ", level); - - for (Object message : messages) { - print(message); - } - - output.println(); - output.flush(); - } - } - - public static void trace(final Object... messages) { - if (TRACE) { - log(Level.TRACE, messages); - } - } - - public static void debug(final Object... messages) { - if (TRACE || DEBUG) { - log(Level.DEBUG, messages); - } - } - - public static void warn(final Object... messages) { - log(Level.WARN, messages); - } - - public static void error(final Object... messages) { - log(Level.ERROR, messages); - } -}
\ No newline at end of file diff --git a/src/jline/src/main/java/scala/tools/jline/internal/ReplayPrefixOneCharInputStream.java b/src/jline/src/main/java/scala/tools/jline/internal/ReplayPrefixOneCharInputStream.java deleted file mode 100644 index 2adabdd2ab..0000000000 --- a/src/jline/src/main/java/scala/tools/jline/internal/ReplayPrefixOneCharInputStream.java +++ /dev/null @@ -1,95 +0,0 @@ -package scala.tools.jline.internal; - -import java.io.IOException; -import java.io.InputStream; -import java.text.MessageFormat; - -/** - * This is awkward and inefficient, but probably the minimal way to add UTF-8 support to JLine - * - * @author <a href="mailto:Marc.Herbert@continuent.com">Marc Herbert</a> - * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> - * @since 2.0 - */ -public final class ReplayPrefixOneCharInputStream - extends InputStream -{ - private byte firstByte; - - private int byteLength; - - private InputStream wrappedStream; - - private int byteRead; - - private final String encoding; - - public ReplayPrefixOneCharInputStream(final String encoding) { - assert encoding != null; - this.encoding = encoding; - } - - public String getEncoding() { - return encoding; - } - - public void setInput(final int recorded, final InputStream wrapped) throws IOException { - this.byteRead = 0; - this.firstByte = (byte) recorded; - this.wrappedStream = wrapped; - - byteLength = 1; - if (encoding.equalsIgnoreCase("UTF-8")) { - setInputUTF8(recorded, wrapped); - } - else if (encoding.equalsIgnoreCase("UTF-16")) { - byteLength = 2; - } - else if (encoding.equalsIgnoreCase("UTF-32")) { - byteLength = 4; - } - } - - - public void setInputUTF8(final int recorded, final InputStream wrapped) throws IOException { - // 110yyyyy 10zzzzzz - if ((firstByte & (byte) 0xE0) == (byte) 0xC0) { - this.byteLength = 2; - } - // 1110xxxx 10yyyyyy 10zzzzzz - else if ((firstByte & (byte) 0xF0) == (byte) 0xE0) { - this.byteLength = 3; - } - // 11110www 10xxxxxx 10yyyyyy 10zzzzzz - else if ((firstByte & (byte) 0xF8) == (byte) 0xF0) { - this.byteLength = 4; - } - else { - throw new IOException(MessageFormat.format("Invalid UTF-8 first byte: {0}", firstByte)); - } - } - - public int read() throws IOException { - if (available() == 0) { - return -1; - } - - byteRead++; - - if (byteRead == 1) { - return firstByte; - } - - return wrappedStream.read(); - } - - /** - * InputStreamReader is greedy and will try to read bytes in advance. We - * do NOT want this to happen since we use a temporary/"losing bytes" - * InputStreamReader above, that's why we hide the real - * wrappedStream.available() here. - */ - public int available() { - return byteLength - byteRead; - } -}
\ No newline at end of file diff --git a/src/jline/src/main/java/scala/tools/jline/internal/TerminalLineSettings.java b/src/jline/src/main/java/scala/tools/jline/internal/TerminalLineSettings.java deleted file mode 100644 index 151862c14d..0000000000 --- a/src/jline/src/main/java/scala/tools/jline/internal/TerminalLineSettings.java +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ - -package scala.tools.jline.internal; - -import java.io.ByteArrayOutputStream; -import java.io.Closeable; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.text.MessageFormat; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Provides access to terminal line settings via <tt>stty</tt>. - * - * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> - * @author <a href="mailto:dwkemp@gmail.com">Dale Kemp</a> - * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> - * @author <a href="mailto:jbonofre@apache.org">Jean-Baptiste Onofré</a> - * @since 2.0 - */ -public final class TerminalLineSettings -{ - public static final String JLINE_STTY = "jline.stty"; - - public static final String DEFAULT_STTY = "stty"; - - public static final String JLINE_SH = "jline.sh"; - - public static final String DEFAULT_SH = "sh"; - - private static String sttyCommand = Configuration.getString(JLINE_STTY, DEFAULT_STTY); - - private static String shCommand = Configuration.getString(JLINE_SH, DEFAULT_SH); - - private String config; - - private long configLastFetched; - - public TerminalLineSettings() throws IOException, InterruptedException { - config = get("-a"); - configLastFetched = System.currentTimeMillis(); - - Log.debug("Config: ", config); - - // sanity check - if (config.length() == 0) { - throw new IOException(MessageFormat.format("Unrecognized stty code: {0}", config)); - } - } - - public String getConfig() { - return config; - } - - public void restore() throws IOException, InterruptedException { - set("sane"); - } - - public String get(final String args) throws IOException, InterruptedException { - return stty(args); - } - - public void set(final String args) throws IOException, InterruptedException { - stty(args); - } - - /** - * <p> - * Get the value of a stty property, including the management of a cache. - * </p> - * - * @param name the stty property. - * @return the stty property value. - */ - public int getProperty(String name) { - assert name != null; - try { - // tty properties are cached so we don't have to worry too much about getting term widht/height - if (config == null || System.currentTimeMillis() - configLastFetched > 1000 ) { - config = get("-a"); - configLastFetched = System.currentTimeMillis(); - } - return this.getProperty(name, config); - } catch (Exception e) { - Log.warn("Failed to query stty ", name, e); - return -1; - } - } - - /** - * <p> - * Parses a stty output (provided by stty -a) and return the value of a given property. - * </p> - * - * @param name property name. - * @param stty string resulting of stty -a execution. - * @return value of the given property. - */ - protected int getProperty(String name, String stty) { - // try the first kind of regex - Pattern pattern = Pattern.compile(name + "\\s+=\\s+([^;]*)[;\\n\\r]"); - Matcher matcher = pattern.matcher(stty); - if (!matcher.find()) { - // try a second kind of regex - pattern = Pattern.compile(name + "\\s+([^;]*)[;\\n\\r]"); - matcher = pattern.matcher(stty); - if (!matcher.find()) { - // try a second try of regex - pattern = Pattern.compile("(\\S*)\\s+" + name); - matcher = pattern.matcher(stty); - if (!matcher.find()) { - return -1; - } - } - } - return parseControlChar(matcher.group(1)); - } - - private int parseControlChar(String str) { - // under - if ("<undef>".equals(str)) { - return -1; - } - // octal - if (str.charAt(0) == '0') { - return Integer.parseInt(str, 8); - } - // decimal - if (str.charAt(0) >= '1' && str.charAt(0) <= '9') { - return Integer.parseInt(str, 10); - } - // control char - if (str.charAt(0) == '^') { - if (str.charAt(1) == '?') { - return 127; - } else { - return str.charAt(1) - 64; - } - } else if (str.charAt(0) == 'M' && str.charAt(1) == '-') { - if (str.charAt(2) == '^') { - if (str.charAt(3) == '?') { - return 127 + 128; - } else { - return str.charAt(3) - 64 + 128; - } - } else { - return str.charAt(2) + 128; - } - } else { - return str.charAt(0); - } - } - - private static String stty(final String args) throws IOException, InterruptedException { - assert args != null; - return exec(String.format("%s %s < /dev/tty", sttyCommand, args)); - } - - private static String exec(final String cmd) throws IOException, InterruptedException { - assert cmd != null; - return exec(shCommand, "-c", cmd); - } - - private static String exec(final String... cmd) throws IOException, InterruptedException { - assert cmd != null; - - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - - Log.trace("Running: ", cmd); - - Process p = Runtime.getRuntime().exec(cmd); - - InputStream in = null; - InputStream err = null; - OutputStream out = null; - try { - int c; - in = p.getInputStream(); - while ((c = in.read()) != -1) { - bout.write(c); - } - err = p.getErrorStream(); - while ((c = err.read()) != -1) { - bout.write(c); - } - out = p.getOutputStream(); - p.waitFor(); - } - finally { - close(in, out, err); - } - - String result = bout.toString(); - - Log.trace("Result: ", result); - - return result; - } - - private static void close(final Closeable... closeables) { - for (Closeable c : closeables) { - try { - c.close(); - } - catch (Exception e) { - // Ignore - } - } - } -}
\ No newline at end of file diff --git a/src/jline/src/main/java/scala/tools/jline/internal/package-info.java b/src/jline/src/main/java/scala/tools/jline/internal/package-info.java deleted file mode 100644 index d27444cfdf..0000000000 --- a/src/jline/src/main/java/scala/tools/jline/internal/package-info.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2009 the original author(s). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Internal support. - * - * @since 2.0 - */ -package scala.tools.jline.internal;
\ No newline at end of file diff --git a/src/jline/src/main/java/scala/tools/jline/package-info.java b/src/jline/src/main/java/scala/tools/jline/package-info.java deleted file mode 100644 index fde16f98de..0000000000 --- a/src/jline/src/main/java/scala/tools/jline/package-info.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2009 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * JLine 2. - * - * @since 2.0 - */ -package scala.tools.jline;
\ No newline at end of file diff --git a/src/jline/src/main/resources/scala/tools/jline/console/completer/CandidateListCompletionHandler.properties b/src/jline/src/main/resources/scala/tools/jline/console/completer/CandidateListCompletionHandler.properties deleted file mode 100644 index fd097efb8a..0000000000 --- a/src/jline/src/main/resources/scala/tools/jline/console/completer/CandidateListCompletionHandler.properties +++ /dev/null @@ -1,4 +0,0 @@ -DISPLAY_CANDIDATES=Display all %d possibilities? (y or n) -DISPLAY_CANDIDATES_YES=y -DISPLAY_CANDIDATES_NO=n -DISPLAY_MORE=--More-- diff --git a/src/jline/src/main/resources/scala/tools/jline/keybindings.properties b/src/jline/src/main/resources/scala/tools/jline/keybindings.properties deleted file mode 100644 index ad932d2a80..0000000000 --- a/src/jline/src/main/resources/scala/tools/jline/keybindings.properties +++ /dev/null @@ -1,71 +0,0 @@ -# Keybinding mapping for JLine. The format is: -# [key code]=[logical operation] - -# CTRL-A: move to the beginning of the line -1=MOVE_TO_BEG - -# CTRL-B: move to the previous character -2=PREV_CHAR - -# CTRL-D: close out the input stream -4=EXIT - -# CTRL-E: move the cursor to the end of the line -5=MOVE_TO_END - -# CTRL-F: move to the next character -6=NEXT_CHAR - -# CTRL-G: abort -7=ABORT - -# BACKSPACE, CTRL-H: delete the previous character -# 8 is the ASCII code for backspace and therefor -# deleting the previous character -8=DELETE_PREV_CHAR - -# TAB, CTRL-I: signal that console completion should be attempted -9=COMPLETE - -# CTRL-J, CTRL-M: newline -10=NEWLINE - -# CTRL-K: erase the current line -11=KILL_LINE - -# CTRL-L: clear screen -12=CLEAR_SCREEN - -# ENTER: newline -13=NEWLINE - -# CTRL-N: scroll to the next element in the history buffer -14=NEXT_HISTORY - -# CTRL-O: move to the previous word -15=PREV_WORD - -# CTRL-P: scroll to the previous element in the history buffer -16=PREV_HISTORY - -# CTRL-R: search history -18=SEARCH_PREV - -# CTRL-T: move to next word -20=NEXT_WORD - -# CTRL-U: delete all the characters before the cursor position -21=KILL_LINE_PREV - -# CTRL-V: paste the contents of the clipboard (useful for Windows terminal) -22=PASTE - -# CTRL-W: delete the word directly before the cursor -23=DELETE_PREV_WORD - -# CTRL-X: delete the word directly after the cursor -24=DELETE_NEXT_WORD - -# DELETE, CTRL-?: delete the next character -# 127 is the ASCII code for delete -127=DELETE_NEXT_CHAR diff --git a/src/jline/src/main/resources/scala/tools/jline/windowsbindings.properties b/src/jline/src/main/resources/scala/tools/jline/windowsbindings.properties deleted file mode 100644 index 340b5aa5b9..0000000000 --- a/src/jline/src/main/resources/scala/tools/jline/windowsbindings.properties +++ /dev/null @@ -1,71 +0,0 @@ -# Keybinding mapping for JLine. The format is: -# [key code]=[logical operation] - -# CTRL-A: move to the beginning of the line -1=MOVE_TO_BEG - -# CTRL-B: move to the previous character -2=PREV_CHAR - -# CTRL-C: toggle overtype mode (frankly, I wasn't sure where to bind this) -3=INSERT - -# CTRL-D: close out the input stream -4=EXIT - -# CTRL-E: move the cursor to the end of the line -5=MOVE_TO_END - -# CTRL-F: move to the next character -6=NEXT_CHAR - -# CTRL-G: move to the previous word -7=ABORT - -# CTRL-H: delete the previous character -8=DELETE_PREV_CHAR - -# TAB, CTRL-I: signal that console completion should be attempted -9=COMPLETE - -# CTRL-J, CTRL-M: newline -10=NEWLINE - -# CTRL-K: erase the current line -11=KILL_LINE - -# CTRL-L: clear screen -12=CLEAR_SCREEN - -# ENTER: newline -13=NEWLINE - -# CTRL-N: scroll to the next element in the history buffer -14=NEXT_HISTORY - -# CTRL-O: move to the previous word -15=PREV_WORD - -# CTRL-P: scroll to the previous element in the history buffer -16=PREV_HISTORY - -# CTRL-R: search backwards in history -18=SEARCH_PREV - -# CTRL-S: Move to the end of the history -19=END_OF_HISTORY - -# CTRL-U: delete all the characters before the cursor position -21=KILL_LINE_PREV - -# CTRL-V: paste the contents of the clipboard (useful for Windows terminal) -22=PASTE - -# CTRL-W: delete the word directly before the cursor -23=DELETE_PREV_WORD - -# CTRL-[: escape - clear the current line. -27=CLEAR_LINE - -# CTRL-?: delete the previous character -127=DELETE_NEXT_CHAR diff --git a/src/jline/src/test/java/scala/tools/jline/TerminalFactoryTest.java b/src/jline/src/test/java/scala/tools/jline/TerminalFactoryTest.java deleted file mode 100644 index c0c070bdfd..0000000000 --- a/src/jline/src/test/java/scala/tools/jline/TerminalFactoryTest.java +++ /dev/null @@ -1,34 +0,0 @@ -package scala.tools.jline; - -import org.junit.Before; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -/** - * Tests for the {@link TerminalFactory}. - */ -public class TerminalFactoryTest -{ - @Before - public void setUp() throws Exception { - TerminalFactory.reset(); - } - - @Test - public void testConfigureNone() { - TerminalFactory.configure(TerminalFactory.NONE); - Terminal t = TerminalFactory.get(); - assertNotNull(t); - assertEquals(UnsupportedTerminal.class.getName(), t.getClass().getName()); - } - - @Test - public void testConfigureUnsupportedTerminal() { - TerminalFactory.configure(UnsupportedTerminal.class.getName()); - Terminal t = TerminalFactory.get(); - assertNotNull(t); - assertEquals(UnsupportedTerminal.class.getName(), t.getClass().getName()); - } -}
\ No newline at end of file diff --git a/src/jline/src/test/java/scala/tools/jline/console/ConsoleReaderTest.java b/src/jline/src/test/java/scala/tools/jline/console/ConsoleReaderTest.java deleted file mode 100644 index 0e6cba15a0..0000000000 --- a/src/jline/src/test/java/scala/tools/jline/console/ConsoleReaderTest.java +++ /dev/null @@ -1,261 +0,0 @@ -package scala.tools.jline.console; - -import scala.tools.jline.TerminalFactory; -import scala.tools.jline.WindowsTerminal; -import scala.tools.jline.console.history.History; -import scala.tools.jline.console.history.MemoryHistory; -import org.junit.Before; -import org.junit.Test; - -import java.io.ByteArrayInputStream; -import java.io.InputStream; -import java.io.StringWriter; -import java.io.Writer; - -import static scala.tools.jline.WindowsTerminal.WindowsKey.DELETE_KEY; -import static scala.tools.jline.WindowsTerminal.WindowsKey.END_KEY; -import static scala.tools.jline.WindowsTerminal.WindowsKey.ESCAPE_KEY; -import static scala.tools.jline.WindowsTerminal.WindowsKey.HOME_KEY; -import static scala.tools.jline.WindowsTerminal.WindowsKey.INSERT_KEY; -import static scala.tools.jline.WindowsTerminal.WindowsKey.LEFT_ARROW_KEY; -import static scala.tools.jline.WindowsTerminal.WindowsKey.NUMPAD_KEY_INDICATOR; -import static scala.tools.jline.WindowsTerminal.WindowsKey.PAGE_DOWN_KEY; -import static scala.tools.jline.WindowsTerminal.WindowsKey.PAGE_UP_KEY; -import static scala.tools.jline.WindowsTerminal.WindowsKey.SPECIAL_KEY_INDICATOR; -import static scala.tools.jline.console.Operation.DELETE_NEXT_CHAR; -import static scala.tools.jline.console.Operation.DELETE_PREV_CHAR; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -/** - * Tests for the {@link ConsoleReader}. - */ -public class ConsoleReaderTest -{ - @Before - public void setUp() throws Exception { - System.setProperty(WindowsTerminal.JLINE_WINDOWS_TERMINAL_DIRECT_CONSOLE, "false"); - } - - private void assertWindowsKeyBehavior(String expected, char[] input) throws Exception { - StringBuilder buffer = new StringBuilder(); - buffer.append(input); - ConsoleReader reader = createConsole(buffer.toString().getBytes()); - assertNotNull(reader); - String line = reader.readLine(); - assertEquals(expected, line); - } - - private ConsoleReader createConsole(byte[] bytes) throws Exception { - InputStream in = new ByteArrayInputStream(bytes); - Writer writer = new StringWriter(); - ConsoleReader reader = new ConsoleReader(in, writer); - reader.setHistory(createSeededHistory()); - return reader; - } - - private History createSeededHistory() { - History history = new MemoryHistory(); - history.add("dir"); - history.add("cd c:\\"); - history.add("mkdir monkey"); - return history; - } - - @Test - public void testDeleteAndBackspaceKeymappings() throws Exception { - // test only works on Windows - if (!(TerminalFactory.get() instanceof WindowsTerminal)) { - return; - } - - ConsoleReader consoleReader = new ConsoleReader(); - assertNotNull(consoleReader); - assertEquals(127, consoleReader.getKeyForAction(DELETE_NEXT_CHAR)); - assertEquals(8, consoleReader.getKeyForAction(DELETE_PREV_CHAR)); - } - - @Test - public void testReadline() throws Exception { - ConsoleReader consoleReader = createConsole("Sample String\r\n".getBytes()); - assertNotNull(consoleReader); - String line = consoleReader.readLine(); - assertEquals("Sample String", line); - } - - @Test - public void testDeleteOnWindowsTerminal() throws Exception { - // test only works on Windows - if (!(TerminalFactory.get() instanceof WindowsTerminal)) { - return; - } - - char[] characters = new char[]{ - 'S', 's', - (char) SPECIAL_KEY_INDICATOR.code, - (char) LEFT_ARROW_KEY.code, - (char) SPECIAL_KEY_INDICATOR.code, - (char) DELETE_KEY.code, '\r', 'n' - }; - assertWindowsKeyBehavior("S", characters); - } - - @Test - public void testNumpadDeleteOnWindowsTerminal() throws Exception { - // test only works on Windows - if (!(TerminalFactory.get() instanceof WindowsTerminal)) { - return; - } - - char[] characters = new char[]{ - 'S', 's', - (char) NUMPAD_KEY_INDICATOR.code, - (char) LEFT_ARROW_KEY.code, - (char) NUMPAD_KEY_INDICATOR.code, - (char) DELETE_KEY.code, '\r', 'n' - }; - assertWindowsKeyBehavior("S", characters); - } - - @Test - public void testHomeKeyOnWindowsTerminal() throws Exception { - // test only works on Windows - if (!(TerminalFactory.get() instanceof WindowsTerminal)) { - return; - } - - char[] characters = new char[]{ - 'S', 's', - (char) SPECIAL_KEY_INDICATOR.code, - (char) HOME_KEY.code, 'x', '\r', '\n' - }; - assertWindowsKeyBehavior("xSs", characters); - - } - - @Test - public void testEndKeyOnWindowsTerminal() throws Exception { - // test only works on Windows - if (!(TerminalFactory.get() instanceof WindowsTerminal)) { - return; - } - - char[] characters = new char[]{ - 'S', 's', - (char) SPECIAL_KEY_INDICATOR.code, - (char) HOME_KEY.code, 'x', - (char) SPECIAL_KEY_INDICATOR.code, (char) END_KEY.code, - 'j', '\r', '\n' - }; - assertWindowsKeyBehavior("xSsj", characters); - } - - @Test - public void testPageUpOnWindowsTerminal() throws Exception { - // test only works on Windows - if (!(TerminalFactory.get() instanceof WindowsTerminal)) { - return; - } - - char[] characters = new char[]{ - (char) SPECIAL_KEY_INDICATOR.code, - (char) PAGE_UP_KEY.code, '\r', '\n' - }; - assertWindowsKeyBehavior("dir", characters); - } - - @Test - public void testPageDownOnWindowsTerminal() throws Exception { - // test only works on Windows - if (!(TerminalFactory.get() instanceof WindowsTerminal)) { - return; - } - - char[] characters = new char[]{ - (char) SPECIAL_KEY_INDICATOR.code, - (char) PAGE_DOWN_KEY.code, '\r', '\n' - }; - assertWindowsKeyBehavior("mkdir monkey", characters); - } - - @Test - public void testEscapeOnWindowsTerminal() throws Exception { - // test only works on Windows - if (!(TerminalFactory.get() instanceof WindowsTerminal)) { - return; - } - - char[] characters = new char[]{ - 's', 's', 's', - (char) SPECIAL_KEY_INDICATOR.code, - (char) ESCAPE_KEY.code, '\r', '\n' - }; - assertWindowsKeyBehavior("", characters); - } - - @Test - public void testInsertOnWindowsTerminal() throws Exception { - // test only works on Windows - if (!(TerminalFactory.get() instanceof WindowsTerminal)) { - return; - } - - char[] characters = new char[]{ - 'o', 'p', 's', - (char) SPECIAL_KEY_INDICATOR.code, - (char) HOME_KEY.code, - (char) SPECIAL_KEY_INDICATOR.code, - (char) INSERT_KEY.code, 'o', 'o', 'p', 's', '\r', '\n' - }; - assertWindowsKeyBehavior("oops", characters); - } - - @Test - public void testExpansion() throws Exception { - ConsoleReader reader = new ConsoleReader(); - MemoryHistory history = new MemoryHistory(); - history.setMaxSize(3); - history.add("foo"); - history.add("dir"); - history.add("cd c:\\"); - history.add("mkdir monkey"); - reader.setHistory(history); - - assertEquals("echo a!", reader.expandEvents("echo a!")); - assertEquals("mkdir monkey ; echo a!", reader.expandEvents("!! ; echo a!")); - assertEquals("echo ! a", reader.expandEvents("echo ! a")); - assertEquals("echo !\ta", reader.expandEvents("echo !\ta")); - - assertEquals("mkdir barey", reader.expandEvents("^monk^bar^")); - assertEquals("mkdir barey", reader.expandEvents("^monk^bar")); - assertEquals("a^monk^bar", reader.expandEvents("a^monk^bar")); - - assertEquals("mkdir monkey", reader.expandEvents("!!")); - assertEquals("echo echo a", reader.expandEvents("echo !#a")); - - assertEquals("mkdir monkey", reader.expandEvents("!mk")); - try { - reader.expandEvents("!mz"); - } catch (IllegalArgumentException e) { - assertEquals("!mz: event not found", e.getMessage()); - } - - assertEquals("mkdir monkey", reader.expandEvents("!?mo")); - assertEquals("mkdir monkey", reader.expandEvents("!?mo?")); - - assertEquals("mkdir monkey", reader.expandEvents("!-1")); - assertEquals("cd c:\\", reader.expandEvents("!-2")); - assertEquals("cd c:\\", reader.expandEvents("!2")); - assertEquals("mkdir monkey", reader.expandEvents("!3")); - try { - reader.expandEvents("!20"); - } catch (IllegalArgumentException e) { - assertEquals("!20: event not found", e.getMessage()); - } - try { - reader.expandEvents("!-20"); - } catch (IllegalArgumentException e) { - assertEquals("!-20: event not found", e.getMessage()); - } - } -} diff --git a/src/jline/src/test/java/scala/tools/jline/console/ConsoleReaderTestSupport.java b/src/jline/src/test/java/scala/tools/jline/console/ConsoleReaderTestSupport.java deleted file mode 100644 index c19099f0b2..0000000000 --- a/src/jline/src/test/java/scala/tools/jline/console/ConsoleReaderTestSupport.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ -package scala.tools.jline.console; - -import scala.tools.jline.UnixTerminal; -import org.junit.Before; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; - -import static scala.tools.jline.UnixTerminal.UnixKey.ARROW_DOWN; -import static scala.tools.jline.UnixTerminal.UnixKey.ARROW_LEFT; -import static scala.tools.jline.UnixTerminal.UnixKey.ARROW_PREFIX; -import static scala.tools.jline.UnixTerminal.UnixKey.ARROW_RIGHT; -import static scala.tools.jline.UnixTerminal.UnixKey.ARROW_START; -import static scala.tools.jline.UnixTerminal.UnixKey.ARROW_UP; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -/** - * Provides support for console reader tests. - */ -public abstract class ConsoleReaderTestSupport -{ - protected ConsoleReader console; - - @Before - public void setUp() throws Exception { - console = new ConsoleReader(null, new PrintWriter(new OutputStreamWriter(new ByteArrayOutputStream())), new UnixTerminal()); - } - - protected void assertBuffer(final String expected, final Buffer buffer) throws IOException { - assertBuffer(expected, buffer, true); - } - - protected void assertBuffer(final String expected, final Buffer buffer, final boolean clear) throws IOException { - // clear current buffer, if any - if (clear) { - console.finishBuffer(); - console.getHistory().clear(); - } - - console.setInput(new ByteArrayInputStream(buffer.getBytes())); - - // run it through the reader - while (console.readLine((String) null) != null) { - // ignore - } - - assertEquals(expected, console.getCursorBuffer().toString()); - } - - private int getKeyForAction(final Operation key) { - return getKeyForAction(key.code); - } - - private int getKeyForAction(final short logicalAction) { - int action = console.getKeyForAction(logicalAction); - - if (action == -1) { - console.printBindings(); - fail("Keystroke for logical action " + logicalAction + " was not bound in the console"); - } - - return action; - } - - protected class Buffer - { - private final ByteArrayOutputStream out = new ByteArrayOutputStream(); - - public Buffer() { - // nothing - } - - public Buffer(final String str) { - append(str); - } - - public byte[] getBytes() { - return out.toByteArray(); - } - - public Buffer op(final short operation) { - return append(getKeyForAction(operation)); - } - - public Buffer op(final Operation op) { - return op(op.code); - } - - public Buffer ctrlA() { - return append(getKeyForAction(Operation.MOVE_TO_BEG)); - } - - public Buffer ctrlU() { - return append(getKeyForAction(Operation.KILL_LINE_PREV)); - } - - public Buffer tab() { - return append(getKeyForAction(Operation.COMPLETE)); - } - - public Buffer back() { - return append(getKeyForAction(Operation.DELETE_PREV_CHAR)); - } - - public Buffer left() { - return append(ARROW_START.code).append(ARROW_PREFIX.code).append(ARROW_LEFT.code); - } - - public Buffer right() { - return append(ARROW_START.code).append(ARROW_PREFIX.code).append(ARROW_RIGHT.code); - } - - public Buffer up() { - return append(ARROW_START.code).append(ARROW_PREFIX.code).append(ARROW_UP.code); - } - - public Buffer down() { - return append(ARROW_START.code).append(ARROW_PREFIX.code).append(ARROW_DOWN.code); - } - - public Buffer append(final String str) { - for (byte b : str.getBytes()) { - append(b); - } - return this; - } - - public Buffer append(final int i) { - out.write((byte) i); - return this; - } - } -} diff --git a/src/jline/src/test/java/scala/tools/jline/console/EditLineTest.java b/src/jline/src/test/java/scala/tools/jline/console/EditLineTest.java deleted file mode 100644 index 6f5d46121e..0000000000 --- a/src/jline/src/test/java/scala/tools/jline/console/EditLineTest.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ -package scala.tools.jline.console; - -import org.junit.Test; - -import static scala.tools.jline.console.Operation.*; - -/** - * Tests various features of editing lines. - * - * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> - */ -public class EditLineTest - extends ConsoleReaderTestSupport -{ - @Test - public void testDeletePreviousWord() throws Exception { - Buffer b = new Buffer("This is a test"); - - assertBuffer("This is a ", b = b.op(DELETE_PREV_WORD)); - assertBuffer("This is ", b = b.op(DELETE_PREV_WORD)); - assertBuffer("This ", b = b.op(DELETE_PREV_WORD)); - assertBuffer("", b = b.op(DELETE_PREV_WORD)); - assertBuffer("", b = b.op(DELETE_PREV_WORD)); - assertBuffer("", b.op(DELETE_PREV_WORD)); - } - - @Test - public void testDeleteNextWord() throws Exception { - Buffer b = new Buffer("This is a test "); - - assertBuffer(" is a test ", b = b.op(MOVE_TO_BEG).op(DELETE_NEXT_WORD)); - assertBuffer(" a test ", b = b.op(DELETE_NEXT_WORD)); - assertBuffer(" test ", b = b.op(DELETE_NEXT_WORD)); - assertBuffer(" ", b = b.op(DELETE_NEXT_WORD)); - assertBuffer("", b = b.op(DELETE_NEXT_WORD)); - assertBuffer("", b.op(DELETE_NEXT_WORD)); - } - - @Test - public void testMoveToEnd() throws Exception { - Buffer b = new Buffer("This is a test"); - - assertBuffer("This is a XtestX", - new Buffer("This is a test").op(PREV_WORD) - .append('X') - .op(MOVE_TO_END) - .append('X')); - - assertBuffer("This is Xa testX", - new Buffer("This is a test").op(PREV_WORD) - .op(PREV_WORD) - .append('X') - .op(MOVE_TO_END) - .append('X')); - - assertBuffer("This Xis a testX", - new Buffer("This is a test").op(PREV_WORD) - .op(PREV_WORD) - .op(PREV_WORD) - .append('X') - .op(MOVE_TO_END) - .append('X')); - } - - @Test - public void testPreviousWord() throws Exception { - assertBuffer("This is a Xtest", - new Buffer("This is a test").op(PREV_WORD) - .append('X')); - assertBuffer("This is Xa test", - new Buffer("This is a test").op(PREV_WORD) - .op(PREV_WORD) - .append('X')); - assertBuffer("This Xis a test", - new Buffer("This is a test").op(PREV_WORD) - .op(PREV_WORD) - .op(PREV_WORD) - .append('X')); - assertBuffer("XThis is a test", - new Buffer("This is a test").op(PREV_WORD) - .op(PREV_WORD) - .op(PREV_WORD) - .op(PREV_WORD) - .append('X')); - assertBuffer("XThis is a test", - new Buffer("This is a test").op(PREV_WORD) - .op(PREV_WORD) - .op(PREV_WORD) - .op(PREV_WORD) - .op(PREV_WORD) - .append('X')); - assertBuffer("XThis is a test", - new Buffer("This is a test").op(PREV_WORD) - .op(PREV_WORD) - .op(PREV_WORD) - .op(PREV_WORD) - .op(PREV_WORD) - .op(PREV_WORD) - .append('X')); - } - - @Test - public void testNextWord() throws Exception { - assertBuffer("ThisX is a test", - new Buffer("This is a test").op(MOVE_TO_BEG) - .op(NEXT_WORD) - .append('X')); - assertBuffer("This isX a test", - new Buffer("This is a test").op(MOVE_TO_BEG) - .op(NEXT_WORD) - .op(NEXT_WORD) - .append('X')); - assertBuffer("This is aX test", - new Buffer("This is a test").op(MOVE_TO_BEG) - .op(NEXT_WORD) - .op(NEXT_WORD) - .op(NEXT_WORD) - .append('X')); - assertBuffer("This is a testX ", - new Buffer("This is a test ").op(MOVE_TO_BEG) - .op(NEXT_WORD) - .op(NEXT_WORD) - .op(NEXT_WORD) - .op(NEXT_WORD) - .append('X')); - } - - @Test - public void testLineStart() throws Exception { - assertBuffer("XThis is a test", - new Buffer("This is a test").ctrlA().append('X')); - assertBuffer("TXhis is a test", - new Buffer("This is a test").ctrlA().right().append('X')); - } - - @Test - public void testClearLine() throws Exception { - assertBuffer("", new Buffer("This is a test").ctrlU()); - assertBuffer("t", new Buffer("This is a test").left().ctrlU()); - assertBuffer("st", new Buffer("This is a test").left().left().ctrlU()); - } - - @Test - public void testRight() throws Exception { - Buffer b = new Buffer("This is a test"); - b = b.left().right().back(); - assertBuffer("This is a tes", b); - b = b.left().left().left().right().left().back(); - assertBuffer("This is ates", b); - b.append('X'); - assertBuffer("This is aXtes", b); - } - - @Test - public void testLeft() throws Exception { - Buffer b = new Buffer("This is a test"); - b = b.left().left().left(); - assertBuffer("This is a est", b = b.back()); - assertBuffer("This is aest", b = b.back()); - assertBuffer("This is est", b = b.back()); - assertBuffer("This isest", b = b.back()); - assertBuffer("This iest", b = b.back()); - assertBuffer("This est", b = b.back()); - assertBuffer("Thisest", b = b.back()); - assertBuffer("Thiest", b = b.back()); - assertBuffer("Thest", b = b.back()); - assertBuffer("Test", b = b.back()); - assertBuffer("est", b = b.back()); - assertBuffer("est", b = b.back()); - assertBuffer("est", b = b.back()); - assertBuffer("est", b = b.back()); - assertBuffer("est", b.back()); - } - - @Test - public void testBackspace() throws Exception { - Buffer b = new Buffer("This is a test"); - assertBuffer("This is a tes", b = b.back()); - assertBuffer("This is a te", b = b.back()); - assertBuffer("This is a t", b = b.back()); - assertBuffer("This is a ", b = b.back()); - assertBuffer("This is a", b = b.back()); - assertBuffer("This is ", b = b.back()); - assertBuffer("This is", b = b.back()); - assertBuffer("This i", b = b.back()); - assertBuffer("This ", b = b.back()); - assertBuffer("This", b = b.back()); - assertBuffer("Thi", b = b.back()); - assertBuffer("Th", b = b.back()); - assertBuffer("T", b = b.back()); - assertBuffer("", b = b.back()); - assertBuffer("", b = b.back()); - assertBuffer("", b = b.back()); - assertBuffer("", b = b.back()); - assertBuffer("", b.back()); - } - - @Test - public void testBuffer() throws Exception { - assertBuffer("This is a test", new Buffer("This is a test")); - } -} diff --git a/src/jline/src/test/java/scala/tools/jline/console/completer/ArgumentCompleterTest.java b/src/jline/src/test/java/scala/tools/jline/console/completer/ArgumentCompleterTest.java deleted file mode 100644 index 9e2a2ab031..0000000000 --- a/src/jline/src/test/java/scala/tools/jline/console/completer/ArgumentCompleterTest.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2010 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package scala.tools.jline.console.completer; - -import scala.tools.jline.console.ConsoleReaderTestSupport; -import scala.tools.jline.console.completer.ArgumentCompleter; -import scala.tools.jline.console.completer.StringsCompleter; -import org.junit.Test; - -/** - * Tests for {@link jline.console.completer.ArgumentCompleter}. - * - * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> - */ -public class ArgumentCompleterTest - extends ConsoleReaderTestSupport -{ - @Test - public void test1() throws Exception { - console.addCompleter(new ArgumentCompleter(new StringsCompleter("foo", "bar", "baz"))); - - assertBuffer("foo foo ", new Buffer("foo f").tab()); - assertBuffer("foo ba", new Buffer("foo b").tab()); - assertBuffer("foo ba", new Buffer("foo ba").tab()); - assertBuffer("foo baz ", new Buffer("foo baz").tab()); - - // test completion in the mid range - assertBuffer("foo baz", new Buffer("f baz").left().left().left().left().tab()); - assertBuffer("ba foo", new Buffer("b foo").left().left().left().left().tab()); - assertBuffer("foo ba baz", new Buffer("foo b baz").left().left().left().left().tab()); - assertBuffer("foo foo baz", new Buffer("foo f baz").left().left().left().left().tab()); - } -}
\ No newline at end of file diff --git a/src/jline/src/test/java/scala/tools/jline/console/completer/NullCompleterTest.java b/src/jline/src/test/java/scala/tools/jline/console/completer/NullCompleterTest.java deleted file mode 100644 index 70a4c3b554..0000000000 --- a/src/jline/src/test/java/scala/tools/jline/console/completer/NullCompleterTest.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2010 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package scala.tools.jline.console.completer; - -import scala.tools.jline.console.ConsoleReaderTestSupport; -import scala.tools.jline.console.completer.NullCompleter; -import org.junit.Test; - -/** - * Tests for {@link NullCompleter}. - * - * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> - */ -public class NullCompleterTest - extends ConsoleReaderTestSupport -{ - @Test - public void test1() throws Exception { - console.addCompleter(NullCompleter.INSTANCE); - - assertBuffer("f", new Buffer("f").tab()); - assertBuffer("ba", new Buffer("ba").tab()); - assertBuffer("baz", new Buffer("baz").tab()); - } -}
\ No newline at end of file diff --git a/src/jline/src/test/java/scala/tools/jline/console/completer/StringsCompleterTest.java b/src/jline/src/test/java/scala/tools/jline/console/completer/StringsCompleterTest.java deleted file mode 100644 index 518b88d031..0000000000 --- a/src/jline/src/test/java/scala/tools/jline/console/completer/StringsCompleterTest.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2010 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package scala.tools.jline.console.completer; - -import scala.tools.jline.console.ConsoleReaderTestSupport; -import scala.tools.jline.console.completer.StringsCompleter; -import org.junit.Test; - -/** - * Tests for {@link jline.console.completer.StringsCompleter}. - * - * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> - */ -public class StringsCompleterTest - extends ConsoleReaderTestSupport -{ - @Test - public void test1() throws Exception { - console.addCompleter(new StringsCompleter("foo", "bar", "baz")); - - assertBuffer("foo ", new Buffer("f").tab()); - // single tab completes to unambiguous "ba" - assertBuffer("ba", new Buffer("b").tab()); - assertBuffer("ba", new Buffer("ba").tab()); - assertBuffer("baz ", new Buffer("baz").tab()); - } -}
\ No newline at end of file diff --git a/src/jline/src/test/java/scala/tools/jline/console/history/HistoryTest.java b/src/jline/src/test/java/scala/tools/jline/console/history/HistoryTest.java deleted file mode 100644 index 0a987b2b26..0000000000 --- a/src/jline/src/test/java/scala/tools/jline/console/history/HistoryTest.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ -package scala.tools.jline.console.history; - -import scala.tools.jline.console.ConsoleReaderTestSupport; -import org.junit.Test; - -import static scala.tools.jline.console.Operation.MOVE_TO_BEG; -import static scala.tools.jline.console.Operation.NEWLINE; -import static scala.tools.jline.console.Operation.NEXT_HISTORY; -import static scala.tools.jline.console.Operation.PREV_HISTORY; -import static scala.tools.jline.console.Operation.PREV_CHAR; - -/** - * Tests command history. - * - * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> - */ -public class HistoryTest - extends ConsoleReaderTestSupport -{ - @Test - public void testSingleHistory() throws Exception { - Buffer b = new Buffer(). - append("test line 1").op(NEWLINE). - append("test line 2").op(NEWLINE). - append("test line 3").op(NEWLINE). - append("test line 4").op(NEWLINE). - append("test line 5").op(NEWLINE). - append(""); - - assertBuffer("", b); - - assertBuffer("test line 5", b = b.op(PREV_HISTORY)); - assertBuffer("test line 5", b = b.op(PREV_CHAR)); - assertBuffer("test line 4", b = b.op(PREV_HISTORY)); - assertBuffer("test line 5", b = b.op(NEXT_HISTORY)); - assertBuffer("test line 4", b = b.op(PREV_HISTORY)); - assertBuffer("test line 3", b = b.op(PREV_HISTORY)); - assertBuffer("test line 2", b = b.op(PREV_HISTORY)); - assertBuffer("test line 1", b = b.op(PREV_HISTORY)); - - // beginning of history - assertBuffer("test line 1", b = b.op(PREV_HISTORY)); - assertBuffer("test line 1", b = b.op(PREV_HISTORY)); - assertBuffer("test line 1", b = b.op(PREV_HISTORY)); - assertBuffer("test line 1", b = b.op(PREV_HISTORY)); - - assertBuffer("test line 2", b = b.op(NEXT_HISTORY)); - assertBuffer("test line 3", b = b.op(NEXT_HISTORY)); - assertBuffer("test line 4", b = b.op(NEXT_HISTORY)); - assertBuffer("test line 5", b = b.op(NEXT_HISTORY)); - - // end of history - assertBuffer("", b = b.op(NEXT_HISTORY)); - assertBuffer("", b = b.op(NEXT_HISTORY)); - assertBuffer("", b = b.op(NEXT_HISTORY)); - - assertBuffer("test line 5", b = b.op(PREV_HISTORY)); - assertBuffer("test line 4", b = b.op(PREV_HISTORY)); - b = b.op(MOVE_TO_BEG).append("XXX").op(NEWLINE); - assertBuffer("XXXtest line 4", b = b.op(PREV_HISTORY)); - assertBuffer("test line 5", b = b.op(PREV_HISTORY)); - assertBuffer("test line 4", b = b.op(PREV_HISTORY)); - assertBuffer("test line 5", b = b.op(NEXT_HISTORY)); - assertBuffer("XXXtest line 4", b = b.op(NEXT_HISTORY)); - assertBuffer("", b = b.op(NEXT_HISTORY)); - - assertBuffer("XXXtest line 4", b = b.op(PREV_HISTORY)); - assertBuffer("XXXtest line 4", b = b.op(NEWLINE).op(PREV_HISTORY)); - assertBuffer("XXXtest line 4", b = b.op(NEWLINE).op(PREV_HISTORY)); - assertBuffer("XXXtest line 4", b = b.op(NEWLINE).op(PREV_HISTORY)); - assertBuffer("XXXtest line 4", b = b.op(NEWLINE).op(PREV_HISTORY)); - } -} diff --git a/src/jline/src/test/java/scala/tools/jline/console/history/MemoryHistoryTest.java b/src/jline/src/test/java/scala/tools/jline/console/history/MemoryHistoryTest.java deleted file mode 100644 index 91b81548c8..0000000000 --- a/src/jline/src/test/java/scala/tools/jline/console/history/MemoryHistoryTest.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2010 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package scala.tools.jline.console.history; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import static junit.framework.Assert.*; - -/** - * Tests for {@link MemoryHistory}. - * - * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> - */ -public class MemoryHistoryTest -{ - private MemoryHistory history; - - @Before - public void setUp() { - history = new MemoryHistory(); - } - - @After - public void tearDown() { - history = null; - } - - @Test - public void testAdd() { - assertEquals(0, history.size()); - - history.add("test"); - - assertEquals(1, history.size()); - assertEquals("test", history.get(0)); - assertEquals(1, history.index()); - } - - private void assertHistoryContains(final int offset, final String... items) { - assertEquals(items.length, history.size()); - int i=0; - for (History.Entry entry : history) { - assertEquals(offset + i, entry.index()); - assertEquals(items[i++], entry.value()); - } - } - - @Test - public void testOffset() { - history.setMaxSize(5); - - assertEquals(0, history.size()); - assertEquals(0, history.index()); - - history.add("a"); - history.add("b"); - history.add("c"); - history.add("d"); - history.add("e"); - - assertEquals(5, history.size()); - assertEquals(5, history.index()); - assertHistoryContains(0, "a", "b", "c", "d", "e"); - - history.add("f"); - - assertEquals(5, history.size()); - assertEquals(6, history.index()); - - assertHistoryContains(1, "b", "c", "d", "e", "f"); - assertEquals("f", history.get(5)); - } - - @Test - public void testReplace() { - assertEquals(0, history.size()); - - history.add("a"); - history.add("b"); - history.replace("c"); - - assertHistoryContains(0, "a", "c"); - } -}
\ No newline at end of file diff --git a/src/jline/src/test/java/scala/tools/jline/example/Example.java b/src/jline/src/test/java/scala/tools/jline/example/Example.java deleted file mode 100644 index a89a09c5c9..0000000000 --- a/src/jline/src/test/java/scala/tools/jline/example/Example.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (c) 2002-2006, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ -package scala.tools.jline.example; - -import scala.tools.jline.console.completer.*; -import scala.tools.jline.console.ConsoleReader; - -import java.io.*; -import java.util.*; - -public class Example -{ - public static void usage() { - System.out.println("Usage: java " + Example.class.getName() - + " [none/simple/files/dictionary [trigger mask]]"); - System.out.println(" none - no completors"); - System.out.println(" simple - a simple completor that comples " - + "\"foo\", \"bar\", and \"baz\""); - System.out - .println(" files - a completor that comples " + "file names"); - System.out.println(" classes - a completor that comples " - + "java class names"); - System.out - .println(" trigger - a special word which causes it to assume " - + "the next line is a password"); - System.out.println(" mask - is the character to print in place of " - + "the actual password character"); - System.out.println(" color - colored prompt and feedback"); - System.out.println("\n E.g - java Example simple su '*'\n" - + "will use the simple compleator with 'su' triggering\n" - + "the use of '*' as a password mask."); - } - - public static void main(String[] args) throws IOException { - Character mask = null; - String trigger = null; - boolean color = false; - - ConsoleReader reader = new ConsoleReader(); - - reader.setBellEnabled(false); - reader.setPrompt("prompt> "); - - if ((args == null) || (args.length == 0)) { - usage(); - - return; - } - - List<Completer> completors = new LinkedList<Completer>(); - - if (args.length > 0) { - if (args[0].equals("none")) { - } - else if (args[0].equals("files")) { - completors.add(new FileNameCompleter()); - } - else if (args[0].equals("simple")) { - completors.add(new StringsCompleter("foo", "bar", "baz")); - } - else if (args[0].equals("color")) { - color = true; - reader.setPrompt("\u001B[1mfoo\u001B[0m@bar\u001B[32m@baz\u001B[0m> "); - } - else { - usage(); - - return; - } - } - - if (args.length == 3) { - mask = args[2].charAt(0); - trigger = args[1]; - } - - for (Completer c : completors) { - reader.addCompleter(c); - } - - String line; - PrintWriter out = new PrintWriter( - reader.getTerminal().wrapOutIfNeeded(System.out)); - - while ((line = reader.readLine()) != null) { - if (color){ - out.println("\u001B[33m======>\u001B[0m\"" + line + "\""); - } else { - out.println("======>\"" + line + "\""); - } - out.flush(); - - // If we input the special word then we will mask - // the next line. - if ((trigger != null) && (line.compareTo(trigger) == 0)) { - line = reader.readLine("password> ", mask); - } - if (line.equalsIgnoreCase("quit") || line.equalsIgnoreCase("exit")) { - break; - } - } - } -} diff --git a/src/jline/src/test/java/scala/tools/jline/internal/TerminalLineSettingsTest.java b/src/jline/src/test/java/scala/tools/jline/internal/TerminalLineSettingsTest.java deleted file mode 100644 index 3af10887f1..0000000000 --- a/src/jline/src/test/java/scala/tools/jline/internal/TerminalLineSettingsTest.java +++ /dev/null @@ -1,146 +0,0 @@ -package scala.tools.jline.internal; - -import org.junit.Before; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; - -/** - * Tests for the {@link TerminalLineSettings}. - * - * @author <a href="mailto:jbonofre@apache.org">Jean-Baptiste Onofré</a> - */ -public class TerminalLineSettingsTest -{ - private TerminalLineSettings settings; - - private final String linuxSttySample = "speed 38400 baud; rows 85; columns 244; line = 0;\n" + - "intr = ^C; quit = ^\\; erase = ^?; kill = ^U; eof = ^D; eol = M-^?; eol2 = M-^?; swtch = M-^?; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0;\n" + - "-parenb -parodd cs8 hupcl -cstopb cread -clocal -crtscts\n" + - "-ignbrk brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc ixany imaxbel iutf8\n" + - "opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0\n" + - "isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke"; - - private final String solarisSttySample = "speed 38400 baud; \n" + - "rows = 85; columns = 244; ypixels = 0; xpixels = 0;\n" + - "csdata ?\n" + - "eucw 1:0:0:0, scrw 1:0:0:0\n" + - "intr = ^c; quit = ^\\; erase = ^?; kill = ^u;\n" + - "eof = ^d; eol = -^?; eol2 = -^?; swtch = <undef>;\n" + - "start = ^q; stop = ^s; susp = ^z; dsusp = ^y;\n" + - "rprnt = ^r; flush = ^o; werase = ^w; lnext = ^v;\n" + - "-parenb -parodd cs8 -cstopb -hupcl cread -clocal -loblk -crtscts -crtsxoff -parext \n" + - "-ignbrk brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl -iuclc \n" + - "ixon ixany -ixoff imaxbel \n" + - "isig icanon -xcase echo echoe echok -echonl -noflsh \n" + - "-tostop echoctl -echoprt echoke -defecho -flusho -pendin iexten \n" + - "opost -olcuc onlcr -ocrnl -onocr -onlret -ofill -ofdel tab3"; - - private final String aixSttySample = "speed 38400 baud; 85 rows; 244 columns;\n" + - "eucw 1:1:0:0, scrw 1:1:0:0:\n" + - "intr = ^C; quit = ^\\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>\n" + - "eol2 = <undef>; start = ^Q; stop = ^S; susp = ^Z; dsusp = ^Y; reprint = ^R\n" + - "discard = ^O; werase = ^W; lnext = ^V\n" + - "-parenb -parodd cs8 -cstopb -hupcl cread -clocal -parext \n" + - "-ignbrk brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl -iuclc \n" + - "ixon ixany -ixoff imaxbel \n" + - "isig icanon -xcase echo echoe echok -echonl -noflsh \n" + - "-tostop echoctl -echoprt echoke -flusho -pending iexten \n" + - "opost -olcuc onlcr -ocrnl -onocr -onlret -ofill -ofdel tab3"; - - private final String macOsSttySample = "speed 9600 baud; 47 rows; 155 columns;\n" + - "lflags: icanon isig iexten echo echoe -echok echoke -echonl echoctl\n" + - "-echoprt -altwerase -noflsh -tostop -flusho pendin -nokerninfo\n" + - "-extproc\n" + - "iflags: -istrip icrnl -inlcr -igncr ixon -ixoff ixany imaxbel iutf8\n" + - "-ignbrk brkint -inpck -ignpar -parmrk\n" + - "oflags: opost onlcr -oxtabs -onocr -onlret\n" + - "cflags: cread cs8 -parenb -parodd hupcl -clocal -cstopb -crtscts -dsrflow\n" + - "-dtrflow -mdmbuf\n" + - "cchars: discard = ^O; dsusp = ^Y; eof = ^D; eol = <undef>;\n" + - "eol2 = <undef>; erase = ^?; intr = ^C; kill = ^U; lnext = ^V;\n" + - "min = 1; quit = ^\\; reprint = ^R; start = ^Q; status = ^T;\n" + - "stop = ^S; susp = ^Z; time = 0; werase = ^W;"; - - private final String netBsdSttySample = "speed 38400 baud; 85 rows; 244 columns;\n" + - "lflags: icanon isig iexten echo echoe echok echoke -echonl echoctl\n" + - " -echoprt -altwerase -noflsh -tostop -flusho pendin -nokerninfo\n" + - " -extproc\n" + - "iflags: -istrip icrnl -inlcr -igncr ixon -ixoff ixany imaxbel -ignbrk\n" + - " brkint -inpck -ignpar -parmrk\n" + - "oflags: opost onlcr -ocrnl oxtabs onocr onlret\n" + - "cflags: cread cs8 -parenb -parodd hupcl -clocal -cstopb -crtscts -mdmbuf\n" + - " -cdtrcts\n" + - "cchars: discard = ^O; dsusp = ^Y; eof = ^D; eol = <undef>;\n" + - " eol2 = <undef>; erase = ^?; intr = ^C; kill = ^U; lnext = ^V;\n" + - " min = 1; quit = ^\\; reprint = ^R; start = ^Q; status = ^T;\n" + - " stop = ^S; susp = ^Z; time = 0; werase = ^W;"; - - private final String freeBsdSttySample = "speed 9600 baud; 32 rows; 199 columns;\n" + - "lflags: icanon isig iexten echo echoe echok echoke -echonl echoctl\n" + - " -echoprt -altwerase -noflsh -tostop -flusho -pendin -nokerninfo\n" + - " -extproc\n" + - "iflags: -istrip icrnl -inlcr -igncr ixon -ixoff ixany imaxbel -ignbrk\n" + - " brkint -inpck -ignpar -parmrk\n" + - "oflags: opost onlcr -ocrnl tab0 -onocr -onlret\n" + - "cflags: cread cs8 -parenb -parodd hupcl -clocal -cstopb -crtscts -dsrflow\n" + - " -dtrflow -mdmbuf\n" + - "cchars: discard = ^O; dsusp = ^Y; eof = ^D; eol = <undef>;\n" + - " eol2 = <undef>; erase = ^?; erase2 = ^H; intr = ^C; kill = ^U;\n" + - " lnext = ^V; min = 1; quit = ^\\; reprint = ^R; start = ^Q;\n" + - " status = ^T; stop = ^S; susp = ^Z; time = 0; werase = ^W;"; - - @Before - public void setUp() throws Exception { - settings = new TerminalLineSettings(); - } - - @Test - public void testGetConfig() { - String config = settings.getConfig(); - System.out.println(config); - } - - @Test - public void testLinuxSttyParsing() { - assertEquals(0x7f, settings.getProperty("erase", linuxSttySample)); - assertEquals(244, settings.getProperty("columns", linuxSttySample)); - assertEquals(85, settings.getProperty("rows", linuxSttySample)); - } - - @Test - public void testSolarisSttyParsing() { - assertEquals(0x7f, settings.getProperty("erase", solarisSttySample)); - assertEquals(244, settings.getProperty("columns", solarisSttySample)); - assertEquals(85, settings.getProperty("rows", solarisSttySample)); - } - - @Test - public void testAixSttyParsing() { - assertEquals(0x7f, settings.getProperty("erase", aixSttySample)); - assertEquals(244, settings.getProperty("columns", aixSttySample)); - assertEquals(85, settings.getProperty("rows", aixSttySample)); - } - - @Test - public void testMacOsSttyParsing() { - assertEquals(0x7f, settings.getProperty("erase", macOsSttySample)); - assertEquals(155, settings.getProperty("columns", macOsSttySample)); - assertEquals(47, settings.getProperty("rows", macOsSttySample)); - } - - @Test - public void testNetBsdSttyParsing() { - assertEquals(0x7f, settings.getProperty("erase", netBsdSttySample)); - assertEquals(244, settings.getProperty("columns", netBsdSttySample)); - assertEquals(85, settings.getProperty("rows", netBsdSttySample)); - } - - @Test - public void testFreeBsdSttyParsing() { - assertEquals(0x7f, settings.getProperty("erase", freeBsdSttySample)); - assertEquals(199, settings.getProperty("columns", freeBsdSttySample)); - assertEquals(32, settings.getProperty("rows", freeBsdSttySample)); - } - -}
\ No newline at end of file diff --git a/src/library/scala/StringContext.scala b/src/library/scala/StringContext.scala index ed3b305d59..2632994a34 100644 --- a/src/library/scala/StringContext.scala +++ b/src/library/scala/StringContext.scala @@ -163,7 +163,7 @@ case class StringContext(parts: String*) { */ // The implementation is hardwired to `scala.tools.reflect.MacroImplementations.macro_StringInterpolation_f` // Using the mechanism implemented in `scala.tools.reflect.FastTrack` - def f(args: Any*): String = macro ??? + def f[A >: Any](args: A*): String = macro ??? } object StringContext { @@ -173,8 +173,13 @@ object StringContext { * @param str The offending string * @param idx The index of the offending backslash character in `str`. */ - class InvalidEscapeException(str: String, @deprecatedName('idx) val index: Int) - extends IllegalArgumentException("invalid escape character at index "+index+" in \""+str+"\"") + class InvalidEscapeException(str: String, @deprecatedName('idx) val index: Int) extends IllegalArgumentException( + s"""invalid escape ${ + require(index >= 0 && index < str.length) + val ok = """[\b, \t, \n, \f, \r, \\, \", \']""" + if (index == str.length - 1) "at terminal" else s"'\\${str(index + 1)}' not one of $ok at" + } index $index in "$str". Use \\\\ for literal \\.""" + ) /** Expands standard Scala escape sequences in a string. * Escape sequences are: diff --git a/src/library/scala/collection/IterableProxy.scala b/src/library/scala/collection/IterableProxy.scala index 3a0e2ab115..97aa830c5a 100644 --- a/src/library/scala/collection/IterableProxy.scala +++ b/src/library/scala/collection/IterableProxy.scala @@ -16,4 +16,5 @@ package collection * @version 2.8 * @since 2.8 */ +@deprecated("Proxying is deprecated due to lack of use and compiler-level support.", "2.11.3") trait IterableProxy[+A] extends Iterable[A] with IterableProxyLike[A, Iterable[A]] diff --git a/src/library/scala/collection/MapProxy.scala b/src/library/scala/collection/MapProxy.scala index 941c1f5a4a..26a7c710ee 100644 --- a/src/library/scala/collection/MapProxy.scala +++ b/src/library/scala/collection/MapProxy.scala @@ -17,4 +17,5 @@ package collection * @version 1.0, 21/07/2003 * @since 1 */ +@deprecated("Proxying is deprecated due to lack of use and compiler-level support.", "2.11.3") trait MapProxy[A, +B] extends Map[A, B] with MapProxyLike[A, B, Map[A, B]] diff --git a/src/library/scala/collection/SetProxy.scala b/src/library/scala/collection/SetProxy.scala index f9f38f148a..e17fb215b9 100644 --- a/src/library/scala/collection/SetProxy.scala +++ b/src/library/scala/collection/SetProxy.scala @@ -17,4 +17,5 @@ package collection * @author Martin Odersky * @version 2.0, 01/01/2007 */ +@deprecated("Proxying is deprecated due to lack of use and compiler-level support.", "2.11.3") trait SetProxy[A] extends Set[A] with SetProxyLike[A, Set[A]] diff --git a/src/library/scala/collection/TraversableProxy.scala b/src/library/scala/collection/TraversableProxy.scala index 65936da0e4..9eec685d10 100644 --- a/src/library/scala/collection/TraversableProxy.scala +++ b/src/library/scala/collection/TraversableProxy.scala @@ -21,4 +21,5 @@ package collection * @version 2.8 * @since 2.8 */ +@deprecated("Proxying is deprecated due to lack of use and compiler-level support.", "2.11.3") trait TraversableProxy[+A] extends Traversable[A] with TraversableProxyLike[A, Traversable[A]] diff --git a/src/library/scala/util/matching/Regex.scala b/src/library/scala/util/matching/Regex.scala index 2e1bfb02e4..5c4e706dc1 100644 --- a/src/library/scala/util/matching/Regex.scala +++ b/src/library/scala/util/matching/Regex.scala @@ -6,7 +6,6 @@ ** |/ ** \* */ - /** * This package is concerned with regular expression (regex) matching against strings, * with the main goal of pulling out information from those matches, or replacing @@ -28,117 +27,127 @@ * into a [[java.lang.String]]. * */ -package scala -package util.matching +package scala.util.matching import scala.collection.AbstractIterator import java.util.regex.{ Pattern, Matcher } -/** This class provides methods for creating and using regular expressions. - * It is based on the regular expressions of the JDK since 1.4. +/** A regular expression is used to determine whether a string matches a pattern + * and, if it does, to extract or transform the parts that match. * - * Its main goal is to extract strings that match a pattern, or the subgroups - * that make it up. For that reason, it is usually used with for comprehensions - * and matching (see methods for examples). + * This class delegates to the [[java.util.regex]] package of the Java Platform. + * See the documentation for [[java.util.regex.Pattern]] for details about + * the regular expression syntax for pattern strings. * - * A Regex is created from a [[java.lang.String]] representation of the - * regular expression pattern^1^. That pattern is compiled - * during construction, so frequently used patterns should be declared outside - * loops if performance is of concern. Possibly, they might be declared on a - * companion object, so that they need only to be initialized once. + * An instance of `Regex` represents a compiled regular expression pattern. + * Since compilation is expensive, frequently used `Regex`es should be constructed + * once, outside of loops and perhaps in a companion object. * - * The canonical way of creating regex patterns is by using the method `r`, provided - * on [[java.lang.String]] through an implicit conversion into - * [[scala.collection.immutable.WrappedString]]. Using triple quotes to write these - * strings avoids having to quote the backslash character (`\`). + * The canonical way to create a `Regex` is by using the method `r`, provided + * implicitly for strings: * - * Using the constructor directly, on the other hand, makes - * it possible to declare names for subgroups in the pattern. + * {{{ + * val date = """(\d\d\d\d)-(\d\d)-(\d\d)""".r + * }}} * - * For example, both declarations below generate the same regex, but the second - * one associate names with the subgroups. + * Since escapes are not processed in multi-line string literals, using triple quotes + * avoids having to escape the backslash character, so that `"\\d"` can be written `"""\d"""`. + * + * To extract the capturing groups when a `Regex` is matched, use it as + * an extractor in a pattern match: * * {{{ - * val dateP1 = """(\d\d\d\d)-(\d\d)-(\d\d)""".r - * val dateP2 = new scala.util.matching.Regex("""(\d\d\d\d)-(\d\d)-(\d\d)""", "year", "month", "day") + * "2004-01-20" match { + * case date(year, month, day) => s"$year was a good year for PLs." + * } * }}} * - * There are two ways of using a `Regex` to find a pattern: calling methods on - * Regex, such as `findFirstIn` or `findAllIn`, or using it as an extractor in a - * pattern match. + * To check only whether the `Regex` matches, ignoring any groups, + * use a sequence wildcard: + * + * {{{ + * "2004-01-20" match { + * case date(_*) => "It's a date!" + * } + * }}} * - * Note that, when calling `findAllIn`, the resulting [[scala.util.matching.Regex.MatchIterator]] - * needs to be initialized (by calling `hasNext` or `next()`, or causing these to be - * called) before information about a match can be retrieved: + * That works because a `Regex` extractor produces a sequence of strings. + * Extracting only the year from a date could also be expressed with + * a sequence wildcard: * * {{{ - * val msg = "I love Scala" + * "2004-01-20" match { + * case date(year, _*) => s"$year was a good year for PLs." + * } + * }}} * - * // val start = " ".r.findAllIn(msg).start // throws an IllegalStateException + * In a pattern match, `Regex` normally matches the entire input. + * However, an unanchored `Regex` finds the pattern anywhere + * in the input. * - * val matches = " ".r.findAllIn(msg) - * matches.hasNext // initializes the matcher - * val start = matches.start + * {{{ + * val embeddedDate = date.unanchored + * "Date: 2004-01-20 17:25:18 GMT (10 years, 28 weeks, 5 days, 17 hours and 51 minutes ago)" match { + * case embeddedDate("2004", "01", "20") => "A Scala is born." + * } * }}} * - * When Regex is used as an extractor in a pattern match, note that it - * only succeeds if the whole text can be matched. For this reason, one usually - * calls a method to find the matching substrings, and then use it as an extractor - * to break match into subgroups. + * To find or replace matches of the pattern, use the various find and replace methods. + * There is a flavor of each method that produces matched strings and + * another that produces `Match` objects. * - * As an example, the above patterns can be used like this: + * For example, pattern matching with an unanchored `Regex`, as in the previous example, + * is the same as using `findFirstMatchIn`, except that the findFirst methods return an `Option`, + * or `None` for no match: * * {{{ - * val dateP1(year, month, day) = "2011-07-15" + * val dates = "Important dates in history: 2004-01-20, 1958-09-05, 2010-10-06, 2011-07-15" + * val firstDate = date findFirstIn dates getOrElse "No date found." + * val firstYear = for (m <- date findFirstMatchIn dates) yield m group 1 + * }}} * - * // val dateP1(year, month, day) = "Date 2011-07-15" // throws an exception at runtime + * To find all matches: * - * val copyright: String = dateP1 findFirstIn "Date of this document: 2011-07-15" match { - * case Some(dateP1(year, month, day)) => "Copyright "+year - * case None => "No copyright" - * } + * {{{ + * val allYears = for (m <- date findAllMatchIn dates) yield m group 1 + * }}} * - * val copyright: Option[String] = for { - * dateP1(year, month, day) <- dateP1 findFirstIn "Last modified 2011-07-15" - * } yield year - - * def getYears(text: String): Iterator[String] = for (dateP1(year, _, _) <- dateP1 findAllIn text) yield year - * def getFirstDay(text: String): Option[String] = for (m <- dateP2 findFirstMatchIn text) yield m group "day" + * But `findAllIn` returns a special iterator of strings that can be queried for the `MatchData` + * of the last match: + * + * {{{ + * val mi = date findAllIn dates + * val oldies = mi filter (_ => (mi group 1).toInt < 1960) map (s => s"$s: An oldie but goodie.") * }}} * - * Regex does not provide a method that returns a [[scala.Boolean]]. One can - * use [[java.lang.String]] `matches` method, or, if `Regex` is preferred, - * either ignore the return value or test the `Option` for emptyness. For example: + * Note that `findAllIn` finds matches that don't overlap. (See [[findAllIn]] for more examples.) * * {{{ - * def hasDate(text: String): Boolean = (dateP1 findFirstIn text).nonEmpty - * def printLinesWithDates(lines: Traversable[String]) { - * lines foreach { line => - * dateP1 findFirstIn line foreach { _ => println(line) } - * } - * } + * val num = """(\d+)""".r + * val all = (num findAllIn "123").toList // List("123"), not List("123", "23", "3") * }}} * - * There are also methods that can be used to replace the patterns - * on a text. The substitutions can be simple replacements, or more - * complex functions. For example: + * Text replacement can be performed unconditionally or as a function of the current match: * * {{{ - * val months = Map( 1 -> "Jan", 2 -> "Feb", 3 -> "Mar", - * 4 -> "Apr", 5 -> "May", 6 -> "Jun", - * 7 -> "Jul", 8 -> "Aug", 9 -> "Sep", - * 10 -> "Oct", 11 -> "Nov", 12 -> "Dec") - * - * import scala.util.matching.Regex.Match - * def reformatDate(text: String) = dateP2 replaceAllIn ( text, (m: Match) => - * "%s %s, %s" format (months(m group "month" toInt), m group "day", m group "year") - * ) + * val redacted = date replaceAllIn (dates, "XXXX-XX-XX") + * val yearsOnly = date replaceAllIn (dates, m => m group 1) + * val months = (0 to 11) map { i => val c = Calendar.getInstance; c.set(2014, i, 1); f"$c%tb" } + * val reformatted = date replaceAllIn (dates, _ match { case date(y,m,d) => f"${months(m.toInt - 1)} $d, $y" }) * }}} * - * You can use special pattern syntax constructs like `(?idmsux-idmsux)`¹ to switch - * various regex compilation options like `CASE_INSENSITIVE` or `UNICODE_CASE`. + * Pattern matching the `Match` against the `Regex` that created it does not reapply the `Regex`. + * In the expression for `reformatted`, each `date` match is computed once. But it is possible to apply a + * `Regex` to a `Match` resulting from a different pattern: + * + * {{{ + * val docSpree = """2011(?:-\d{2}){2}""".r + * val docView = date replaceAllIn (dates, _ match { + * case docSpree() => "Historic doc spree!" + * case _ => "Something else happened" + * }) + * }}} * - * @note ¹ A detailed description is available in [[java.util.regex.Pattern]]. * @see [[java.util.regex.Pattern]] * * @author Thibaud Hottelier @@ -154,9 +163,8 @@ import java.util.regex.{ Pattern, Matcher } * interpreted as a reference to a group in the matched pattern, with numbers * 1 through 9 corresponding to the first nine groups, and 0 standing for the * whole match. Any other character is an error. The backslash (`\`) character - * will be interpreted as an escape character, and can be used to escape the - * dollar sign. One can use [[scala.util.matching.Regex]]'s `quoteReplacement` - * to automatically escape these characters. + * will be interpreted as an escape character and can be used to escape the + * dollar sign. Use `Regex.quoteReplacement` to escape these characters. */ @SerialVersionUID(-2094783597747625537L) class Regex private[matching](val pattern: Pattern, groupNames: String*) extends Serializable { @@ -164,51 +172,84 @@ class Regex private[matching](val pattern: Pattern, groupNames: String*) extends import Regex._ - /** - * @param regex A string representing a regular expression - * @param groupNames A mapping from names to indices in capture groups - */ + /** Compile a regular expression, supplied as a string, into a pattern that + * can be matched against inputs. + * + * If group names are supplied, they can be used this way: + * + * {{{ + * val namedDate = new Regex("""(\d\d\d\d)-(\d\d)-(\d\d)""", "year", "month", "day") + * val namedYears = for (m <- namedDate findAllMatchIn dates) yield m group "year" + * }}} + * + * This constructor does not support options as flags, which must be + * supplied as inline flags in the pattern string: `(?idmsux-idmsux)`. + * + * @param regex The regular expression to compile. + * @param groupNames Names of capturing groups. + */ def this(regex: String, groupNames: String*) = this(Pattern.compile(regex), groupNames: _*) /** Tries to match a [[java.lang.CharSequence]]. + * * If the match succeeds, the result is a list of the matching * groups (or a `null` element if a group did not match any input). * If the pattern specifies no groups, then the result will be an empty list * on a successful match. * * This method attempts to match the entire input by default; to find the next - * matching subsequence, use an unanchored Regex. - + * matching subsequence, use an unanchored `Regex`. + * * For example: * * {{{ * val p1 = "ab*c".r * val p1Matches = "abbbc" match { - * case p1() => true + * case p1() => true // no groups * case _ => false * } * val p2 = "a(b*)c".r + * val p2Matches = "abbbc" match { + * case p2(_*) => true // any groups + * case _ => false + * } * val numberOfB = "abbbc" match { - * case p2(b) => Some(b.length) + * case p2(b) => Some(b.length) // one group * case _ => None * } * val p3 = "b*".r.unanchored * val p3Matches = "abbbc" match { - * case p3() => true + * case p3() => true // find the b's * case _ => false * } + * val p4 = "a(b*)(c+)".r + * val p4Matches = "abbbcc" match { + * case p4(_*) => true // multiple groups + * case _ => false + * } + * val allGroups = "abbbcc" match { + * case p4(all @ _*) => all mkString "/" // "bbb/cc" + * case _ => "" + * } + * val cGroup = "abbbcc" match { + * case p4(_, c) => c + * case _ => "" + * } * }}} * * @param s The string to match * @return The matches */ - def unapplySeq(s: CharSequence): Option[List[String]] = { - val m = pattern matcher s - if (runMatcher(m)) Some((1 to m.groupCount).toList map m.group) - else None + def unapplySeq(s: CharSequence): Option[List[String]] = s match { + case null => None + case _ => + val m = pattern matcher s + if (runMatcher(m)) Some((1 to m.groupCount).toList map m.group) + else None } /** Tries to match the String representation of a [[scala.Char]]. + * * If the match succeeds, the result is the first matching * group if any groups are defined, or an empty Sequence otherwise. * @@ -247,13 +288,16 @@ class Regex private[matching](val pattern: Pattern, groupNames: String*) extends } /** Tries to match on a [[scala.util.matching.Regex.Match]]. + * * A previously failed match results in None. + * * If a successful match was made against the current pattern, then that result is used. + * * Otherwise, this Regex is applied to the previously matched input, * and the result of that match is used. */ def unapplySeq(m: Match): Option[List[String]] = - if (m.matched == null) None + if (m == null || m.matched == null) None else if (m.matcher.pattern == this.pattern) Some((1 to m.groupCount).toList map m.group) else unapplySeq(m.matched) @@ -274,30 +318,47 @@ class Regex private[matching](val pattern: Pattern, groupNames: String*) extends // @see UnanchoredRegex protected def runMatcher(m: Matcher) = m.matches() - /** Return all non-overlapping matches of this regexp in given character + /** Return all non-overlapping matches of this `Regex` in the given character * sequence as a [[scala.util.matching.Regex.MatchIterator]], * which is a special [[scala.collection.Iterator]] that returns the - * matched strings, but can also be converted into a normal iterator - * that returns objects of type [[scala.util.matching.Regex.Match]] - * that can be queried for data such as the text that precedes the - * match, subgroups, etc. + * matched strings but can also be queried for more data about the last match, + * such as capturing groups and start position. + * + * A `MatchIterator` can also be converted into an iterator + * that returns objects of type [[scala.util.matching.Regex.Match]], + * such as is normally returned by `findAllMatchIn`. * * Where potential matches overlap, the first possible match is returned, - * followed by the next match that is completely after the first. For - * instance, `"hat[^a]+".r` will match `hath` and `hattth` in the string - * `"hathatthattthatttt"`. + * followed by the next match that follows the input consumed by the + * first match: + * + * {{{ + * val hat = "hat[^a]+".r + * val hathaway = "hathatthattthatttt" + * val hats = (hat findAllIn hathaway).toList // List(hath, hattth) + * val pos = (hat findAllMatchIn hathaway map (_.start)).toList // List(0, 7) + * }}} + * + * To return overlapping matches, it is possible to formulate a regular expression + * with lookahead (`?=`) that does not consume the overlapping region. + * + * {{{ + * val madhatter = "(h)(?=(at[^a]+))".r + * val madhats = (madhatter findAllMatchIn hathaway map { + * case madhatter(x,y) => s"$x$y" + * }).toList // List(hath, hatth, hattth, hatttt) + * }}} * - * Attempting to retrieve information about a match before initializing - * the iterator can result in [[java.lang.IllegalStateException]]s. See - * [[scala.util.matching.Regex.MatchIterator]] for details. + * Attempting to retrieve match information before performing the first match + * or after exhausting the iterator results in [[java.lang.IllegalStateException]]. + * See [[scala.util.matching.Regex.MatchIterator]] for details. * * @param source The text to match against. - * @return A [[scala.util.matching.Regex.MatchIterator]] of all matches. + * @return A [[scala.util.matching.Regex.MatchIterator]] of matched substrings. * @example {{{for (words <- """\w+""".r findAllIn "A simple example.") yield words}}} */ def findAllIn(source: CharSequence) = new Regex.MatchIterator(source, this, groupNames) - /** Return all non-overlapping matches of this regexp in given character sequence as a * [[scala.collection.Iterator]] of [[scala.util.matching.Regex.Match]]. * @@ -316,8 +377,8 @@ class Regex private[matching](val pattern: Pattern, groupNames: String*) extends } } - /** Return optionally first matching string of this regexp in given character sequence, - * or None if it does not exist. + /** Return an optional first matching string of this `Regex` in the given character sequence, + * or None if there is no match. * * @param source The text to match against. * @return An [[scala.Option]] of the first matching string in the text. @@ -328,13 +389,11 @@ class Regex private[matching](val pattern: Pattern, groupNames: String*) extends if (m.find) Some(m.group) else None } - /** Return optionally first match of this regexp in given character sequence, + /** Return an optional first match of this `Regex` in the given character sequence, * or None if it does not exist. * - * The main difference between this method and `findFirstIn` is that the (optional) return - * type for this is [[scala.util.matching.Regex.Match]], through which more - * data can be obtained about the match, such as the strings that precede and follow it, - * or subgroups. + * If the match is successful, the [[scala.util.matching.Regex.Match]] can be queried for + * more data. * * @param source The text to match against. * @return A [[scala.Option]] of [[scala.util.matching.Regex.Match]] of the first matching string in the text. @@ -345,30 +404,28 @@ class Regex private[matching](val pattern: Pattern, groupNames: String*) extends if (m.find) Some(new Match(source, m, groupNames)) else None } - /** Return optionally match of this regexp at the beginning of the - * given character sequence, or None if regexp matches no prefix + /** Return an optional match of this `Regex` at the beginning of the + * given character sequence, or None if it matches no prefix * of the character sequence. * - * The main difference from this method to `findFirstIn` is that this - * method will not return any matches that do not begin at the start - * of the text being matched against. + * Unlike `findFirstIn`, this method will only return a match at + * the beginning of the input. * * @param source The text to match against. * @return A [[scala.Option]] of the matched prefix. - * @example {{{"""[a-z]""".r findPrefixOf "A simple example." // returns None, since the text does not begin with a lowercase letter}}} + * @example {{{"""\p{Lower}""".r findPrefixOf "A simple example." // returns None, since the text does not begin with a lowercase letter}}} */ def findPrefixOf(source: CharSequence): Option[String] = { val m = pattern.matcher(source) if (m.lookingAt) Some(m.group) else None } - /** Return optionally match of this regexp at the beginning of the - * given character sequence, or None if regexp matches no prefix + /** Return an optional match of this `Regex` at the beginning of the + * given character sequence, or None if it matches no prefix * of the character sequence. * - * The main difference from this method to `findFirstMatchIn` is that - * this method will not return any matches that do not begin at the - * start of the text being matched against. + * Unlike `findFirstMatchIn`, this method will only return a match at + * the beginning of the input. * * @param source The text to match against. * @return A [[scala.Option]] of the [[scala.util.matching.Regex.Match]] of the matched string. @@ -402,7 +459,7 @@ class Regex private[matching](val pattern: Pattern, groupNames: String*) extends * import scala.util.matching.Regex * val datePattern = new Regex("""(\d\d\d\d)-(\d\d)-(\d\d)""", "year", "month", "day") * val text = "From 2011-07-15 to 2011-07-17" - * val repl = datePattern replaceAllIn (text, m => m.group("month")+"/"+m.group("day")) + * val repl = datePattern replaceAllIn (text, m => s"${m group "month"}/${m group "day"}") * }}} * * $replacementString @@ -425,10 +482,10 @@ class Regex private[matching](val pattern: Pattern, groupNames: String*) extends * {{{ * import scala.util.matching.Regex._ * - * val map = Map("x" -> "a var", "y" -> """some $ and \ signs""") + * val vars = Map("x" -> "a var", "y" -> """some $ and \ signs""") * val text = "A text with variables %x, %y and %z." * val varPattern = """%(\w+)""".r - * val mapper = (m: Match) => map get (m group 1) map (quoteReplacement(_)) + * val mapper = (m: Match) => vars get (m group 1) map (quoteReplacement(_)) * val repl = varPattern replaceSomeIn (text, mapper) * }}} * @@ -469,17 +526,25 @@ class Regex private[matching](val pattern: Pattern, groupNames: String*) extends pattern.split(toSplit) /** Create a new Regex with the same pattern, but no requirement that - * the entire String matches in extractor patterns. For instance, the strings - * shown below lead to successful matches, where they would not otherwise. + * the entire String matches in extractor patterns. + * + * Normally, matching on `date` behaves as though the pattern were + * enclosed in anchors, `"^pattern$"`. + * + * The unanchored `Regex` behaves as though those anchors were removed. + * + * Note that this method does not actually strip any matchers from the pattern. + * + * Calling `anchored` returns the original `Regex`. * * {{{ - * val dateP1 = """(\d\d\d\d)-(\d\d)-(\d\d)""".r.unanchored + * val date = """(\d\d\d\d)-(\d\d)-(\d\d)""".r.unanchored * - * val dateP1(year, month, day) = "Date 2011-07-15" + * val date(year, month, day) = "Date 2011-07-15" // OK * * val copyright: String = "Date of this document: 2011-07-15" match { - * case dateP1(year, month, day) => "Copyright "+year - * case _ => "No copyright" + * case date(year, month, day) => s"Copyright $year" // OK + * case _ => "No copyright" * } * }}} * @@ -494,6 +559,10 @@ class Regex private[matching](val pattern: Pattern, groupNames: String*) extends override def toString = regex } +/** A [[Regex]] that finds the first match when used in a pattern match. + * + * @see [[Regex#unanchored]] + */ trait UnanchoredRegex extends Regex { override protected def runMatcher(m: Matcher) = m.find() override def unanchored = this @@ -509,70 +578,79 @@ object Regex { */ trait MatchData { - /** The source from where the match originated */ + /** The source from which the match originated */ val source: CharSequence - /** The names of the groups, or some empty sequence if one defined */ + /** The names of the groups, or an empty sequence if none defined */ val groupNames: Seq[String] - /** The number of subgroups in the pattern (not all of these need to match!) */ + /** The number of capturing groups in the pattern. + * (For a given successful match, some of those groups may not have matched any input.) + */ def groupCount: Int /** The index of the first matched character, or -1 if nothing was matched */ def start: Int /** The index of the first matched character in group `i`, - * or -1 if nothing was matched for that group */ + * or -1 if nothing was matched for that group. + */ def start(i: Int): Int - /** The index of the last matched character, or -1 if nothing was matched */ + /** The index following the last matched character, or -1 if nothing was matched. */ def end: Int /** The index following the last matched character in group `i`, - * or -1 if nothing was matched for that group */ + * or -1 if nothing was matched for that group. + */ def end(i: Int): Int - /** The matched string, or `null` if nothing was matched */ + /** The matched string, or `null` if nothing was matched. */ def matched: String = if (start >= 0) source.subSequence(start, end).toString else null /** The matched string in group `i`, - * or `null` if nothing was matched */ + * or `null` if nothing was matched. + */ def group(i: Int): String = if (start(i) >= 0) source.subSequence(start(i), end(i)).toString else null - /** All matched subgroups, i.e., not including group(0) */ + /** All capturing groups, i.e., not including group(0). */ def subgroups: List[String] = (1 to groupCount).toList map group /** The char sequence before first character of match, - * or `null` if nothing was matched */ + * or `null` if nothing was matched. + */ def before: CharSequence = if (start >= 0) source.subSequence(0, start) else null /** The char sequence before first character of match in group `i`, - * or `null` if nothing was matched for that group */ + * or `null` if nothing was matched for that group. + */ def before(i: Int): CharSequence = if (start(i) >= 0) source.subSequence(0, start(i)) else null /** Returns char sequence after last character of match, - * or `null` if nothing was matched */ + * or `null` if nothing was matched. + */ def after: CharSequence = if (end >= 0) source.subSequence(end, source.length) else null /** The char sequence after last character of match in group `i`, - * or `null` if nothing was matched for that group */ + * or `null` if nothing was matched for that group. + */ def after(i: Int): CharSequence = if (end(i) >= 0) source.subSequence(end(i), source.length) else null private lazy val nameToIndex: Map[String, Int] = Map[String, Int]() ++ ("" :: groupNames.toList).zipWithIndex - /** Returns the group with given name + /** Returns the group with given name. * * @param id The group name * @return The requested group @@ -583,24 +661,22 @@ object Regex { case Some(index) => group(index) } - /** The matched string; equivalent to `matched.toString` */ + /** The matched string; equivalent to `matched.toString`. */ override def toString = matched - } - /** Provides information about a succesful match. - */ + /** Provides information about a successful match. */ class Match(val source: CharSequence, private[matching] val matcher: Matcher, val groupNames: Seq[String]) extends MatchData { - /** The index of the first matched character */ + /** The index of the first matched character. */ val start = matcher.start - /** The index following the last matched character */ + /** The index following the last matched character. */ val end = matcher.end - /** The number of subgroups */ + /** The number of subgroups. */ def groupCount = matcher.groupCount private lazy val starts: Array[Int] = @@ -608,19 +684,19 @@ object Regex { private lazy val ends: Array[Int] = ((0 to groupCount) map matcher.end).toArray - /** The index of the first matched character in group `i` */ + /** The index of the first matched character in group `i`. */ def start(i: Int) = starts(i) - /** The index following the last matched character in group `i` */ + /** The index following the last matched character in group `i`. */ def end(i: Int) = ends(i) /** The match itself with matcher-dependent lazy vals forced, - * so that match is valid even once matcher is advanced + * so that match is valid even once matcher is advanced. */ def force: this.type = { starts; ends; this } } - /** An extractor object for Matches, yielding the matched string + /** An extractor object for Matches, yielding the matched string. * * This can be used to help writing replacer functions when you * are not interested in match data. For example: @@ -635,15 +711,15 @@ object Regex { def unapply(m: Match): Some[String] = Some(m.matched) } - /** An extractor object that yields the groups in the match. Using an extractor - * rather than the original regex avoids recomputing the match. + /** An extractor object that yields the groups in the match. Using this extractor + * rather than the original `Regex` ensures that the match is not recomputed. * * {{{ * import scala.util.matching.Regex.Groups * - * val datePattern = """(\d\d\d\d)-(\d\d)-(\d\d)""".r + * val date = """(\d\d\d\d)-(\d\d)-(\d\d)""".r * val text = "The doc spree happened on 2011-07-15." - * val day = datePattern replaceAllIn(text, _ match { case Groups(year, month, day) => month+"/"+day }) + * val day = date replaceAllIn(text, _ match { case Groups(_, month, day) => s"$month/$day" }) * }}} */ object Groups { @@ -672,7 +748,7 @@ object Regex { nextSeen } - /** The next matched substring of `source` */ + /** The next matched substring of `source`. */ def next(): String = { if (!hasNext) throw new NoSuchElementException nextSeen = false @@ -681,28 +757,28 @@ object Regex { override def toString = super[AbstractIterator].toString - /** The index of the first matched character */ + /** The index of the first matched character. */ def start: Int = matcher.start - /** The index of the first matched character in group `i` */ + /** The index of the first matched character in group `i`. */ def start(i: Int): Int = matcher.start(i) - /** The index of the last matched character */ + /** The index of the last matched character. */ def end: Int = matcher.end - /** The index following the last matched character in group `i` */ + /** The index following the last matched character in group `i`. */ def end(i: Int): Int = matcher.end(i) - /** The number of subgroups */ + /** The number of subgroups. */ def groupCount = matcher.groupCount - /** Convert to an iterator that yields MatchData elements instead of Strings */ + /** Convert to an iterator that yields MatchData elements instead of Strings. */ def matchData: Iterator[Match] = new AbstractIterator[Match] { def hasNext = self.hasNext def next = { self.next(); new Match(source, matcher, groupNames).force } } - /** Convert to an iterator that yields MatchData elements instead of Strings and has replacement support */ + /** Convert to an iterator that yields MatchData elements instead of Strings and has replacement support. */ private[matching] def replacementData = new AbstractIterator[Match] with Replacement { def matcher = self.matcher def hasNext = self.hasNext diff --git a/src/reflect/scala/reflect/api/Quasiquotes.scala b/src/reflect/scala/reflect/api/Quasiquotes.scala index e905aa4153..eaae05bed5 100644 --- a/src/reflect/scala/reflect/api/Quasiquotes.scala +++ b/src/reflect/scala/reflect/api/Quasiquotes.scala @@ -13,7 +13,7 @@ trait Quasiquotes { self: Universe => protected trait api { // implementation is hardwired to `dispatch` method of `scala.tools.reflect.quasiquotes.Quasiquotes` // using the mechanism implemented in `scala.tools.reflect.FastTrack` - def apply[T](args: T*): Tree = macro ??? + def apply[A >: Any](args: A*): Tree = macro ??? def unapply(scrutinee: Any): Any = macro ??? } object q extends api diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index bf560a21e5..02578e2038 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -836,12 +836,18 @@ trait Definitions extends api.StandardDefinitions { def typeOfMemberNamedHead(tp: Type) = typeArgOfBaseTypeOr(tp, SeqClass)(resultOfMatchingMethod(tp, nme.head)()) def typeOfMemberNamedApply(tp: Type) = typeArgOfBaseTypeOr(tp, SeqClass)(resultOfMatchingMethod(tp, nme.apply)(IntTpe)) def typeOfMemberNamedDrop(tp: Type) = typeArgOfBaseTypeOr(tp, SeqClass)(resultOfMatchingMethod(tp, nme.drop)(IntTpe)) - def typesOfSelectors(tp: Type) = getterMemberTypes(tp, productSelectors(tp)) + def typesOfSelectors(tp: Type) = + if (isTupleType(tp)) tp.typeArgs + else getterMemberTypes(tp, productSelectors(tp)) + // SI-8128 Still using the type argument of the base type at Seq/Option if this is an old-style (2.10 compatible) // extractor to limit exposure to regressions like the reported problem with existentials. // TODO fix the existential problem in the general case, see test/pending/pos/t8128.scala private def typeArgOfBaseTypeOr(tp: Type, baseClass: Symbol)(or: => Type): Type = (tp baseType baseClass).typeArgs match { - case x :: Nil => x + case x :: Nil => + val x1 = x + val x2 = repackExistential(x1) + x2 case _ => or } diff --git a/src/reflect/scala/reflect/internal/Reporting.scala b/src/reflect/scala/reflect/internal/Reporting.scala index 423127803e..f2de83bc5d 100644 --- a/src/reflect/scala/reflect/internal/Reporting.scala +++ b/src/reflect/scala/reflect/internal/Reporting.scala @@ -89,6 +89,9 @@ abstract class Reporter { def count(severity: Severity): Int def resetCount(severity: Severity): Unit + def errorCount: Int = count(ERROR) + def warningCount: Int = count(WARNING) + def hasErrors: Boolean = count(ERROR) > 0 def hasWarnings: Boolean = count(WARNING) > 0 diff --git a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala index b808666360..8d4c3f752f 100644 --- a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala +++ b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala @@ -254,12 +254,13 @@ abstract class UnPickler { // (4) Call the mirror's "missing" hook. adjust(mirrorThatLoaded(owner).missingHook(owner, name)) orElse { // (5) Create a stub symbol to defer hard failure a little longer. - val fullName = s"${owner.fullName}.$name" + val advice = moduleAdvice(s"${owner.fullName}.$name") val missingMessage = - s"""|bad symbolic reference to $fullName encountered in class file '$filename'. - |Cannot access ${name.longString} in ${owner.kindString} ${owner.fullName}. The current classpath may be - |missing a definition for $fullName, or $filename may have been compiled against a version that's - |incompatible with the one found on the current classpath.${moduleAdvice(fullName)}""".stripMargin + s"""|missing or invalid dependency detected while loading class file '$filename'. + |Could not access ${name.longString} in ${owner.kindString} ${owner.fullName}, + |because it (or its dependencies) are missing. Check your build definition for + |missing or conflicting dependencies. (Re-run with `-Ylog-classpath` to see the problematic classpath.) + |A full rebuild may help if '$filename' was compiled against an incompatible version of ${owner.fullName}.$advice""".stripMargin owner.newStubSymbol(name, missingMessage) } } diff --git a/src/repl/scala/tools/nsc/interpreter/ILoop.scala b/src/repl/scala/tools/nsc/interpreter/ILoop.scala index ce0eadc04f..50c89f7442 100644 --- a/src/repl/scala/tools/nsc/interpreter/ILoop.scala +++ b/src/repl/scala/tools/nsc/interpreter/ILoop.scala @@ -430,7 +430,14 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) import scala.concurrent.duration._ Await.ready(globalFuture, 60.seconds) - (line ne null) && (command(line) match { + if (line eq null) { + // SI-4563: this means the console was properly interrupted (Ctrl+D usually) + // so we display the output message (which by default ends with + // a newline so as not to break the user's terminal) + if (in.interactive) out.print(Properties.shellInterruptedString) + + false + } else (command(line) match { case Result(false, _) => false case Result(_, Some(line)) => addReplay(line) ; true case _ => true diff --git a/src/repl/scala/tools/nsc/interpreter/IMain.scala b/src/repl/scala/tools/nsc/interpreter/IMain.scala index 8ea8759ee5..6e30b73e0e 100644 --- a/src/repl/scala/tools/nsc/interpreter/IMain.scala +++ b/src/repl/scala/tools/nsc/interpreter/IMain.scala @@ -1121,7 +1121,7 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set def apply(line: String): Result = debugging(s"""parse("$line")""") { var isIncomplete = false - currentRun.reporting.withIncompleteHandler((_, _) => isIncomplete = true) { + currentRun.parsing.withIncompleteHandler((_, _) => isIncomplete = true) { reporter.reset() val trees = newUnitParser(line).parseStats() if (reporter.hasErrors) Error diff --git a/src/scaladoc/scala/tools/nsc/doc/ScaladocAnalyzer.scala b/src/scaladoc/scala/tools/nsc/doc/ScaladocAnalyzer.scala index ccf18b76de..cbf8ff22ba 100644 --- a/src/scaladoc/scala/tools/nsc/doc/ScaladocAnalyzer.scala +++ b/src/scaladoc/scala/tools/nsc/doc/ScaladocAnalyzer.scala @@ -208,7 +208,7 @@ abstract class ScaladocSyntaxAnalyzer[G <: Global](val global: G) extends Syntax super.skipDocComment() } override def skipBlockComment(): Unit = { - inDocComment = false + inDocComment = false // ??? this means docBuffer won't receive contents of this comment??? docBuffer = new StringBuilder("/*") super.skipBlockComment() } @@ -217,9 +217,10 @@ abstract class ScaladocSyntaxAnalyzer[G <: Global](val global: G) extends Syntax def foundStarComment(start: Int, end: Int) = try { val str = docBuffer.toString val pos = Position.range(unit.source, start, start, end) - unit.comment(pos, str) - if (inDocComment) + if (inDocComment) { + signalParsedDocComment(str, pos) lastDoc = DocComment(str, pos) + } true } finally { docBuffer = null |