diff --git a/CHANGELOG.md b/CHANGELOG.md index dfa09bc..1d26db4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,17 +3,19 @@ All notable changes to this project will be documented in this file. ## [0.2] ### Added +- Syntax highlight. - JShell-like Kotlin snippets. - Completely new REPL architecture with object based class layout `kshell-repl-api`. - HDFS browser plugin. - Kotlin interpreter for Zeppelin. ### Changed +- Switch to JLine3 - Kotlin version 1.2.20 -> 1.2.30. - Unit testes moved to `kshell-repl-api`. ### Fixed -- Custom REPL fixes [#7](http://github.com/khud/sparklin/issues/7) +- Custom REPL fixes [#7](http://github.com/khud/sparklin/issues/7). - Problem with custom commands which start with the same letter. ### Removed @@ -30,8 +32,8 @@ All notable changes to this project will be documented in this file. - Added 2 methods to `ConsoleReader` to support history changes. ### Fixed -- Problem with multiline code [#9](http://github.com/khud/sparklin/issues/9) -- Doubled colon problem [#10](http://github.com/khud/sparklin/issues/10) +- Problem with multiline code [#9](http://github.com/khud/sparklin/issues/9). +- Doubled colon problem [#10](http://github.com/khud/sparklin/issues/10). ## [0.1.1] - 2018-02-08 ### Added diff --git a/bin/kshell.sh b/bin/kshell.sh index 7db95b4..a6a8ed3 100755 --- a/bin/kshell.sh +++ b/bin/kshell.sh @@ -12,12 +12,12 @@ KOTLIN_REFLECT=${MAVEN_REPO_BASE}/kotlin-reflect/${KOTLIN_VERSION}/kotlin-reflec # JLine2 console support FUSE_JANSI=$HOME/.m2/repository/org/fusesource/jansi/jansi/1.15/jansi-1.15.jar -KSHELL_CONSOLE_JLINE2=$HOME/.m2/repository/sparklin/kshell-console-jline2/${SPARKLIN_VERSION}/kshell-console-jline2-${SPARKLIN_VERSION}.jar +KSHELL_CONSOLE_JLINE3=$HOME/.m2/repository/sparklin/jline3-shaded/${SPARKLIN_VERSION}/jline3-shaded-${SPARKLIN_VERSION}.jar KSHELL_REPL_API=$HOME/.m2/repository/sparklin/kshell-repl-api/${SPARKLIN_VERSION}/kshell-repl-api-${SPARKLIN_VERSION}.jar # KShell path KSHELL=$HOME/.m2/repository/sparklin/kshell/${SPARKLIN_VERSION}/kshell-${SPARKLIN_VERSION}.jar -JARS=${KOTLIN_COMPILER}:${KOTLIN_STDLIB}:${KOTLIN_REFLECT}:${KOTLIN_SCRIPT_RUNTIME}:${KSHELL_CONSOLE_JLINE2}:${KSHELL_REPL_API} +JARS=${KOTLIN_COMPILER}:${KOTLIN_STDLIB}:${KOTLIN_REFLECT}:${KOTLIN_SCRIPT_RUNTIME}:${KSHELL_CONSOLE_JLINE3}:${KSHELL_REPL_API} java -classpath ${JARS}:${KSHELL} sparklin.kshell.KotlinShell $@ diff --git a/bin/sparklin-spark1x.sh b/bin/sparklin-spark1x.sh index 33f2527..422d234 100755 --- a/bin/sparklin-spark1x.sh +++ b/bin/sparklin-spark1x.sh @@ -12,7 +12,7 @@ KOTLIN_REFLECT=${MAVEN_REPO_BASE}/kotlin-reflect/${KOTLIN_VERSION}/kotlin-reflec # JLine2 console support FUSE_JANSI=$HOME/.m2/repository/org/fusesource/jansi/jansi/1.15/jansi-1.15.jar -KSHELL_CONSOLE_JLINE2=$HOME/.m2/repository/sparklin/kshell-console-jline2/${SPARKLIN_VERSION}/kshell-console-jline2-${SPARKLIN_VERSION}.jar +KSHELL_CONSOLE_JLINE3=$HOME/.m2/repository/sparklin/jline3-shaded/${SPARKLIN_VERSION}/jline3-shaded-${SPARKLIN_VERSION}.jar KSHELL_REPL_API=$HOME/.m2/repository/sparklin/kshell-repl-api/${SPARKLIN_VERSION}/kshell-repl-api-${SPARKLIN_VERSION}.jar # Spark 1.x support @@ -30,7 +30,7 @@ KSHELL=$HOME/.m2/repository/sparklin/kshell/${SPARKLIN_VERSION}/kshell-${SPARKLI # Apache Spark home directory must point to Spark version 1.x > 1.6 SPARK_HOME=$HOME/Programs/spark-1.6.1-bin-hadoop2.6 -JARS=${KOTLIN_COMPILER},${KOTLIN_STDLIB},${KOTLIN_REFLECT},${KOTLIN_SCRIPT_RUNTIME},${KSHELL_CONSOLE_JLINE2},${KSHELL_REPL_API},${SPARKLIN_PLUGIN},${HDFS_BROWSER_PLUGIN} +JARS=${KOTLIN_COMPILER},${KOTLIN_STDLIB},${KOTLIN_REFLECT},${KOTLIN_SCRIPT_RUNTIME},${KSHELL_CONSOLE_JLINE3},${KSHELL_REPL_API},${SPARKLIN_PLUGIN},${HDFS_BROWSER_PLUGIN} ${SPARK_HOME}/bin/spark-submit --jars=${JARS} --conf "spark.driver.extraJavaOptions=-Dconfig.path=${SPARKLIN_CONFIG}" \ --master "local[2]" --class "sparklin.kshell.KotlinShell" \ diff --git a/bin/sparklin-spark2x.sh b/bin/sparklin-spark2x.sh index ed1f9f0..599fa17 100755 --- a/bin/sparklin-spark2x.sh +++ b/bin/sparklin-spark2x.sh @@ -12,7 +12,7 @@ KOTLIN_REFLECT=${MAVEN_REPO_BASE}/kotlin-reflect/${KOTLIN_VERSION}/kotlin-reflec # JLine2 console support FUSE_JANSI=$HOME/.m2/repository/org/fusesource/jansi/jansi/1.15/jansi-1.15.jar -KSHELL_CONSOLE_JLINE2=$HOME/.m2/repository/sparklin/kshell-console-jline2/${SPARKLIN_VERSION}/kshell-console-jline2-${SPARKLIN_VERSION}.jar +KSHELL_CONSOLE_JLINE3=$HOME/.m2/repository/sparklin/jline3-shaded/${SPARKLIN_VERSION}/jline3-shaded-${SPARKLIN_VERSION}.jar KSHELL_REPL_API=$HOME/.m2/repository/sparklin/kshell-repl-api/${SPARKLIN_VERSION}/kshell-repl-api-${SPARKLIN_VERSION}.jar # Spark 2.x support @@ -30,7 +30,7 @@ KSHELL=$HOME/.m2/repository/sparklin/kshell/${SPARKLIN_VERSION}/kshell-${SPARKLI # Apache Spark home directory must point to Spark version 2.x SPARK_HOME=$HOME/Programs/spark-2.2.0-bin-hadoop2.7 -JARS=${KOTLIN_COMPILER},${KOTLIN_STDLIB},${KOTLIN_REFLECT},${KOTLIN_SCRIPT_RUNTIME},${KSHELL_CONSOLE_JLINE2},${KSHELL_REPL_API},${SPARKLIN_PLUGIN},${HDFS_BROWSER_PLUGIN} +JARS=${KOTLIN_COMPILER},${KOTLIN_STDLIB},${KOTLIN_REFLECT},${KOTLIN_SCRIPT_RUNTIME},${KSHELL_CONSOLE_JLINE3},${KSHELL_REPL_API},${SPARKLIN_PLUGIN},${HDFS_BROWSER_PLUGIN} ${SPARK_HOME}/bin/spark-submit --jars=${JARS} --conf "spark.driver.extraJavaOptions=-Dconfig.path=${SPARKLIN_CONFIG}" \ --master "local[2]" --class "sparklin.kshell.KotlinShell" \ diff --git a/conf/spark1x.properties b/conf/spark1x.properties index 3619f9b..b75340b 100644 --- a/conf/spark1x.properties +++ b/conf/spark1x.properties @@ -3,4 +3,6 @@ plugins=sparklin.kshell.plugins.LoadFilePlugin, \ sparklin.kshell.plugins.HelpPlugin, \ sparklin.kshell.plugins.PastePlugin, \ sparklin.spark1x.Spark1xPlugin, \ - sparklin.hdfsbrowser.HdfsBrowserPlugin \ No newline at end of file + sparklin.hdfsbrowser.HdfsBrowserPlugin, \ + sparklin.kshell.plugins.SyntaxPlugin, \ + sparklin.kshell.plugins.PromptPlugin \ No newline at end of file diff --git a/conf/spark2x.properties b/conf/spark2x.properties index d1ebd8a..caab20e 100644 --- a/conf/spark2x.properties +++ b/conf/spark2x.properties @@ -3,4 +3,6 @@ plugins=sparklin.kshell.plugins.LoadFilePlugin, \ sparklin.kshell.plugins.HelpPlugin, \ sparklin.kshell.plugins.PastePlugin, \ sparklin.spark2x.Spark2xPlugin, \ - sparklin.hdfsbrowser.HdfsBrowserPlugin \ No newline at end of file + sparklin.hdfsbrowser.HdfsBrowserPlugin, \ + sparklin.kshell.plugins.SyntaxPlugin, \ + sparklin.kshell.plugins.PromptPlugin \ No newline at end of file diff --git a/hdfs-browser/src/main/kotlin/sparklin/hdfsbrowser/HdfsBrowserPlugin.kt b/hdfs-browser/src/main/kotlin/sparklin/hdfsbrowser/HdfsBrowserPlugin.kt index 798da46..e14e476 100644 --- a/hdfs-browser/src/main/kotlin/sparklin/hdfsbrowser/HdfsBrowserPlugin.kt +++ b/hdfs-browser/src/main/kotlin/sparklin/hdfsbrowser/HdfsBrowserPlugin.kt @@ -1,11 +1,11 @@ package sparklin.hdfsbrowser -import sparklin.kshell.console.ConsoleReader import org.apache.hadoop.fs.FileSystem import org.apache.hadoop.fs.Path import sparklin.kshell.BaseCommand import sparklin.kshell.KShell import sparklin.kshell.Plugin +import sparklin.kshell.calcHumanReadableSize import sparklin.kshell.configuration.Configuration import sparklin.kshell.plugins.SparkPlugin import kotlin.reflect.KClass @@ -15,7 +15,6 @@ import org.apache.hadoop.conf.Configuration as HadoopConfiguration class HdfsBrowserPlugin : Plugin { private lateinit var repl: KShell private lateinit var fs: FileSystem - private lateinit var console: ConsoleReader private var workingDirectory = "." inner class LsCommand(conf: Configuration) : BaseCommand() { @@ -29,7 +28,7 @@ class HdfsBrowserPlugin : Plugin { val p = line.indexOf(' ') val parseResult = parseOpts(if (p < 0) "" else line.substring(p + 1).trim(), "h") when (parseResult) { - is ParseResult.ParseError -> console.println(parseResult.msg) + is ParseResult.ParseError -> println(parseResult.msg) is ParseResult.ParsedOptions -> { val path = if (parseResult.other.isBlank()) workingDirectory else parseResult.other listFiles(path, parseResult.opts.isNotEmpty()) @@ -41,7 +40,6 @@ class HdfsBrowserPlugin : Plugin { override fun init(repl: KShell, config: Configuration) { this.repl = repl this.fs = FileSystem.get(findHadoopConfiguration(config)) - this.console = config.getConsoleReader() repl.registerCommand(LsCommand(config)) } @@ -66,18 +64,10 @@ class HdfsBrowserPlugin : Plugin { fs.listStatus(Path(path)).forEach { val summary = fs.getContentSummary(it.path) val size = if (isHumanReadable) calcHumanReadableSize(summary.length) else summary.length.toString() - console.println(String.format("%-20s%s", size, it.path)) + println(String.format("%-20s%s", size, it.path)) } } - private fun calcHumanReadableSize(bytes: Long, si: Boolean = false): String { - val unit = if (si) 1000 else 1024 - if (bytes < unit) return "$bytes B" - val exp = (Math.log(bytes.toDouble()) / Math.log(unit.toDouble())).toInt() - val pre = (if (si) "kMGTPE" else "KMGTPE")[exp - 1] + if (si) "" else "i" - return String.format("%.1f %sB", bytes / Math.pow(unit.toDouble(), exp.toDouble()), pre) - } - override fun cleanUp() { } companion object { diff --git a/jline3-shaded/pom.xml b/jline3-shaded/pom.xml new file mode 100644 index 0000000..4c50227 --- /dev/null +++ b/jline3-shaded/pom.xml @@ -0,0 +1,49 @@ + + + 4.0.0 + + sparklin + sparklin + 0.2-SNAPSHOT + + + + jline3-shaded + 0.2-SNAPSHOT + + + + org.jline + jline + 3.6.2 + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.1.0 + + + package + + shade + + + + + org.jline + sparklin.kshell.org.jline + + + + + + + + + diff --git a/kshell-console-jline2/pom.xml b/kshell-console-jline2/pom.xml deleted file mode 100644 index a3559be..0000000 --- a/kshell-console-jline2/pom.xml +++ /dev/null @@ -1,131 +0,0 @@ - - - 4.0.0 - - sparklin - sparklin - 0.2-SNAPSHOT - - - kshell-console-jline2 - 0.2-SNAPSHOT - - - - org.jetbrains.kotlin - kotlin-stdlib-jdk8 - ${kotlin.version} - - - org.jetbrains.kotlin - kotlin-stdlib - ${kotlin.version} - - - org.jetbrains.kotlin - kotlin-reflect - ${kotlin.version} - - - - org.fusesource.jansi - jansi - 1.16 - - - junit - junit - 4.0 - - - sparklin - kshell - ${sparklin.version} - - - - - - - ${project.basedir}/src/main/resources - - - - - - - kotlin-maven-plugin - - ${jvm.version} - - org.jetbrains.kotlin - ${kotlin.version} - - - - compile - compile - compile - - - src/main/kotlin - src/main/java - - - - - test-compile - test-compile - test-compile - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.5.1 - - - - default-compile - none - - - - default-testCompile - none - - - java-compile - compile - compile - - - java-test-compile - test-compile - testCompile - - - - 1.6 - 1.6 - - - - org.apache.maven.plugins - maven-jar-plugin - 2.6 - - - - - sparklin.repl.Main - - - - - - - diff --git a/kshell-console-jline2/src/main/java/lib/jline/AnsiWindowsTerminal.java b/kshell-console-jline2/src/main/java/lib/jline/AnsiWindowsTerminal.java deleted file mode 100644 index c89a6fd..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/AnsiWindowsTerminal.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.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) { - if (lib.jline.internal.Configuration.isWindows()) { - // 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/kshell-console-jline2/src/main/java/lib/jline/DefaultTerminal2.java b/kshell-console-jline2/src/main/java/lib/jline/DefaultTerminal2.java deleted file mode 100644 index 3866023..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/DefaultTerminal2.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import lib.jline.internal.InfoCmp; - -/** - * Terminal wrapper with default ansi capabilities - */ -public class DefaultTerminal2 implements Terminal2 { - - private final Terminal terminal; - private final Set bools = new HashSet(); - private final Map strings = new HashMap(); - - public DefaultTerminal2(Terminal terminal) { - this.terminal = terminal; - registerCap("key_backspace", "^H"); - registerCap("bell", "^G"); - registerCap("carriage_return", "^M"); - if (isSupported() && isAnsiSupported()) { - registerCap("clr_eol", "\\E[K"); - registerCap("clr_bol", "\\E[1K"); - registerCap("cursor_up", "\\E[A"); - registerCap("cursor_down", "^J"); - registerCap("column_address", "\\E[%i%p1%dG"); - registerCap("clear_screen", "\\E[H\\E[2J"); - registerCap("parm_down_cursor", "\\E[%p1%dB"); - registerCap("cursor_left", "^H"); - registerCap("cursor_right", "\\E[C"); - } - if (hasWeirdWrap()) { - registerCap("eat_newline_glitch"); - registerCap("auto_right_margin"); - } - } - - public void init() throws Exception { - terminal.init(); - } - - public void restore() throws Exception { - terminal.restore(); - } - - public void reset() throws Exception { - terminal.reset(); - } - - public boolean isSupported() { - return terminal.isSupported(); - } - - public int getWidth() { - return terminal.getWidth(); - } - - public int getHeight() { - return terminal.getHeight(); - } - - public boolean isAnsiSupported() { - return terminal.isAnsiSupported(); - } - - public OutputStream wrapOutIfNeeded(OutputStream out) { - return terminal.wrapOutIfNeeded(out); - } - - public InputStream wrapInIfNeeded(InputStream in) throws IOException { - return terminal.wrapInIfNeeded(in); - } - - public boolean hasWeirdWrap() { - return terminal.hasWeirdWrap(); - } - - public boolean isEchoEnabled() { - return terminal.isEchoEnabled(); - } - - public void setEchoEnabled(boolean enabled) { - terminal.setEchoEnabled(enabled); - } - - public void disableInterruptCharacter() { - terminal.disableInterruptCharacter(); - } - - public void enableInterruptCharacter() { - terminal.enableInterruptCharacter(); - } - - public String getOutputEncoding() { - return terminal.getOutputEncoding(); - } - - private void registerCap(String cap, String value) { - for (String key : InfoCmp.getNames(cap)) { - strings.put(key, value); - } - } - - private void registerCap(String cap) { - Collections.addAll(bools, InfoCmp.getNames(cap)); - } - - public boolean getBooleanCapability(String capability) { - return bools.contains(capability); - } - - public Integer getNumericCapability(String capability) { - return null; - } - - public String getStringCapability(String capability) { - return strings.get(capability); - } - -} diff --git a/kshell-console-jline2/src/main/java/lib/jline/NoInterruptUnixTerminal.java b/kshell-console-jline2/src/main/java/lib/jline/NoInterruptUnixTerminal.java deleted file mode 100644 index 70bcd20..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/NoInterruptUnixTerminal.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline; - -// Based on Apache Karaf impl - -/** - * Non-interruptible (via CTRL-C) {@link UnixTerminal}. - * - * @since 2.0 - */ -public class NoInterruptUnixTerminal - extends UnixTerminal -{ - private String intr; - - public NoInterruptUnixTerminal() throws Exception { - super(); - } - - @Override - public void init() throws Exception { - super.init(); - intr = getSettings().getPropertyAsString("intr"); - if ("".equals(intr)) { - intr = null; - } - if (intr != null) { - getSettings().undef("intr"); - } - } - - @Override - public void restore() throws Exception { - if (intr != null) { - getSettings().set("intr", intr); - } - super.restore(); - } -} diff --git a/kshell-console-jline2/src/main/java/lib/jline/OSvTerminal.java b/kshell-console-jline2/src/main/java/lib/jline/OSvTerminal.java deleted file mode 100644 index 65d5b19..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/OSvTerminal.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline; - -import lib.jline.internal.Log; - -/** - * Terminal that is used for OSv. This is seperate to unix terminal - * implementation because exec cannot be used as currently used by UnixTerminal. - * - * This implimentation is derrived from the implementation at - * http://github.com/cloudius-systems/mgmt/blob/master/crash/src/main/java/com/cloudius/cli/OSvTerminal.java - * authored by Or Cohen. - * - * @author Or Cohen - * @author Arun Neelicattu - * @since 2.13 - */ -public class OSvTerminal - extends TerminalSupport -{ - - public Class sttyClass = null; - public Object stty = null; - - public OSvTerminal() { - super(true); - - setAnsiSupported(true); - - try { - if (stty == null) { - sttyClass = Class.forName("com.cloudius.util.Stty"); - stty = sttyClass.newInstance(); - } - } catch (Exception e) { - Log.warn("Failed to load com.cloudius.util.Stty", e); - } - } - - @Override - public void init() throws Exception { - super.init(); - - if (stty != null) { - sttyClass.getMethod("jlineMode").invoke(stty); - } - } - - @Override - public void restore() throws Exception { - if (stty != null) { - sttyClass.getMethod("reset").invoke(stty); - } - super.restore(); - - // Newline in end of restore like in UnixTerminal - System.out.println(); - } - -} diff --git a/kshell-console-jline2/src/main/java/lib/jline/Terminal.java b/kshell-console-jline2/src/main/java/lib/jline/Terminal.java deleted file mode 100644 index 5049147..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/Terminal.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -/** - * Representation of the input terminal for a platform. - * - * @author Marc Prud'hommeaux - * @author Jason Dillon - * @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); - - /** - * When using native support, return the InputStream to use for reading characters - * else return the input stream passed as a parameter. - * - * @since 2.6 - */ - InputStream wrapInIfNeeded(InputStream in) throws IOException; - - /** - * 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); - - void disableInterruptCharacter(); - void enableInterruptCharacter(); - - String getOutputEncoding(); - -} diff --git a/kshell-console-jline2/src/main/java/lib/jline/Terminal2.java b/kshell-console-jline2/src/main/java/lib/jline/Terminal2.java deleted file mode 100644 index e53c670..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/Terminal2.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline; - -/** - * Terminal extension. - * - * @author Guillaume Nodet - * @since 2.13 - */ -public interface Terminal2 extends Terminal -{ - boolean getBooleanCapability(String capability); - - Integer getNumericCapability(String capability); - - String getStringCapability(String capability); - -} diff --git a/kshell-console-jline2/src/main/java/lib/jline/TerminalFactory.java b/kshell-console-jline2/src/main/java/lib/jline/TerminalFactory.java deleted file mode 100644 index c5cb7f1..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/TerminalFactory.java +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline; - -import lib.jline.internal.Configuration; -import lib.jline.internal.Log; -import lib.jline.internal.Preconditions; - -import java.lang.reflect.Constructor; -import java.text.MessageFormat; -import java.util.HashMap; -import java.util.Map; - -/** - * Creates terminal instances. - * - * @author Jason Dillon - * @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 OSV = "osv"; - - public static final String WIN = "win"; - - public static final String WINDOWS = "windows"; - - public static final String FREEBSD = "freebsd"; - - public static final String NONE = "none"; - - public static final String OFF = "off"; - - public static final String FALSE = "false"; - - private static lib.jline.Terminal term = null; - - public static synchronized lib.jline.Terminal create() { - return create(null); - } - - public static synchronized lib.jline.Terminal create(String ttyDevice) { - if (Log.TRACE) { - //noinspection ThrowableInstanceNeverThrown - Log.trace(new Throwable("CREATE MARKER")); - } - - String type = Configuration.getString(JLINE_TERMINAL); - if (type == null) { - type = AUTO; - if ("dumb".equals(System.getenv("TERM"))) { - // emacs communicates with shell through a 'dumb' terminal - // but sets these env variables to let programs know - // it is ok to send ANSI control sequences - String emacs = System.getenv("EMACS"); - String insideEmacs = System.getenv("INSIDE_EMACS"); - if (emacs == null || insideEmacs == null) { - type = NONE; - } - } - } - - Log.debug("Creating terminal; type=", type); - - lib.jline.Terminal t; - try { - String tmp = type.toLowerCase(); - - if (tmp.equals(UNIX)) { - t = getFlavor(Flavor.UNIX); - } - else if (tmp.equals(OSV)) { - t = getFlavor(Flavor.OSV); - } - 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; - } else if (System.getenv("OSV_CPUS") != null) { - flavor = Flavor.OSV; - } - t = getFlavor(flavor, ttyDevice); - } - else { - try { - t = (lib.jline.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 (Throwable e) { - Log.error("Terminal initialization failed; falling back to unsupported", e); - return new UnsupportedTerminal(); - } - - return t; - } - - public static synchronized void reset() { - term = null; - } - - public static synchronized void resetIf(final lib.jline.Terminal t) { - if(t == term) { - reset(); - } - } - - public static enum Type - { - AUTO, - WINDOWS, - UNIX, - OSV, - NONE - } - - public static synchronized void configure(final String type) { - Preconditions.checkNotNull(type); - System.setProperty(JLINE_TERMINAL, type); - } - - public static synchronized void configure(final Type type) { - Preconditions.checkNotNull(type); - configure(type.name().toLowerCase()); - } - - // - // Flavor Support - // - - public static enum Flavor - { - WINDOWS, - UNIX, - OSV - } - - private static final Map> FLAVORS = new HashMap>(); - - static { - registerFlavor(Flavor.WINDOWS, lib.jline.AnsiWindowsTerminal.class); - registerFlavor(Flavor.UNIX, lib.jline.UnixTerminal.class); - registerFlavor(Flavor.OSV, OSvTerminal.class); - } - - public static synchronized lib.jline.Terminal get(String ttyDevice) { - // The code is assuming we've got only one terminal per process. - // Continuing this assumption, if this terminal is already initialized, - // we don't check if it's using the same tty line either. Both assumptions - // are a bit crude. TODO: check single terminal assumption. - if (term == null) { - term = create(ttyDevice); - } - return term; - } - - public static synchronized lib.jline.Terminal get() { - return get(null); - } - - public static lib.jline.Terminal getFlavor(final Flavor flavor) throws Exception { - return getFlavor(flavor, null); - } - - public static lib.jline.Terminal getFlavor(final Flavor flavor, String ttyDevice) throws Exception { - Class type = FLAVORS.get(flavor); - lib.jline.Terminal result = null; - if (type != null) { - if (ttyDevice != null) { - Constructor ttyDeviceConstructor = type.getConstructor(String.class); - if (ttyDeviceConstructor != null) { - result = (lib.jline.Terminal) ttyDeviceConstructor.newInstance(ttyDevice); - } else { - result = type.newInstance(); - } - } else { - result = type.newInstance(); - } - } else { - throw new InternalError(); - } - return result; - } - - public static void registerFlavor(final Flavor flavor, final Class type) { - FLAVORS.put(flavor, type); - } - -} diff --git a/kshell-console-jline2/src/main/java/lib/jline/TerminalSupport.java b/kshell-console-jline2/src/main/java/lib/jline/TerminalSupport.java deleted file mode 100644 index d40afea..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/TerminalSupport.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import lib.jline.internal.Log; -import lib.jline.internal.ShutdownHooks; - -/** - * Provides support for {@link Terminal} instances. - * - * @author Jason Dillon - * @since 2.0 - */ -public abstract class TerminalSupport - implements Terminal -{ - public static final int DEFAULT_WIDTH = 80; - - public static final int DEFAULT_HEIGHT = 24; - - private ShutdownHooks.Task shutdownTask; - - private boolean supported; - - private boolean echoEnabled; - - private boolean ansiSupported; - - protected TerminalSupport(final boolean supported) { - this.supported = supported; - } - - public void init() throws Exception { - if (shutdownTask != null) { - ShutdownHooks.remove(shutdownTask); - } - // Register a task to restore the terminal on shutdown - this.shutdownTask = ShutdownHooks.add(new ShutdownHooks.Task() - { - public void run() throws Exception { - restore(); - } - }); - } - - public void restore() throws Exception { - TerminalFactory.resetIf(this); - if (shutdownTask != null) { - ShutdownHooks.remove(shutdownTask); - shutdownTask = null; - } - } - - public void reset() throws Exception { - restore(); - init(); - } - - 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 void disableInterruptCharacter() { - } - - public void enableInterruptCharacter() { - } - - public InputStream wrapInIfNeeded(InputStream in) throws IOException { - return in; - } - - public String getOutputEncoding() { - // null for unknown - return null; - } -} diff --git a/kshell-console-jline2/src/main/java/lib/jline/UnixTerminal.java b/kshell-console-jline2/src/main/java/lib/jline/UnixTerminal.java deleted file mode 100644 index af75d7e..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/UnixTerminal.java +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import lib.jline.internal.*; -import lib.jline.internal.Configuration; -import lib.jline.internal.Log; - -/** - * Terminal that is used for unix platforms. Terminal initialization - * is handled by issuing the stty command against the - * /dev/tty file to disable character echoing and enable - * character input. All known unix systems (including - * Linux and Macintosh OS X) support the stty), so this - * implementation should work for an reasonable POSIX system. - * - * @author Marc Prud'hommeaux - * @author Dale Kemp - * @author Jason Dillon - * @author Jean-Baptiste Onofr�� - * @since 2.0 - */ -public class UnixTerminal - extends TerminalSupport - implements Terminal2 -{ - private final lib.jline.internal.TerminalLineSettings settings; - private final String type; - private String intr; - private String lnext; - private Set bools = new HashSet(); - private Map ints = new HashMap(); - private Map strings = new HashMap(); - - public UnixTerminal() throws Exception { - this(lib.jline.internal.TerminalLineSettings.DEFAULT_TTY, null); - } - - public UnixTerminal(String ttyDevice) throws Exception { - this(ttyDevice, null); - } - - public UnixTerminal(String ttyDevice, String type) throws Exception { - super(true); - lib.jline.internal.Preconditions.checkNotNull(ttyDevice); - this.settings = lib.jline.internal.TerminalLineSettings.getSettings(ttyDevice); - if (type == null) { - type = System.getenv("TERM"); - } - this.type = type; - parseInfoCmp(); - } - - public lib.jline.internal.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. - // Make sure we're distinguishing carriage return from newline. - // Allow ctrl-s keypress to be used (as forward search) - // - // Please note that FreeBSD does not seem to support -icrnl and thus - // has to be handled separately. Otherwise the console will be "stuck" - // and will neither accept input nor print anything to stdout. - if (Configuration.getOsName().contains(TerminalFactory.FREEBSD)) { - settings.set("-icanon min 1 -inlcr -ixon"); - } else { - settings.set("-icanon min 1 -icrnl -inlcr -ixon"); - } - settings.undef("dsusp"); - - setEchoEnabled(false); - - parseInfoCmp(); - } - - /** - * 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(); - } - - /** - * Returns the value of stty columns param. - */ - @Override - public int getWidth() { - int w = settings.getProperty("columns"); - return w < 1 ? DEFAULT_WIDTH : w; - } - - /** - * Returns the value of stty rows>/tt> param. - */ - @Override - public int getHeight() { - int h = settings.getProperty("rows"); - return h < 1 ? DEFAULT_HEIGHT : h; - } - - @Override - public boolean hasWeirdWrap() { - return getBooleanCapability("auto_right_margin") - && getBooleanCapability("eat_newline_glitch"); - } - - @Override - public synchronized void setEchoEnabled(final boolean enabled) { - try { - if (enabled) { - settings.set("echo"); - } - else { - settings.set("-echo"); - } - super.setEchoEnabled(enabled); - } - catch (Exception e) { - if (e instanceof InterruptedException) { - Thread.currentThread().interrupt(); - } - Log.error("Failed to ", enabled ? "enable" : "disable", " echo", e); - } - } - - public void disableInterruptCharacter() - { - try { - intr = getSettings().getPropertyAsString("intr"); - if ("".equals(intr)) { - intr = null; - } - settings.undef("intr"); - } - catch (Exception e) { - if (e instanceof InterruptedException) { - Thread.currentThread().interrupt(); - } - Log.error("Failed to disable interrupt character", e); - } - } - - public void enableInterruptCharacter() - { - try { - if (intr != null) { - settings.set("intr", intr); - } - } - catch (Exception e) { - if (e instanceof InterruptedException) { - Thread.currentThread().interrupt(); - } - Log.error("Failed to enable interrupt character", e); - } - } - - public void disableLitteralNextCharacter() - { - try { - lnext = getSettings().getPropertyAsString("lnext"); - if ("".equals(lnext)) { - lnext = null; - } - settings.undef("lnext"); - } - catch (Exception e) { - if (e instanceof InterruptedException) { - Thread.currentThread().interrupt(); - } - Log.error("Failed to disable litteral next character", e); - } - } - - public void enableLitteralNextCharacter() - { - try { - if (lnext != null) { - settings.set("lnext", lnext); - } - } - catch (Exception e) { - if (e instanceof InterruptedException) { - Thread.currentThread().interrupt(); - } - Log.error("Failed to enable litteral next character", e); - } - } - - public boolean getBooleanCapability(String capability) { - return bools.contains(capability); - } - - public Integer getNumericCapability(String capability) { - return ints.get(capability); - } - - public String getStringCapability(String capability) { - return strings.get(capability); - } - - private void parseInfoCmp() { - String capabilities = null; - if (type != null) { - try { - capabilities = InfoCmp.getInfoCmp(type); - } catch (Exception e) { - } - } - if (capabilities == null) { - capabilities = InfoCmp.getAnsiCaps(); - } - InfoCmp.parseInfoCmp(capabilities, bools, ints, strings); - } -} diff --git a/kshell-console-jline2/src/main/java/lib/jline/UnsupportedTerminal.java b/kshell-console-jline2/src/main/java/lib/jline/UnsupportedTerminal.java deleted file mode 100644 index a11bf4f..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/UnsupportedTerminal.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline; - -/** - * An unsupported terminal. - * - * @author Marc Prud'hommeaux - * @author Jason Dillon - * @since 2.0 - */ -public class UnsupportedTerminal - extends TerminalSupport -{ - public UnsupportedTerminal() { - super(false); - setAnsiSupported(false); - setEchoEnabled(true); - } -} diff --git a/kshell-console-jline2/src/main/java/lib/jline/WindowsTerminal.java b/kshell-console-jline2/src/main/java/lib/jline/WindowsTerminal.java deleted file mode 100644 index 716bcf6..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/WindowsTerminal.java +++ /dev/null @@ -1,384 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline; - -import java.io.FileDescriptor; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; - -import lib.jline.internal.Log; -import org.fusesource.jansi.internal.WindowsSupport; -import org.fusesource.jansi.internal.Kernel32; -import static org.fusesource.jansi.internal.Kernel32.*; - -import static lib.jline.WindowsTerminal.ConsoleMode.ENABLE_ECHO_INPUT; -import static lib.jline.WindowsTerminal.ConsoleMode.ENABLE_LINE_INPUT; -import static lib.jline.WindowsTerminal.ConsoleMode.ENABLE_PROCESSED_INPUT; -import static lib.jline.WindowsTerminal.ConsoleMode.ENABLE_WINDOW_INPUT; - -/** - * Terminal implementation for Microsoft Windows. Terminal initialization in - * {@link #init} is accomplished by extracting the - * jline_version.dll, saving it to the system temporary - * directoy (determined by the setting of the java.io.tmpdir System - * property), loading the library, and then calling the Win32 APIs SetConsoleMode and - * GetConsoleMode to - * disable character echoing. - *

- *

- * By default, the {@link #wrapInIfNeeded(java.io.InputStream)} 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 WindowsTerminal.directConsole system property - * to false. - *

- * - * @author Marc Prud'hommeaux - * @author Jason Dillon - * @since 2.0 - */ -public class WindowsTerminal - extends TerminalSupport -{ - public static final String DIRECT_CONSOLE = WindowsTerminal.class.getName() + ".directConsole"; - - public static final String ANSI = WindowsTerminal.class.getName() + ".ansi"; - - private boolean directConsole; - - private int originalMode; - - public WindowsTerminal() throws Exception { - super(true); - } - - @Override - public void init() throws Exception { - super.init(); - - setAnsiSupported(lib.jline.internal.Configuration.getBoolean(ANSI, true)); - - // - // FIXME: Need a way to disable direct console and sysin detection muck - // - - setDirectConsole(lib.jline.internal.Configuration.getBoolean(DIRECT_CONSOLE, true)); - - 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_WINDOW_INPUT.code); - } - else { - setConsoleMode(getConsoleMode() & - ~(ENABLE_LINE_INPUT.code | - ENABLE_ECHO_INPUT.code | - ENABLE_WINDOW_INPUT.code)); - } - super.setEchoEnabled(enabled); - } - - public void disableInterruptCharacter() { - setConsoleMode(getConsoleMode() & - ~(ENABLE_PROCESSED_INPUT.code)); - } - - public void enableInterruptCharacter() { - setConsoleMode(getConsoleMode() | - ENABLE_PROCESSED_INPUT.code); - } - - /** - * 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 InputStream wrapInIfNeeded(InputStream in) throws IOException { - if (directConsole && isSystemIn(in)) { - return new InputStream() { - private byte[] buf = null; - int bufIdx = 0; - - @Override - public int read() throws IOException { - while (buf == null || bufIdx == buf.length) { - buf = readConsoleInput(); - bufIdx = 0; - } - int c = buf[bufIdx] & 0xFF; - bufIdx++; - return c; - } - }; - } else { - return super.wrapInIfNeeded(in); - } - } - - protected boolean isSystemIn(final InputStream in) throws IOException { - if (in == null) { - return false; - } - else if (in == System.in) { - return true; - } - else if (in instanceof FileInputStream && ((FileInputStream) in).getFD() == FileDescriptor.in) { - return true; - } - - return false; - } - - @Override - public String getOutputEncoding() { - int codepage = getConsoleOutputCodepage(); - //http://docs.oracle.com/javase/6/docs/technotes/guides/intl/encoding.doc.html - String charsetMS = "ms" + codepage; - if (java.nio.charset.Charset.isSupported(charsetMS)) { - return charsetMS; - } - String charsetCP = "cp" + codepage; - if (java.nio.charset.Charset.isSupported(charsetCP)) { - return charsetCP; - } - Log.debug("can't figure out the Java Charset of this code page (" + codepage + ")..."); - return super.getOutputEncoding(); - } - - // - // Native Bits - // - private static int getConsoleMode() { - return WindowsSupport.getConsoleMode(); - } - - private static void setConsoleMode(int mode) { - WindowsSupport.setConsoleMode(mode); - } - - private byte[] readConsoleInput() { - // XXX does how many events to read in one call matter? - INPUT_RECORD[] events = null; - try { - events = WindowsSupport.readConsoleInput(1); - } catch (IOException e) { - Log.debug("read Windows console input error: ", e); - } - if (events == null) { - return new byte[0]; - } - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < events.length; i++ ) { - KEY_EVENT_RECORD keyEvent = events[i].keyEvent; - //Log.trace(keyEvent.keyDown? "KEY_DOWN" : "KEY_UP", "key code:", keyEvent.keyCode, "char:", (long)keyEvent.uchar); - if (keyEvent.keyDown) { - if (keyEvent.uchar > 0) { - // support some C1 control sequences: ALT + [@-_] (and [a-z]?) => ESC - // http://en.wikipedia.org/wiki/C0_and_C1_control_codes#C1_set - final int altState = KEY_EVENT_RECORD.LEFT_ALT_PRESSED | KEY_EVENT_RECORD.RIGHT_ALT_PRESSED; - // Pressing "Alt Gr" is translated to Alt-Ctrl, hence it has to be checked that Ctrl is _not_ pressed, - // otherwise inserting of "Alt Gr" codes on non-US keyboards would yield errors - final int ctrlState = KEY_EVENT_RECORD.LEFT_CTRL_PRESSED | KEY_EVENT_RECORD.RIGHT_CTRL_PRESSED; - if (((keyEvent.uchar >= '@' && keyEvent.uchar <= '_') || (keyEvent.uchar >= 'a' && keyEvent.uchar <= 'z')) - && ((keyEvent.controlKeyState & altState) != 0) && ((keyEvent.controlKeyState & ctrlState) == 0)) { - sb.append('\u001B'); // ESC - } - - sb.append(keyEvent.uchar); - continue; - } - // virtual keycodes: http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx - // just add support for basic editing keys (no control state, no numpad keys) - String escapeSequence = null; - switch (keyEvent.keyCode) { - case 0x21: // VK_PRIOR PageUp - escapeSequence = "\u001B[5~"; - break; - case 0x22: // VK_NEXT PageDown - escapeSequence = "\u001B[6~"; - break; - case 0x23: // VK_END - escapeSequence = "\u001B[4~"; - break; - case 0x24: // VK_HOME - escapeSequence = "\u001B[1~"; - break; - case 0x25: // VK_LEFT - escapeSequence = "\u001B[D"; - break; - case 0x26: // VK_UP - escapeSequence = "\u001B[A"; - break; - case 0x27: // VK_RIGHT - escapeSequence = "\u001B[C"; - break; - case 0x28: // VK_DOWN - escapeSequence = "\u001B[B"; - break; - case 0x2D: // VK_INSERT - escapeSequence = "\u001B[2~"; - break; - case 0x2E: // VK_DELETE - escapeSequence = "\u001B[3~"; - break; - default: - break; - } - if (escapeSequence != null) { - for (int k = 0; k < keyEvent.repeatCount; k++) { - sb.append(escapeSequence); - } - } - } else { - // key up event - // support ALT+NumPad input method - if (keyEvent.keyCode == 0x12/*VK_MENU ALT key*/ && keyEvent.uchar > 0) { - sb.append(keyEvent.uchar); - } - } - } - return sb.toString().getBytes(); - } - - private static int getConsoleOutputCodepage() { - return Kernel32.GetConsoleOutputCP(); - } - - private static int getWindowsTerminalWidth() { - return WindowsSupport.getWindowsTerminalWidth(); - } - - private static int getWindowsTerminalHeight() { - return WindowsSupport.getWindowsTerminalHeight(); - } - - /** - * Console mode - *

- * Constants copied wincon.h. - */ - 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; - } - } - -} diff --git a/kshell-console-jline2/src/main/java/lib/jline/console/ConsoleKeys.java b/kshell-console-jline2/src/main/java/lib/jline/console/ConsoleKeys.java deleted file mode 100644 index 92d987c..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/console/ConsoleKeys.java +++ /dev/null @@ -1,387 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline.console; - -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import lib.jline.internal.Log; - -/** - * @author Ståle W. Pedersen - */ -public class ConsoleKeys { - - private KeyMap keys; - - private Map keyMaps; - private Map variables = new HashMap(); - - public ConsoleKeys(String appName, URL inputrcUrl) { - keyMaps = KeyMap.keyMaps(); - setVar("editing-mode", "emacs"); - loadKeys(appName, inputrcUrl); - } - - protected boolean setKeyMap (String name) { - KeyMap map = keyMaps.get(name); - if (map == null) { - return false; - } - this.keys = map; - return true; - } - - protected Map getKeyMaps() { - return keyMaps; - } - - protected KeyMap getKeys() { - return keys; - } - - protected void setKeys(KeyMap keys) { - this.keys = keys; - } - - protected void loadKeys(String appName, URL inputrcUrl) { - keys = keyMaps.get(KeyMap.EMACS); - - try { - InputStream input = inputrcUrl.openStream(); - try { - loadKeys(input, appName); - Log.debug("Loaded user configuration: ", inputrcUrl); - } - finally { - try { - input.close(); - } catch (IOException e) { - // Ignore - } - } - } - catch (IOException e) { - if (inputrcUrl.getProtocol().equals("file")) { - File file = new File(inputrcUrl.getPath()); - if (file.exists()) { - Log.warn("Unable to read user configuration: ", inputrcUrl, e); - } - } else { - Log.warn("Unable to read user configuration: ", inputrcUrl, e); - } - } - } - - private void loadKeys(InputStream input, String appName) throws IOException { - BufferedReader reader = new BufferedReader( new java.io.InputStreamReader( input ) ); - String line; - boolean parsing = true; - List ifsStack = new ArrayList(); - while ( (line = reader.readLine()) != null ) { - try { - line = line.trim(); - if (line.length() == 0) { - continue; - } - if (line.charAt(0) == '#') { - continue; - } - int i = 0; - if (line.charAt(i) == '$') { - String cmd; - String args; - for (++i; i < line.length() && (line.charAt(i) == ' ' || line.charAt(i) == '\t'); i++); - int s = i; - for (; i < line.length() && (line.charAt(i) != ' ' && line.charAt(i) != '\t'); i++); - cmd = line.substring(s, i); - for (; i < line.length() && (line.charAt(i) == ' ' || line.charAt(i) == '\t'); i++); - s = i; - for (; i < line.length() && (line.charAt(i) != ' ' && line.charAt(i) != '\t'); i++); - args = line.substring(s, i); - if ("if".equalsIgnoreCase(cmd)) { - ifsStack.add( parsing ); - if (!parsing) { - continue; - } - if (args.startsWith("term=")) { - // TODO - } else if (args.startsWith("mode=")) { - String mode = variables.get("editing-mode"); - parsing = args.substring("mode=".length()).equalsIgnoreCase(mode); - } else { - parsing = args.equalsIgnoreCase(appName); - } - } else if ("else".equalsIgnoreCase(cmd)) { - if (ifsStack.isEmpty()) { - throw new IllegalArgumentException("$else found without matching $if"); - } - boolean invert = true; - for (boolean b : ifsStack) { - if (!b) { - invert = false; - break; - } - } - if (invert) { - parsing = !parsing; - } - } else if ("endif".equalsIgnoreCase(cmd)) { - if (ifsStack.isEmpty()) { - throw new IllegalArgumentException("endif found without matching $if"); - } - parsing = ifsStack.remove( ifsStack.size() - 1 ); - } else if ("include".equalsIgnoreCase(cmd)) { - // TODO - } - continue; - } - if (!parsing) { - continue; - } - boolean equivalency; - String keySeq = ""; - if (line.charAt(i++) == '"') { - boolean esc = false; - for (;; i++) { - if (i >= line.length()) { - throw new IllegalArgumentException("Missing closing quote on line '" + line + "'"); - } - if (esc) { - esc = false; - } else if (line.charAt(i) == '\\') { - esc = true; - } else if (line.charAt(i) == '"') { - break; - } - } - } - for (; i < line.length() && line.charAt(i) != ':' - && line.charAt(i) != ' ' && line.charAt(i) != '\t' - ; i++); - keySeq = line.substring(0, i); - equivalency = i + 1 < line.length() && line.charAt(i) == ':' && line.charAt(i + 1) == '='; - i++; - if (equivalency) { - i++; - } - if (keySeq.equalsIgnoreCase("set")) { - String key; - String val; - for (; i < line.length() && (line.charAt(i) == ' ' || line.charAt(i) == '\t'); i++); - int s = i; - for (; i < line.length() && (line.charAt(i) != ' ' && line.charAt(i) != '\t'); i++); - key = line.substring( s, i ); - for (; i < line.length() && (line.charAt(i) == ' ' || line.charAt(i) == '\t'); i++); - s = i; - for (; i < line.length() && (line.charAt(i) != ' ' && line.charAt(i) != '\t'); i++); - val = line.substring( s, i ); - setVar( key, val ); - } else { - for (; i < line.length() && (line.charAt(i) == ' ' || line.charAt(i) == '\t'); i++); - int start = i; - if (i < line.length() && (line.charAt(i) == '\'' || line.charAt(i) == '\"')) { - char delim = line.charAt(i++); - boolean esc = false; - for (;; i++) { - if (i >= line.length()) { - break; - } - if (esc) { - esc = false; - } else if (line.charAt(i) == '\\') { - esc = true; - } else if (line.charAt(i) == delim) { - break; - } - } - } - for (; i < line.length() && line.charAt(i) != ' ' && line.charAt(i) != '\t'; i++); - String val = line.substring(Math.min(start, line.length()), Math.min(i, line.length())); - if (keySeq.charAt(0) == '"') { - keySeq = translateQuoted(keySeq); - } else { - // Bind key name - String keyName = keySeq.lastIndexOf('-') > 0 ? keySeq.substring( keySeq.lastIndexOf('-') + 1 ) : keySeq; - char key = getKeyFromName(keyName); - keyName = keySeq.toLowerCase(); - keySeq = ""; - if (keyName.contains("meta-") || keyName.contains("m-")) { - keySeq += "\u001b"; - } - if (keyName.contains("control-") || keyName.contains("c-") || keyName.contains("ctrl-")) { - key = (char)(Character.toUpperCase( key ) & 0x1f); - } - keySeq += key; - } - if (val.length() > 0 && (val.charAt(0) == '\'' || val.charAt(0) == '\"')) { - keys.bind( keySeq, translateQuoted(val) ); - } else { - String operationName = val.replace('-', '_').toUpperCase(); - try { - keys.bind(keySeq, Operation.valueOf(operationName)); - } catch(IllegalArgumentException e) { - Log.info("Unable to bind key for unsupported operation: ", val); - } - } - } - } catch (IllegalArgumentException e) { - Log.warn("Unable to parse user configuration: ", e); - } - } - } - - private static String translateQuoted(String keySeq) { - int i; - String str = keySeq.substring( 1, keySeq.length() - 1 ); - keySeq = ""; - for (i = 0; i < str.length(); i++) { - char c = str.charAt(i); - if (c == '\\') { - boolean ctrl = str.regionMatches(i, "\\C-", 0, 3)|| str.regionMatches(i, "\\M-\\C-", 0, 6); - boolean meta = str.regionMatches(i, "\\M-", 0, 3)|| str.regionMatches(i, "\\C-\\M-", 0, 6); - i += (meta ? 3 : 0) + (ctrl ? 3 : 0) + (!meta && !ctrl ? 1 : 0); - if (i >= str.length()) { - break; - } - c = str.charAt(i); - if (meta) { - keySeq += "\u001b"; - } - if (ctrl) { - c = c == '?' ? 0x7f : (char)(Character.toUpperCase( c ) & 0x1f); - } - if (!meta && !ctrl) { - switch (c) { - case 'a': c = 0x07; break; - case 'b': c = '\b'; break; - case 'd': c = 0x7f; break; - case 'e': c = 0x1b; break; - case 'f': c = '\f'; break; - case 'n': c = '\n'; break; - case 'r': c = '\r'; break; - case 't': c = '\t'; break; - case 'v': c = 0x0b; break; - case '\\': c = '\\'; break; - case '0': case '1': case '2': case '3': - case '4': case '5': case '6': case '7': - c = 0; - for (int j = 0; j < 3; j++, i++) { - if (i >= str.length()) { - break; - } - int k = Character.digit(str.charAt(i), 8); - if (k < 0) { - break; - } - c = (char)(c * 8 + k); - } - c &= 0xFF; - break; - case 'x': - i++; - c = 0; - for (int j = 0; j < 2; j++, i++) { - if (i >= str.length()) { - break; - } - int k = Character.digit(str.charAt(i), 16); - if (k < 0) { - break; - } - c = (char)(c * 16 + k); - } - c &= 0xFF; - break; - case 'u': - i++; - c = 0; - for (int j = 0; j < 4; j++, i++) { - if (i >= str.length()) { - break; - } - int k = Character.digit(str.charAt(i), 16); - if (k < 0) { - break; - } - c = (char)(c * 16 + k); - } - break; - } - } - keySeq += c; - } else { - keySeq += c; - } - } - return keySeq; - } - - private static char getKeyFromName(String name) { - if ("DEL".equalsIgnoreCase(name) || "Rubout".equalsIgnoreCase(name)) { - return 0x7f; - } else if ("ESC".equalsIgnoreCase(name) || "Escape".equalsIgnoreCase(name)) { - return '\033'; - } else if ("LFD".equalsIgnoreCase(name) || "NewLine".equalsIgnoreCase(name)) { - return '\n'; - } else if ("RET".equalsIgnoreCase(name) || "Return".equalsIgnoreCase(name)) { - return '\r'; - } else if ("SPC".equalsIgnoreCase(name) || "Space".equalsIgnoreCase(name)) { - return ' '; - } else if ("Tab".equalsIgnoreCase(name)) { - return '\t'; - } else { - return name.charAt(0); - } - } - - private void setVar(String key, String val) { - if ("keymap".equalsIgnoreCase(key)) { - if (keyMaps.containsKey(val)) { - keys = keyMaps.get(val); - } - } else if ("editing-mode".equals(key)) { - if ("vi".equalsIgnoreCase(val)) { - keys = keyMaps.get(KeyMap.VI_INSERT); - } else if ("emacs".equalsIgnoreCase(key)) { - keys = keyMaps.get(KeyMap.EMACS); - } - } else if ("blink-matching-paren".equals(key)) { - if ("on".equalsIgnoreCase(val)) { - keys.setBlinkMatchingParen(true); - } else if ("off".equalsIgnoreCase(val)) { - keys.setBlinkMatchingParen(false); - } - } - - /* - * Technically variables should be defined as a functor class - * so that validation on the variable value can be done at parse - * time. This is a stop-gap. - */ - variables.put(key, val); - } - - /** - * Retrieves the value of a variable that was set in the .inputrc file - * during processing - * @param var The variable name - * @return The variable value. - */ - public String getVariable(String var) { - return variables.get (var); - } -} diff --git a/kshell-console-jline2/src/main/java/lib/jline/console/ConsoleReader.java b/kshell-console-jline2/src/main/java/lib/jline/console/ConsoleReader.java deleted file mode 100644 index 96d2a0d..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/console/ConsoleReader.java +++ /dev/null @@ -1,4006 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline.console; - -import java.awt.*; -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.BufferedReader; -import java.io.Closeable; -import java.io.File; -import java.io.FileDescriptor; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.Reader; -import java.io.Writer; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.net.URL; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; -import java.util.ResourceBundle; -import java.util.Stack; - -import lib.jline.console.history.History; -import lib.jline.console.history.MemoryHistory; -import lib.jline.internal.*; -import lib.jline.DefaultTerminal2; -import lib.jline.Terminal2; -import lib.jline.internal.Ansi; -import lib.jline.internal.InputStreamReader; -import lib.jline.internal.NonBlockingInputStream; -import lib.jline.internal.TerminalLineSettings; - -/** - * 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 lib.jline.Terminal#init} for convenience - * methods for issuing platform-specific setup commands. - * - * @author Marc Prud'hommeaux - * @author Jason Dillon - * @author Guillaume Nodet - */ -public class ConsoleReader implements Closeable -{ - public static final String JLINE_NOBELL = "jline.nobell"; - - public static final String JLINE_ESC_TIMEOUT = "jline.esc.timeout"; - - public static final String JLINE_INPUTRC = "jline.inputrc"; - - public static final String INPUT_RC = ".inputrc"; - - public static final String DEFAULT_INPUT_RC = "/etc/inputrc"; - - public static final String JLINE_EXPAND_EVENTS = "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 = 8; - - private static final ResourceBundle - resources = ResourceBundle.getBundle(lib.jline.console.completer.CandidateListCompletionHandler.class.getName()); - - private static final int ESCAPE = 27; - private static final int READ_EXPIRED = -2; - - private final Terminal2 terminal; - - private final Writer out; - - private final CursorBuffer buf = new CursorBuffer(); - private boolean cursorOk; - - private String prompt; - private int promptLen; - - private boolean expandEvents = lib.jline.internal.Configuration.getBoolean(JLINE_EXPAND_EVENTS, true); - - private boolean bellEnabled = !lib.jline.internal.Configuration.getBoolean(JLINE_NOBELL, true); - - private boolean handleUserInterrupt = false; - - private boolean handleLitteralNext = true; - - private Character mask; - - private Character echoCharacter; - - private CursorBuffer originalBuffer = null; - - private StringBuffer searchTerm = null; - - private String previousSearchTerm = ""; - - private int searchIndex = -1; - - private int parenBlinkTimeout = 500; - - // Reading buffers - private final StringBuilder opBuffer = new StringBuilder(); - private final Stack pushBackChar = new Stack(); - - /* - * The reader and the nonBlockingInput go hand-in-hand. The reader wraps - * the nonBlockingInput, but we have to retain a handle to it so that - * we can shut down its blocking read thread when we go away. - */ - private NonBlockingInputStream in; - private long escapeTimeout; - private Reader reader; - - /** - * Last character searched for with a vi character search - */ - private char charSearchChar = 0; // Character to search for - private char charSearchLastInvokeChar = 0; // Most recent invocation key - private char charSearchFirstInvokeChar = 0;// First character that invoked - - /** - * The vi yank buffer - */ - private String yankBuffer = ""; - - private KillRing killRing = new KillRing(); - - private String encoding; - - private boolean quotedInsert; - - private boolean recording; - - private String macro = ""; - - private String appName; - - private URL inputrcUrl; - - private ConsoleKeys consoleKeys; - - private String commentBegin = null; - - private boolean skipLF = false; - - /** - * Set to true if the reader should attempt to detect copy-n-paste. The - * effect of this that an attempt is made to detect if tab is quickly - * followed by another character, then it is assumed that the tab was - * a literal tab as part of a copy-and-paste operation and is inserted as - * such. - */ - private boolean copyPasteDetection = false; - - /* - * Current internal state of the line reader - */ - private State state = State.NORMAL; - - /** - * Possible states in which the current readline operation may be in. - */ - private static enum State { - /** - * The user is just typing away - */ - NORMAL, - /** - * In the middle of a emacs seach - */ - SEARCH, - FORWARD_SEARCH, - /** - * VI "yank-to" operation ("y" during move mode) - */ - VI_YANK_TO, - /** - * VI "delete-to" operation ("d" during move mode) - */ - VI_DELETE_TO, - /** - * VI "change-to" operation ("c" during move mode) - */ - VI_CHANGE_TO - } - - public ConsoleReader() throws IOException { - this(null, new FileInputStream(FileDescriptor.in), System.out, null); - } - - public ConsoleReader(final InputStream in, final OutputStream out) throws IOException { - this(null, in, out, null); - } - - public ConsoleReader(final InputStream in, final OutputStream out, final lib.jline.Terminal term) throws IOException { - this(null, in, out, term); - } - - public ConsoleReader(final @Nullable String appName, final InputStream in, final OutputStream out, final @Nullable lib.jline.Terminal term) throws IOException { - this(appName, in, out, term, null); - } - - public ConsoleReader(final @Nullable String appName, final InputStream in, final OutputStream out, final @Nullable lib.jline.Terminal term, final @Nullable String encoding) - throws IOException - { - this.appName = appName != null ? appName : "JLine"; - this.encoding = encoding != null ? encoding : lib.jline.internal.Configuration.getEncoding(); - lib.jline.Terminal terminal = term != null ? term : lib.jline.TerminalFactory.get(); - this.terminal = terminal instanceof Terminal2 ? (Terminal2) terminal : new DefaultTerminal2(terminal); - String outEncoding = terminal.getOutputEncoding() != null? terminal.getOutputEncoding() : this.encoding; - this.out = new OutputStreamWriter(terminal.wrapOutIfNeeded(out), outEncoding); - setInput( in ); - - this.inputrcUrl = getInputRc(); - - consoleKeys = new ConsoleKeys(this.appName, inputrcUrl); - - if (terminal instanceof lib.jline.UnixTerminal - && TerminalLineSettings.DEFAULT_TTY.equals(((lib.jline.UnixTerminal) terminal).getSettings().getTtyDevice()) - && lib.jline.internal.Configuration.getBoolean("jline.sigcont", false)) { - setupSigCont(); - } - } - - private void setupSigCont() { - // Check that sun.misc.SignalHandler and sun.misc.Signal exists - try { - Class signalClass = Class.forName("sun.misc.Signal"); - Class signalHandlerClass = Class.forName("sun.misc.SignalHandler"); - // Implement signal handler - Object signalHandler = Proxy.newProxyInstance(getClass().getClassLoader(), - new Class[]{signalHandlerClass}, new InvocationHandler() { - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - // only method we are proxying is handle() - terminal.init(); - try { - drawLine(); - flush(); - } catch (IOException e) { - e.printStackTrace(); - } - return null; - } - }); - // Register the signal handler, this code is equivalent to: - // Signal.handle(new Signal("CONT"), signalHandler); - signalClass.getMethod("handle", signalClass, signalHandlerClass).invoke(null, signalClass.getConstructor(String.class).newInstance("CONT"), signalHandler); - } catch (ClassNotFoundException cnfe) { - // sun.misc Signal handler classes don't exist - } catch (Exception e) { - // Ignore this one too, if the above failed, the signal API is incompatible with what we're expecting - } - } - - private static URL getInputRc() throws IOException { - String path = lib.jline.internal.Configuration.getString(JLINE_INPUTRC); - if (path == null) { - File f = new File(lib.jline.internal.Configuration.getUserHome(), INPUT_RC); - if (!f.exists()) { - f = new File(DEFAULT_INPUT_RC); - } - return f.toURI().toURL(); - } else { - return lib.jline.internal.Urls.create(path); - } - } - - public KeyMap getKeys() { - return consoleKeys.getKeys(); - } - - void setInput(final InputStream in) throws IOException { - this.escapeTimeout = lib.jline.internal.Configuration.getLong(JLINE_ESC_TIMEOUT, 100); - boolean nonBlockingEnabled = - escapeTimeout > 0L - && terminal.isSupported() - && in != null; - - /* - * If we had a non-blocking thread already going, then shut it down - * and start a new one. - */ - if (this.in != null) { - this.in.shutdown(); - } - - final InputStream wrapped = terminal.wrapInIfNeeded( in ); - - this.in = new NonBlockingInputStream(wrapped, nonBlockingEnabled); - this.reader = new InputStreamReader( this.in, encoding ); - } - - /** - * Shuts the console reader down. This method should be called when you - * have completed using the reader as it shuts down and cleans up resources - * that would otherwise be "leaked". - */ - @Override - public void close() { - if (in != null) { - in.shutdown(); - } - } - - /** - * Shuts the console reader down. The same as {@link #close()}. - * @deprecated Use {@link #close()} instead. - */ - @Deprecated - public void shutdown() { - this.close(); - } - - /** - * Shuts down the ConsoleReader if the JVM attempts to clean it up. - */ - @Override - protected void finalize() throws Throwable { - try { - close(); - } - finally { - super.finalize(); - } - } - - public InputStream getInput() { - return in; - } - - public Writer getOutput() { - return out; - } - - public lib.jline.Terminal getTerminal() { - return terminal; - } - - public CursorBuffer getCursorBuffer() { - return buf; - } - - public void setExpandEvents(final boolean expand) { - this.expandEvents = expand; - } - - public boolean getExpandEvents() { - return expandEvents; - } - - /** - * Enables or disables copy and paste detection. The effect of enabling this - * this setting is that when a tab is received immediately followed by another - * character, the tab will not be treated as a completion, but as a tab literal. - * @param onoff true if detection is enabled - */ - public void setCopyPasteDetection(final boolean onoff) { - copyPasteDetection = onoff; - } - - /** - * @return true if copy and paste detection is enabled. - */ - public boolean isCopyPasteDetectionEnabled() { - return copyPasteDetection; - } - - /** - * Set whether the console bell is enabled. - * - * @param enabled true if enabled; false otherwise - * @since 2.7 - */ - public void setBellEnabled(boolean enabled) { - this.bellEnabled = enabled; - } - - /** - * Get whether the console bell is enabled - * - * @return true if enabled; false otherwise - * @since 2.7 - */ - public boolean getBellEnabled() { - return bellEnabled; - } - - /** - * Set whether user interrupts (ctrl-C) are handled by having JLine - * throw {@link UserInterruptException} from {@link #readLine}. - * Otherwise, the JVM will handle {@code SIGINT} as normal, which - * usually causes it to exit. The default is {@code false}. - * - * @since 2.10 - */ - public void setHandleUserInterrupt(boolean enabled) - { - this.handleUserInterrupt = enabled; - } - - /** - * Get whether user interrupt handling is enabled - * - * @return true if enabled; false otherwise - * @since 2.10 - */ - public boolean getHandleUserInterrupt() - { - return handleUserInterrupt; - } - - /** - * Set wether literal next are handled by JLine. - * - * @since 2.13 - */ - public void setHandleLitteralNext(boolean handleLitteralNext) { - this.handleLitteralNext = handleLitteralNext; - } - - /** - * Get wether literal next are handled by JLine. - * - * @since 2.13 - */ - public boolean getHandleLitteralNext() { - return handleLitteralNext; - } - - /** - * Sets the string that will be used to start a comment when the - * insert-comment key is struck. - * @param commentBegin The begin comment string. - * @since 2.7 - */ - public void setCommentBegin(String commentBegin) { - this.commentBegin = commentBegin; - } - - /** - * @return the string that will be used to start a comment when the - * insert-comment key is struck. - * @since 2.7 - */ - public String getCommentBegin() { - String str = commentBegin; - - if (str == null) { - str = consoleKeys.getVariable("comment-begin"); - if (str == null) { - str = "#"; - } - } - return str; - } - - public void setPrompt(final String prompt) { - this.prompt = prompt; - this.promptLen = (prompt == null) ? 0 : wcwidth(Ansi.stripAnsi(lastLine(prompt)), 0); - } - - public String getPrompt() { - return prompt; - } - - /** - * Set the echo character. For example, to have "*" entered when a password is typed: - *

-     * myConsoleReader.setEchoCharacter(new Character('*'));
-     * 
- * Setting the character to null will restore normal character echoing.

- * Setting the character to Character.valueOf(0) 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; - } - - StringBuilder killed = new StringBuilder(); - - while (buf.cursor > 0) { - char c = buf.current(); - if (c == 0) { - break; - } - - killed.append(c); - backspace(); - } - - String copy = killed.reverse().toString(); - killRing.addBackwards(copy); - - return true; - } - - int wcwidth(CharSequence str, int pos) { - return wcwidth(str, 0, str.length(), pos); - } - - int wcwidth(CharSequence str, int start, int end, int pos) { - int cur = pos; - for (int i = start; i < end;) { - int ucs; - char c1 = str.charAt(i++); - if (!Character.isHighSurrogate(c1) || i >= end) { - ucs = c1; - } else { - char c2 = str.charAt(i); - if (Character.isLowSurrogate(c2)) { - i++; - ucs = Character.toCodePoint(c1, c2); - } else { - ucs = c1; - } - } - cur += wcwidth(ucs, cur); - } - return cur - pos; - } - - int wcwidth(int ucs, int pos) { - if (ucs == '\t') { - return nextTabStop(pos); - } else if (ucs < 32) { - return 2; - } else { - int w = WCWidth.wcwidth(ucs); - return w > 0 ? w : 0; - } - } - - int nextTabStop(int pos) { - int tabWidth = TAB_WIDTH; - int width = getTerminal().getWidth(); - int mod = (pos + tabWidth - 1) % tabWidth; - int npos = pos + tabWidth - mod; - return npos < width ? npos - pos : width - pos; - } - - int getCursorPosition() { - return promptLen + wcwidth(buf.buffer, 0, buf.cursor, promptLen); - } - - /** - * Returns the text after the last '\n'. - * prompt is returned if no '\n' characters are present. - * null is returned if prompt is null. - */ - private static 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; - } - - /** - * Move the cursor position to the specified absolute index. - */ - public boolean setCursorPosition(final int position) throws IOException { - if (position == buf.cursor) { - return true; - } - - 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(); // cleanUp 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)); - } - - private void setBufferKeepPos(final String buffer) throws IOException { - int pos = buf.cursor; - setBuffer(buffer); - setCursorPosition(pos); - } - - private void setBufferKeepPos(final CharSequence buffer) throws IOException { - setBufferKeepPos(String.valueOf(buffer)); - } - - /** - * Output put the prompt + the current buffer - */ - public void drawLine() throws IOException { - String prompt = getPrompt(); - if (prompt != null) { - rawPrint(prompt); - } - - fmtPrint(buf.buffer, 0, buf.cursor, promptLen); - - // force drawBuffer to check for weird wrap (after cleanUp screen) - drawBuffer(); - } - - /** - * Clear the line and redraw it. - */ - public void redrawLine() throws IOException { - tputs("carriage_return"); - 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(); - String historyLine = str; - - if (expandEvents) { - try { - str = expandEvents(str); - // all post-expansion occurrences of '!' must have been escaped, so re-add escape to each - historyLine = str.replace("!", "\\!"); - // only leading '^' results in expansion, so only re-add escape for that case - historyLine = historyLine.replaceAll("^\\^", "\\\\^"); - } catch(IllegalArgumentException e) { - lib.jline.internal.Log.error("Could not expand event", e); - beep(); - buf.clear(); - 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 cleanUp the mask after this call - if (str.length() > 0) { - if (mask == null && isHistoryEnabled()) { - history.add(historyLine); - } - 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 - */ - @SuppressWarnings("fallthrough") - 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 '\\': - // any '\!' should be considered an expansion escape, so skip expansion and strip the escape character - // a leading '\^' should be considered an expansion escape, so skip expansion and strip the escape character - // otherwise, add the escape - if (i + 1 < str.length()) { - char nextChar = str.charAt(i+1); - if (nextChar == '!' || (nextChar == '^' && i == 0)) { - c = nextChar; - i++; - } - } - sb.append(c); - break; - 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 '$': - if (history.size() == 0) { - throw new IllegalArgumentException("!$: event not found"); - } - String previous = history.get(history.index() - 1).toString().trim(); - int lastSpace = previous.lastIndexOf(' '); - if(lastSpace != -1) { - rep = previous.substring(lastSpace+1); - } else { - rep = previous; - } - 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 > 0 && 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 - 1)).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)) { - fmtPrint(result, getCursorPosition()); - println(); - flush(); - } - return result; - - } - - /** - * Write out the specified string to the buffer and the output stream. - */ - public void putString(final CharSequence str) throws IOException { - int pos = getCursorPosition(); - buf.write(str); - if (mask == null) { - // no masking - fmtPrint(str, pos); - } else if (mask == NULL_MASK) { - // don't print anything - } else { - rawPrint(mask, str.length()); - } - 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 cleanUp after the end of the buffer - */ - private void drawBuffer(final int clear) throws IOException { - // debug ("drawBuffer: " + cleanUp); - int nbChars = buf.length() - buf.cursor; - if (buf.cursor != buf.length() || clear != 0) { - if (mask != null) { - if (mask != NULL_MASK) { - rawPrint(mask, nbChars); - } else { - nbChars = 0; - } - } else { - fmtPrint(buf.buffer, buf.cursor, buf.length()); - } - } - int cursorPos = promptLen + wcwidth(buf.buffer, 0, buf.length(), promptLen); - if (terminal.hasWeirdWrap() && !cursorOk) { - int width = terminal.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 (cursorPos > 0 && (cursorPos % width == 0)) { - // the following workaround is reverse-engineered from looking - // at what bash sent to the terminal in the same situation - rawPrint(' '); // move cursor to next line by printing dummy space - tputs("carriage_return"); // CR / not newline. - } - cursorOk = true; - } - clearAhead(clear, cursorPos); - back(nbChars); - } - - /** - * 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 cleanUp - * @param pos the current screen cursor position - */ - private void clearAhead(int num, final int pos) throws IOException { - if (num == 0) return; - - int width = terminal.getWidth(); - // Use kill line - if (terminal.getStringCapability("clr_eol") != null) { - int cur = pos; - int c0 = cur % width; - // Erase end of current line - int nb = Math.min(num, width - c0); - tputs("clr_eol"); - num -= nb; - // Loop - while (num > 0) { - // Move to beginning of next line - int prev = cur; - cur = cur - cur % width + width; - moveCursorFromTo(prev, cur); - // Erase - nb = Math.min(num, width); - tputs("clr_eol"); - num -= nb; - } - moveCursorFromTo(cur, pos); - } - // Terminal does not wrap on the right margin - else if (!terminal.getBooleanCapability("auto_right_margin")) { - int cur = pos; - int c0 = cur % width; - // Erase end of current line - int nb = Math.min(num, width - c0); - rawPrint(' ', nb); - num -= nb; - cur += nb; - // Loop - while (num > 0) { - // Move to beginning of next line - moveCursorFromTo(cur, ++cur); - // Erase - nb = Math.min(num, width); - rawPrint(' ', nb); - num -= nb; - cur += nb; - } - moveCursorFromTo(cur, pos); - } - // Simple erasure - else { - rawPrint(' ', num); - moveCursorFromTo(pos + num, pos); - } - } - - /** - * Move the visual cursor backward without modifying the buffer cursor. - */ - protected void back(final int num) throws IOException { - if (num == 0) return; - int i0 = promptLen + wcwidth(buf.buffer, 0, buf.cursor, promptLen); - int i1 = i0 + ((mask != null) ? num : wcwidth(buf.buffer, buf.cursor, buf.cursor + num, i0)); - moveCursorFromTo(i1, i0); - } - - /** - * 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 num backspaces. - * - * @return the number of characters backed up - */ - private int backspace(final int num) throws IOException { - if (buf.cursor == 0) { - return 0; - } - - int count = - moveCursor(-num); - int clear = wcwidth(buf.buffer, buf.cursor, buf.cursor + count, getCursorPosition()); - buf.buffer.delete(buf.cursor, buf.cursor + count); - - drawBuffer(clear); - return count; - } - - /** - * Issue a backspace. - * - * @return true if successful - */ - public boolean backspace() throws IOException { - return backspace(1) == 1; - } - - protected boolean moveToEnd() throws IOException { - if (buf.cursor == buf.length()) { - return true; - } - 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; - } - - /** - * This method is calling while doing a delete-to ("d"), change-to ("c"), - * or yank-to ("y") and it filters out only those movement operations - * that are allowable during those operations. Any operation that isn't - * allow drops you back into movement mode. - * - * @param op The incoming operation to remap - * @return The remaped operation - */ - private lib.jline.console.Operation viDeleteChangeYankToRemap (lib.jline.console.Operation op) { - switch (op) { - case VI_EOF_MAYBE: - case ABORT: - case BACKWARD_CHAR: - case FORWARD_CHAR: - case END_OF_LINE: - case VI_MATCH: - case VI_BEGINNING_OF_LINE_OR_ARG_DIGIT: - case VI_ARG_DIGIT: - case VI_PREV_WORD: - case VI_END_WORD: - case VI_CHAR_SEARCH: - case VI_NEXT_WORD: - case VI_FIRST_PRINT: - case VI_GOTO_MARK: - case VI_COLUMN: - case VI_DELETE_TO: - case VI_YANK_TO: - case VI_CHANGE_TO: - return op; - - default: - return lib.jline.console.Operation.VI_MOVEMENT_MODE; - } - } - - /** - * Deletes the previous character from the cursor position - * @param count number of times to do it. - * @return true if it was done. - */ - private boolean viRubout(int count) throws IOException { - boolean ok = true; - for (int i = 0; ok && i < count; i++) { - ok = backspace(); - } - return ok; - } - - /** - * Deletes the character you are sitting on and sucks the rest of - * the line in from the right. - * @param count Number of times to perform the operation. - * @return true if its works, false if it didn't - */ - private boolean viDelete(int count) throws IOException { - boolean ok = true; - for (int i = 0; ok && i < count; i++) { - ok = deleteCurrentCharacter(); - } - return ok; - } - - /** - * Switches the case of the current character from upper to lower - * or lower to upper as necessary and advances the cursor one - * position to the right. - * @param count The number of times to repeat - * @return true if it completed successfully, false if not all - * case changes could be completed. - */ - private boolean viChangeCase(int count) throws IOException { - boolean ok = true; - for (int i = 0; ok && i < count; i++) { - - ok = buf.cursor < buf.buffer.length (); - if (ok) { - char ch = buf.buffer.charAt(buf.cursor); - if (Character.isUpperCase(ch)) { - ch = Character.toLowerCase(ch); - } - else if (Character.isLowerCase(ch)) { - ch = Character.toUpperCase(ch); - } - buf.buffer.setCharAt(buf.cursor, ch); - drawBuffer(1); - moveCursor(1); - } - } - return ok; - } - - /** - * Implements the vi change character command (in move-mode "r" - * followed by the character to change to). - * @param count Number of times to perform the action - * @param c The character to change to - * @return Whether or not there were problems encountered - */ - private boolean viChangeChar(int count, int c) throws IOException { - // EOF, ESC, or CTRL-C aborts. - if (c < 0 || c == '\033' || c == '\003') { - return true; - } - - boolean ok = true; - for (int i = 0; ok && i < count; i++) { - ok = buf.cursor < buf.buffer.length (); - if (ok) { - buf.buffer.setCharAt(buf.cursor, (char) c); - drawBuffer(1); - if (i < (count-1)) { - moveCursor(1); - } - } - } - return ok; - } - - /** - * This is a close facsimile of the actual vi previous word logic. In - * actual vi words are determined by boundaries of identity characterse. - * This logic is a bit more simple and simply looks at white space or - * digits or characters. It should be revised at some point. - * - * @param count number of iterations - * @return true if the move was successful, false otherwise - */ - private boolean viPreviousWord(int count) throws IOException { - boolean ok = true; - if (buf.cursor == 0) { - return false; - } - - int pos = buf.cursor - 1; - for (int i = 0; pos > 0 && i < count; i++) { - // If we are on white space, then move back. - while (pos > 0 && isWhitespace(buf.buffer.charAt(pos))) { - --pos; - } - - while (pos > 0 && !isDelimiter(buf.buffer.charAt(pos-1))) { - --pos; - } - - if (pos > 0 && i < (count-1)) { - --pos; - } - } - setCursorPosition(pos); - return ok; - } - - /** - * Performs the vi "delete-to" action, deleting characters between a given - * span of the input line. - * @param startPos The start position - * @param endPos The end position. - * @param isChange If true, then the delete is part of a change operationg - * (e.g. "c$" is change-to-end-of line, so we first must delete to end - * of line to start the change - * @return true if it succeeded, false otherwise - */ - private boolean viDeleteTo(int startPos, int endPos, boolean isChange) throws IOException { - if (startPos == endPos) { - return true; - } - - if (endPos < startPos) { - int tmp = endPos; - endPos = startPos; - startPos = tmp; - } - - setCursorPosition(startPos); - buf.cursor = startPos; - buf.buffer.delete(startPos, endPos); - drawBuffer(endPos - startPos); - - // If we are doing a delete operation (e.g. "d$") then don't leave the - // cursor dangling off the end. In reality the "isChange" flag is silly - // what is really happening is that if we are in "move-mode" then the - // cursor can't be moved off the end of the line, but in "edit-mode" it - // is ok, but I have no easy way of knowing which mode we are in. - if (! isChange && startPos > 0 && startPos == buf.length()) { - moveCursor(-1); - } - return true; - } - - /** - * Implement the "vi" yank-to operation. This operation allows you - * to yank the contents of the current line based upon a move operation, - * for exaple "yw" yanks the current word, "3yw" yanks 3 words, etc. - * - * @param startPos The starting position from which to yank - * @param endPos The ending position to which to yank - * @return true if the yank succeeded - */ - private boolean viYankTo(int startPos, int endPos) throws IOException { - int cursorPos = startPos; - - if (endPos < startPos) { - int tmp = endPos; - endPos = startPos; - startPos = tmp; - } - - if (startPos == endPos) { - yankBuffer = ""; - return true; - } - - yankBuffer = buf.buffer.substring(startPos, endPos); - - /* - * It was a movement command that moved the cursor to find the - * end position, so put the cursor back where it started. - */ - setCursorPosition(cursorPos); - return true; - } - - /** - * Pasts the yank buffer to the right of the current cursor position - * and moves the cursor to the end of the pasted region. - * - * @param count Number of times to perform the operation. - * @return true if it worked, false otherwise - */ - private boolean viPut(int count) throws IOException { - if (yankBuffer.length () == 0) { - return true; - } - if (buf.cursor < buf.buffer.length ()) { - moveCursor(1); - } - for (int i = 0; i < count; i++) { - putString(yankBuffer); - } - moveCursor(-1); - return true; - } - - /** - * Searches forward of the current position for a character and moves - * the cursor onto it. - * @param count Number of times to repeat the process. - * @param ch The character to search for - * @return true if the char was found, false otherwise - */ - private boolean viCharSearch(int count, int invokeChar, int ch) throws IOException { - if (ch < 0 || invokeChar < 0) { - return false; - } - - char searchChar = (char)ch; - boolean isForward; - boolean stopBefore; - - /* - * The character stuff turns out to be hairy. Here is how it works: - * f - search forward for ch - * F - search backward for ch - * t - search forward for ch, but stop just before the match - * T - search backward for ch, but stop just after the match - * ; - After [fFtT;], repeat the last search, after ',' reverse it - * , - After [fFtT;], reverse the last search, after ',' repeat it - */ - if (invokeChar == ';' || invokeChar == ',') { - // No recent search done? Then bail - if (charSearchChar == 0) { - return false; - } - - // Reverse direction if switching between ',' and ';' - if (charSearchLastInvokeChar == ';' || charSearchLastInvokeChar == ',') { - if (charSearchLastInvokeChar != invokeChar) { - charSearchFirstInvokeChar = switchCase(charSearchFirstInvokeChar); - } - } - else { - if (invokeChar == ',') { - charSearchFirstInvokeChar = switchCase(charSearchFirstInvokeChar); - } - } - - searchChar = charSearchChar; - } - else { - charSearchChar = searchChar; - charSearchFirstInvokeChar = (char) invokeChar; - } - - charSearchLastInvokeChar = (char)invokeChar; - - isForward = Character.isLowerCase(charSearchFirstInvokeChar); - stopBefore = (Character.toLowerCase(charSearchFirstInvokeChar) == 't'); - - boolean ok = false; - - if (isForward) { - while (count-- > 0) { - int pos = buf.cursor + 1; - while (pos < buf.buffer.length()) { - if (buf.buffer.charAt(pos) == searchChar) { - setCursorPosition(pos); - ok = true; - break; - } - ++pos; - } - } - - if (ok) { - if (stopBefore) - moveCursor(-1); - - /* - * When in yank-to, move-to, del-to state we actually want to - * go to the character after the one we landed on to make sure - * that the character we ended up on is included in the - * operation - */ - if (isInViMoveOperationState()) { - moveCursor(1); - } - } - } - else { - while (count-- > 0) { - int pos = buf.cursor - 1; - while (pos >= 0) { - if (buf.buffer.charAt(pos) == searchChar) { - setCursorPosition(pos); - ok = true; - break; - } - --pos; - } - } - - if (ok && stopBefore) - moveCursor(1); - } - - return ok; - } - - private static char switchCase(char ch) { - if (Character.isUpperCase(ch)) { - return Character.toLowerCase(ch); - } - return Character.toUpperCase(ch); - } - - /** - * @return true if line reader is in the middle of doing a change-to - * delete-to or yank-to. - */ - private final boolean isInViMoveOperationState() { - return state == State.VI_CHANGE_TO - || state == State.VI_DELETE_TO - || state == State.VI_YANK_TO; - } - - /** - * This is a close facsimile of the actual vi next word logic. - * As with viPreviousWord() this probably needs to be improved - * at some point. - * - * @param count number of iterations - * @return true if the move was successful, false otherwise - */ - private boolean viNextWord(int count) throws IOException { - int pos = buf.cursor; - int end = buf.buffer.length(); - - for (int i = 0; pos < end && i < count; i++) { - // Skip over letter/digits - while (pos < end && !isDelimiter(buf.buffer.charAt(pos))) { - ++pos; - } - - /* - * Don't you love special cases? During delete-to and yank-to - * operations the word movement is normal. However, during a - * change-to, the trailing spaces behind the last word are - * left in tact. - */ - if (i < (count-1) || !(state == State.VI_CHANGE_TO)) { - while (pos < end && isDelimiter(buf.buffer.charAt(pos))) { - ++pos; - } - } - } - - setCursorPosition(pos); - return true; - } - - /** - * Implements a close facsimile of the vi end-of-word movement. - * If the character is on white space, it takes you to the end - * of the next word. If it is on the last character of a word - * it takes you to the next of the next word. Any other character - * of a word, takes you to the end of the current word. - * - * @param count Number of times to repeat the action - * @return true if it worked. - */ - private boolean viEndWord(int count) throws IOException { - int pos = buf.cursor; - int end = buf.buffer.length(); - - for (int i = 0; pos < end && i < count; i++) { - if (pos < (end-1) - && !isDelimiter(buf.buffer.charAt(pos)) - && isDelimiter(buf.buffer.charAt (pos+1))) { - ++pos; - } - - // If we are on white space, then move back. - while (pos < end && isDelimiter(buf.buffer.charAt(pos))) { - ++pos; - } - - while (pos < (end-1) && !isDelimiter(buf.buffer.charAt(pos+1))) { - ++pos; - } - } - setCursorPosition(pos); - return true; - } - - private boolean previousWord() throws IOException { - while (isDelimiter(buf.current()) && (moveCursor(-1) != 0)) { - // nothing - } - - while (!isDelimiter(buf.current()) && (moveCursor(-1) != 0)) { - // nothing - } - - return true; - } - - private boolean nextWord() throws IOException { - while (isDelimiter(buf.nextChar()) && (moveCursor(1) != 0)) { - // nothing - } - - while (!isDelimiter(buf.nextChar()) && (moveCursor(1) != 0)) { - // nothing - } - - return true; - } - - /** - * Deletes to the beginning of the word that the cursor is sitting on. - * If the cursor is on white-space, it deletes that and to the beginning - * of the word before it. If the user is not on a word or whitespace - * it deletes up to the end of the previous word. - * - * @param count Number of times to perform the operation - * @return true if it worked, false if you tried to delete too many words - */ - private boolean unixWordRubout(int count) throws IOException { - boolean success = true; - StringBuilder killed = new StringBuilder(); - - for (; count > 0; --count) { - if (buf.cursor == 0) { - success = false; - break; - } - - while (isWhitespace(buf.current())) { - char c = buf.current(); - if (c == 0) { - break; - } - - killed.append(c); - backspace(); - } - - while (!isWhitespace(buf.current())) { - char c = buf.current(); - if (c == 0) { - break; - } - - killed.append(c); - backspace(); - } - } - - String copy = killed.reverse().toString(); - killRing.addBackwards(copy); - - return success; - } - - private String insertComment(boolean isViMode) throws IOException { - String comment = this.getCommentBegin(); - setCursorPosition(0); - putString(comment); - if (isViMode) { - consoleKeys.setKeyMap(KeyMap.VI_INSERT); - } - return accept(); - } - - /** - * Implements vi search ("/" or "?"). - */ - @SuppressWarnings("fallthrough") - private int viSearch(char searchChar) throws IOException { - boolean isForward = (searchChar == '/'); - - /* - * This is a little gross, I'm sure there is a more appropriate way - * of saving and restoring state. - */ - CursorBuffer origBuffer = buf.copy(); - - // Clear the contents of the current line and - setCursorPosition (0); - killLine(); - - // Our new "prompt" is the character that got us into search mode. - putString(Character.toString(searchChar)); - flush(); - - boolean isAborted = false; - boolean isComplete = false; - - /* - * Readline doesn't seem to do any special character map handling - * here, so I think we are safe. - */ - int ch = -1; - while (!isAborted && !isComplete && (ch = readCharacter()) != -1) { - switch (ch) { - case '\033': // ESC - /* - * The ESC behavior doesn't appear to be readline behavior, - * but it is a little tweak of my own. I like it. - */ - isAborted = true; - break; - case '\010': // Backspace - case '\177': // Delete - backspace(); - /* - * Backspacing through the "prompt" aborts the search. - */ - if (buf.cursor == 0) { - isAborted = true; - } - break; - case '\012': // NL - case '\015': // CR - isComplete = true; - break; - default: - putString(Character.toString((char) ch)); - } - - flush(); - } - - // If we aborted, then put ourself at the end of the original buffer. - if (ch == -1 || isAborted) { - setCursorPosition(0); - killLine(); - putString(origBuffer.buffer); - setCursorPosition(origBuffer.cursor); - return -1; - } - - /* - * The first character of the buffer was the search character itself - * so we discard it. - */ - String searchTerm = buf.buffer.substring(1); - int idx = -1; - - /* - * The semantics of the history thing is gross when you want to - * explicitly iterate over entries (without an iterator) as size() - * returns the actual number of entries in the list but get() - * doesn't work the way you think. - */ - int end = history.index(); - int start = (end <= history.size()) ? 0 : end - history.size(); - - if (isForward) { - for (int i = start; i < end; i++) { - if (history.get(i).toString().contains(searchTerm)) { - idx = i; - break; - } - } - } - else { - for (int i = end-1; i >= start; i--) { - if (history.get(i).toString().contains(searchTerm)) { - idx = i; - break; - } - } - } - - /* - * No match? Then restore what we were working on, but make sure - * the cursor is at the beginning of the line. - */ - if (idx == -1) { - setCursorPosition(0); - killLine(); - putString(origBuffer.buffer); - setCursorPosition(0); - return -1; - } - - /* - * Show the match. - */ - setCursorPosition(0); - killLine(); - putString(history.get(idx)); - setCursorPosition(0); - flush(); - - /* - * While searching really only the "n" and "N" keys are interpreted - * as movement, any other key is treated as if you are editing the - * line with it, so we return it back up to the caller for interpretation. - */ - isComplete = false; - while (!isComplete && (ch = readCharacter()) != -1) { - boolean forward = isForward; - switch (ch) { - case 'p': case 'P': - forward = !isForward; - // Fallthru - case 'n': case 'N': - boolean isMatch = false; - if (forward) { - for (int i = idx+1; !isMatch && i < end; i++) { - if (history.get(i).toString().contains(searchTerm)) { - idx = i; - isMatch = true; - } - } - } - else { - for (int i = idx - 1; !isMatch && i >= start; i--) { - if (history.get(i).toString().contains(searchTerm)) { - idx = i; - isMatch = true; - } - } - } - if (isMatch) { - setCursorPosition(0); - killLine(); - putString(history.get(idx)); - setCursorPosition(0); - } - break; - default: - isComplete = true; - } - flush(); - } - - /* - * Complete? - */ - return ch; - } - - public void setParenBlinkTimeout(int timeout) { - parenBlinkTimeout = timeout; - } - - private void insertClose(String s) throws IOException { - putString(s); - int closePosition = buf.cursor; - - moveCursor(-1); - viMatch(); - - - if (in.isNonBlockingEnabled()) { - in.peek(parenBlinkTimeout); - } - - setCursorPosition(closePosition); - flush(); - } - - /** - * Implements vi style bracket matching ("%" command). The matching - * bracket for the current bracket type that you are sitting on is matched. - * The logic works like so: - * @return true if it worked, false if the cursor was not on a bracket - * character or if there was no matching bracket. - */ - private boolean viMatch() throws IOException { - int pos = buf.cursor; - - if (pos == buf.length()) { - return false; - } - - int type = getBracketType(buf.buffer.charAt (pos)); - int move = (type < 0) ? -1 : 1; - int count = 1; - - if (type == 0) - return false; - - while (count > 0) { - pos += move; - - // Fell off the start or end. - if (pos < 0 || pos >= buf.buffer.length ()) { - return false; - } - - int curType = getBracketType(buf.buffer.charAt (pos)); - if (curType == type) { - ++count; - } - else if (curType == -type) { - --count; - } - } - - /* - * Slight adjustment for delete-to, yank-to, change-to to ensure - * that the matching paren is consumed - */ - if (move > 0 && isInViMoveOperationState()) - ++pos; - - setCursorPosition(pos); - flush(); - return true; - } - - /** - * Given a character determines what type of bracket it is (paren, - * square, curly, or none). - * @param ch The character to check - * @return 1 is square, 2 curly, 3 parent, or zero for none. The value - * will be negated if it is the closing form of the bracket. - */ - private static int getBracketType (char ch) { - switch (ch) { - case '[': return 1; - case ']': return -1; - case '{': return 2; - case '}': return -2; - case '(': return 3; - case ')': return -3; - default: - return 0; - } - } - - private boolean deletePreviousWord() throws IOException { - StringBuilder killed = new StringBuilder(); - char c; - - while (isDelimiter((c = buf.current()))) { - if (c == 0) { - break; - } - - killed.append(c); - backspace(); - } - - while (!isDelimiter((c = buf.current()))) { - if (c == 0) { - break; - } - - killed.append(c); - backspace(); - } - - String copy = killed.reverse().toString(); - killRing.addBackwards(copy); - return true; - } - - private boolean deleteNextWord() throws IOException { - StringBuilder killed = new StringBuilder(); - char c; - - while (isDelimiter((c = buf.nextChar()))) { - if (c == 0) { - break; - } - killed.append(c); - delete(); - } - - while (!isDelimiter((c = buf.nextChar()))) { - if (c == 0) { - break; - } - killed.append(c); - delete(); - } - - String copy = killed.toString(); - killRing.add(copy); - - return true; - } - - private boolean capitalizeWord() throws IOException { - boolean first = true; - int i = 1; - char c; - while (buf.cursor + i - 1< buf.length() && !isDelimiter((c = buf.buffer.charAt(buf.cursor + i - 1)))) { - buf.buffer.setCharAt(buf.cursor + i - 1, first ? Character.toUpperCase(c) : Character.toLowerCase(c)); - first = false; - i++; - } - drawBuffer(); - moveCursor(i - 1); - return true; - } - - private boolean upCaseWord() throws IOException { - int i = 1; - char c; - while (buf.cursor + i - 1 < buf.length() && !isDelimiter((c = buf.buffer.charAt(buf.cursor + i - 1)))) { - buf.buffer.setCharAt(buf.cursor + i - 1, Character.toUpperCase(c)); - i++; - } - drawBuffer(); - moveCursor(i - 1); - return true; - } - - private boolean downCaseWord() throws IOException { - int i = 1; - char c; - while (buf.cursor + i - 1 < buf.length() && !isDelimiter((c = buf.buffer.charAt(buf.cursor + i - 1)))) { - buf.buffer.setCharAt(buf.cursor + i - 1, Character.toLowerCase(c)); - i++; - } - drawBuffer(); - moveCursor(i - 1); - return true; - } - - /** - * Performs character transpose. The character prior to the cursor and the - * character under the cursor are swapped and the cursor is advanced one - * character unless you are already at the end of the line. - * - * @param count The number of times to perform the transpose - * @return true if the operation succeeded, false otherwise (e.g. transpose - * cannot happen at the beginning of the line). - */ - private boolean transposeChars(int count) throws IOException { - for (; count > 0; --count) { - if (buf.cursor == 0 || buf.cursor == buf.buffer.length()) { - return false; - } - - int first = buf.cursor-1; - int second = buf.cursor; - - char tmp = buf.buffer.charAt (first); - buf.buffer.setCharAt(first, buf.buffer.charAt(second)); - buf.buffer.setCharAt(second, tmp); - - // This could be done more efficiently by only re-drawing at the end. - moveInternal(-1); - drawBuffer(); - moveInternal(2); - } - - return true; - } - - public boolean isKeyMap(String name) { - // Current keymap. - KeyMap map = consoleKeys.getKeys(); - KeyMap mapByName = consoleKeys.getKeyMaps().get(name); - - if (mapByName == null) - return false; - - /* - * This may not be safe to do, but there doesn't appear to be a - * clean way to find this information out. - */ - return map == mapByName; - } - - - /** - * The equivalent of hitting <RET>. The line is considered - * complete and is returned. - * - * @return The completed line of text. - */ - public String accept() throws IOException { - moveToEnd(); - println(); // output newline - flush(); - return finishBuffer(); - } - - private void abort() throws IOException { - beep(); - buf.clear(); - println(); - redrawLine(); - } - - /** - * Move the cursor where characters. - * - * @param num If less than 0, move abs(where) to the left, otherwise move where 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 where 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; - - int i0; - int i1; - if (mask == null) { - if (where < 0) { - i1 = promptLen + wcwidth(buf.buffer, 0, buf.cursor, promptLen); - i0 = i1 + wcwidth(buf.buffer, buf.cursor, buf.cursor - where, i1); - } else { - i0 = promptLen + wcwidth(buf.buffer, 0, buf.cursor - where, promptLen); - i1 = i0 + wcwidth(buf.buffer, buf.cursor - where, buf.cursor, i0); - } - } else if (mask != NULL_MASK) { - i1 = promptLen + buf.cursor; - i0 = i1 - where; - } else { - return; - } - moveCursorFromTo(i0, i1); - } - - private void moveCursorFromTo(int i0, int i1) throws IOException { - if (i0 == i1) return; - int width = getTerminal().getWidth(); - int l0 = i0 / width; - int c0 = i0 % width; - int l1 = i1 / width; - int c1 = i1 % width; - if (l0 == l1 + 1) { - if (!tputs("cursor_up")) { - tputs("parm_up_cursor", 1); - } - } else if (l0 > l1) { - if (!tputs("parm_up_cursor", l0 - l1)) { - for (int i = l1; i < l0; i++) { - tputs("cursor_up"); - } - } - } else if (l0 < l1) { - tputs("carriage_return"); - rawPrint('\n', l1 - l0); - c0 = 0; - } - if (c0 == c1 - 1) { - tputs("cursor_right"); - } else if (c0 == c1 + 1) { - tputs("cursor_left"); - } else if (c0 < c1) { - if (!tputs("parm_right_cursor", c1 - c0)) { - for (int i = c0; i < c1; i++) { - tputs("cursor_right"); - } - } - } else if (c0 > c1) { - if (!tputs("parm_left_cursor", c0 - c1)) { - for (int i = c1; i < c0; i++) { - tputs("cursor_left"); - } - } - } - cursorOk = true; - } - - /** - * Read a character from the console. - * - * @return the character, or -1 if an EOF is received. - */ - public int readCharacter() throws IOException { - return readCharacter(false); - } - - /** - * Read a character from the console. If boolean parameter is "true", it will check whether the keystroke was an "alt-" key combination, and - * if so add 1000 to the value returned. Better way...? - * - * @return the character, or -1 if an EOF is received. - */ - public int readCharacter(boolean checkForAltKeyCombo) throws IOException { - int c = reader.read(); - if (c >= 0) { - lib.jline.internal.Log.trace("Keystroke: ", c); - // cleanUp any echo characters - if (terminal.isSupported()) { - clearEcho(c); - } - if (c == ESCAPE && checkForAltKeyCombo && in.peek(escapeTimeout) >= 32) { - /* When ESC is encountered and there is a pending - * character in the pushback queue, then it seems to be - * an Alt-[key] combination. Is this true, cross-platform? - * It's working for me on Debian GNU/Linux at the moment anyway. - * I removed the "isNonBlockingEnabled" check, though it was - * in the similar code in "readLine(String prompt, final Character mask)" (way down), - * as I am not sure / didn't look up what it's about, and things are working so far w/o it. - */ - int next = reader.read(); - // with research, there's probably a much cleaner way to do this, but, this is now it flags an Alt key combination for now: - next = next + 1000; - return next; - } - } - 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 (!terminal.isEchoEnabled()) { - return 0; - } - - // otherwise, cleanUp - int pos = getCursorPosition(); - int num = wcwidth(c, pos); - moveCursorFromTo(pos + num, pos); - drawBuffer(num); - - return num; - } - - public int readCharacter(final char... allowed) throws IOException { - return readCharacter(false, allowed); - } - - public int readCharacter(boolean checkForAltKeyCombo, 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) readCharacter(checkForAltKeyCombo)) < 0) { - // nothing - } - - return c; - } - - /** - * Read from the input stream and decode an operation from the key map. - * - * The input stream will be read character by character until a matching - * binding can be found. Characters that can't possibly be matched to - * any binding will be discarded. - * - * @param keys the KeyMap to use for decoding the input stream - * @return the decoded binding or null if the end of - * stream has been reached - */ - public Object readBinding(KeyMap keys) throws IOException { - Object o; - opBuffer.setLength(0); - do { - int c = pushBackChar.isEmpty() ? readCharacter() : pushBackChar.pop(); - if (c == -1) { - return null; - } - opBuffer.appendCodePoint(c); - - if (recording) { - macro += new String(Character.toChars(c)); - } - - if (quotedInsert) { - o = lib.jline.console.Operation.SELF_INSERT; - quotedInsert = false; - } else { - o = keys.getBound(opBuffer); - } - - /* - * The kill ring keeps record of whether or not the - * previous command was a yank or a kill. We reset - * that state here if needed. - */ - if (!recording && !(o instanceof KeyMap)) { - if (o != lib.jline.console.Operation.YANK_POP && o != lib.jline.console.Operation.YANK) { - killRing.resetLastYank(); - } - if (o != lib.jline.console.Operation.KILL_LINE && o != lib.jline.console.Operation.KILL_WHOLE_LINE - && o != lib.jline.console.Operation.BACKWARD_KILL_WORD && o != lib.jline.console.Operation.KILL_WORD - && o != lib.jline.console.Operation.UNIX_LINE_DISCARD && o != lib.jline.console.Operation.UNIX_WORD_RUBOUT) { - killRing.resetLastKill(); - } - } - - if (o == lib.jline.console.Operation.DO_LOWERCASE_VERSION) { - opBuffer.setLength(opBuffer.length() - 1); - opBuffer.append(Character.toLowerCase((char) c)); - o = keys.getBound(opBuffer); - } - - /* - * A KeyMap indicates that the key that was struck has a - * number of keys that can follow it as indicated in the - * map. This is used primarily for Emacs style ESC-META-x - * lookups. Since more keys must follow, go back to waiting - * for the next key. - */ - if (o instanceof KeyMap) { - /* - * The ESC key (#27) is special in that it is ambiguous until - * you know what is coming next. The ESC could be a literal - * escape, like the user entering vi-move mode, or it could - * be part of a terminal control sequence. The following - * logic attempts to disambiguate things in the same - * fashion as regular vi or readline. - * - * When ESC is encountered and there is no other pending - * character in the pushback queue, then attempt to peek - * into the input stream (if the feature is enabled) for - * 150ms. If nothing else is coming, then assume it is - * not a terminal control sequence, but a raw escape. - */ - if (c == ESCAPE - && pushBackChar.isEmpty() - && in.isNonBlockingEnabled() - && in.peek(escapeTimeout) == READ_EXPIRED) { - o = ((KeyMap) o).getAnotherKey(); - if (o == null || o instanceof KeyMap) { - continue; - } - opBuffer.setLength(0); - } else { - continue; - } - } - - /* - * If we didn't find a binding for the key and there is - * more than one character accumulated then start checking - * the largest span of characters from the beginning to - * see if there is a binding for them. - * - * For example if our buffer has ESC,CTRL-M,C the getBound() - * called previously indicated that there is no binding for - * this sequence, so this then checks ESC,CTRL-M, and failing - * that, just ESC. Each keystroke that is pealed off the end - * during these tests is stuffed onto the pushback buffer so - * they won't be lost. - * - * If there is no binding found, then we go back to waiting for - * input. - */ - while (o == null && opBuffer.length() > 0) { - c = opBuffer.charAt(opBuffer.length() - 1); - opBuffer.setLength(opBuffer.length() - 1); - Object o2 = keys.getBound(opBuffer); - if (o2 instanceof KeyMap) { - o = ((KeyMap) o2).getAnotherKey(); - if (o == null) { - continue; - } else { - pushBackChar.push((char) c); - } - } - } - - } while (o == null || o instanceof KeyMap); - - return o; - } - - public String getLastBinding() { - return opBuffer.toString(); - } - - // - // Key Bindings - // - - public static final String JLINE_COMPLETION_THRESHOLD = "jline.completion.threshold"; - - // - // 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 in {@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., CTRL-D - * was pressed). - */ - public String readLine(String prompt, final Character mask) throws IOException { - return readLine(prompt, mask, null); - } - - /** - * Sets the current keymap by name. Supported keymaps are "emacs", - * "vi-insert", "vi-move". - * @param name The name of the keymap to switch to - * @return true if the keymap was set, or false if the keymap is - * not recognized. - */ - public boolean setKeyMap(String name) { - return consoleKeys.setKeyMap(name); - } - - /** - * Returns the name of the current key mapping. - * @return the name of the key mapping. This will be the canonical name - * of the current mode of the key map and may not reflect the name that - * was used with {@link #setKeyMap(String)}. - */ - public String getKeyMap() { - return consoleKeys.getKeys().getName(); - } - - /** - * Read a line from the in {@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., CTRL-D - * was pressed). - */ - public String readLine(String prompt, final Character mask, String buffer) throws IOException { - // prompt may be null - // mask may be null - // buffer may be null - - /* - * This is the accumulator for VI-mode repeat count. That is, while in - * move mode, if you type 30x it will delete 30 characters. This is - * where the "30" is accumulated until the command is struck. - */ - int repeatCount = 0; - - // FIXME: This blows, each call to readLine will reset the console's state which doesn't seem very nice. - this.mask = mask != null ? mask : this.echoCharacter; - if (prompt != null) { - setPrompt(prompt); - } - else { - prompt = getPrompt(); - } - - try { - if (buffer != null) { - buf.write(buffer); - } - - if (!terminal.isSupported()) { - beforeReadLine(prompt, mask); - } - - if (buffer != null && buffer.length() > 0 - || prompt != null && prompt.length() > 0) { - drawLine(); - out.flush(); - } - - // if the terminal is unsupported, just use plain-java reading - if (!terminal.isSupported()) { - return readLineSimple(); - } - - if (handleUserInterrupt) { - terminal.disableInterruptCharacter(); - } - if (handleLitteralNext && (terminal instanceof lib.jline.UnixTerminal)) { - ((lib.jline.UnixTerminal) terminal).disableLitteralNextCharacter(); - } - - String originalPrompt = this.prompt; - - state = State.NORMAL; - - boolean success = true; - - pushBackChar.clear(); - while (true) { - - Object o = readBinding(getKeys()); - if (o == null) { - return null; - } - int c = 0; - if (opBuffer.length() > 0) { - c = opBuffer.codePointBefore(opBuffer.length()); - } - lib.jline.internal.Log.trace("Binding: ", o); - - - // Handle macros - if (o instanceof String) { - String macro = (String) o; - for (int i = 0; i < macro.length(); i++) { - pushBackChar.push(macro.charAt(macro.length() - 1 - i)); - } - opBuffer.setLength(0); - continue; - } - - // Handle custom callbacks - if (o instanceof ActionListener) { - ((ActionListener) o).actionPerformed(null); - opBuffer.setLength(0); - continue; - } - - CursorBuffer oldBuf = new CursorBuffer(); - oldBuf.buffer.append(buf.buffer); - oldBuf.cursor = buf.cursor; - - // 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 == State.SEARCH || state == State.FORWARD_SEARCH) { - int cursorDest = -1; - // TODO: check the isearch-terminators variable terminating the search - switch ( ((lib.jline.console.Operation) o )) { - case ABORT: - state = State.NORMAL; - buf.clear(); - buf.write(originalBuffer.buffer); - buf.cursor = originalBuffer.cursor; - break; - - case REVERSE_SEARCH_HISTORY: - state = State.SEARCH; - if (searchTerm.length() == 0) { - searchTerm.append(previousSearchTerm); - } - - if (searchIndex > 0) { - searchIndex = searchBackwards(searchTerm.toString(), searchIndex); - } - break; - - case FORWARD_SEARCH_HISTORY: - state = State.FORWARD_SEARCH; - if (searchTerm.length() == 0) { - searchTerm.append(previousSearchTerm); - } - - if (searchIndex > -1 && searchIndex < history.size() - 1) { - searchIndex = searchForwards(searchTerm.toString(), searchIndex); - } - break; - - case BACKWARD_DELETE_CHAR: - if (searchTerm.length() > 0) { - searchTerm.deleteCharAt(searchTerm.length() - 1); - if (state == State.SEARCH) { - searchIndex = searchBackwards(searchTerm.toString()); - } else { - searchIndex = searchForwards(searchTerm.toString()); - } - } - break; - - case SELF_INSERT: - searchTerm.appendCodePoint(c); - if (state == State.SEARCH) { - searchIndex = searchBackwards(searchTerm.toString()); - } else { - searchIndex = searchForwards(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()); - } - if (o != lib.jline.console.Operation.ACCEPT_LINE) { - o = null; - } - state = State.NORMAL; - break; - } - - // if we're still in search mode, print the search status - if (state == State.SEARCH || state == State.FORWARD_SEARCH) { - if (searchTerm.length() == 0) { - if (state == State.SEARCH) { - printSearchStatus("", ""); - } else { - printForwardSearchStatus("", ""); - } - searchIndex = -1; - } else { - if (searchIndex == -1) { - beep(); - printSearchStatus(searchTerm.toString(), ""); - } else if (state == State.SEARCH) { - printSearchStatus(searchTerm.toString(), history.get(searchIndex).toString()); - } else { - printForwardSearchStatus(searchTerm.toString(), history.get(searchIndex).toString()); - } - } - } - // otherwise, restore the line - else { - restoreLine(originalPrompt, cursorDest); - } - } - if (state != State.SEARCH && state != State.FORWARD_SEARCH) { - /* - * If this is still false at the end of the switch, then - * we reset our repeatCount to 0. - */ - boolean isArgDigit = false; - - /* - * Every command that can be repeated a specified number - * of times, needs to know how many times to repeat, so - * we figure that out here. - */ - int count = (repeatCount == 0) ? 1 : repeatCount; - - /* - * Default success to true. You only need to explicitly - * set it if something goes wrong. - */ - success = true; - - if (o instanceof lib.jline.console.Operation) { - lib.jline.console.Operation op = (lib.jline.console.Operation)o; - /* - * Current location of the cursor (prior to the operation). - * These are used by vi *-to operation (e.g. delete-to) - * so we know where we came from. - */ - int cursorStart = buf.cursor; - State origState = state; - - /* - * If we are on a "vi" movement based operation, then we - * need to restrict the sets of inputs pretty heavily. - */ - if (state == State.VI_CHANGE_TO - || state == State.VI_YANK_TO - || state == State.VI_DELETE_TO) { - - op = viDeleteChangeYankToRemap(op); - } - - switch ( op ) { - case COMPLETE: // tab - // There is an annoyance with tab completion in that - // sometimes the user is actually pasting input in that - // has physical tabs in it. This attempts to look at how - // quickly a character follows the tab, if the character - // follows *immediately*, we assume it is a tab literal. - boolean isTabLiteral = false; - if (copyPasteDetection - && c == 9 - && (!pushBackChar.isEmpty() - || (in.isNonBlockingEnabled() && in.peek(escapeTimeout) != -2))) { - isTabLiteral = true; - } - - if (! isTabLiteral) { - success = complete(); - } - else { - putString(opBuffer); - } - break; - - case POSSIBLE_COMPLETIONS: - printCompletionCandidates(); - break; - - case BEGINNING_OF_LINE: - success = setCursorPosition(0); - break; - - case YANK: - success = yank(); - break; - - case YANK_POP: - success = yankPop(); - break; - - case KILL_LINE: // CTRL-K - success = killLine(); - break; - - case KILL_WHOLE_LINE: - success = setCursorPosition(0) && killLine(); - break; - - case CLEAR_SCREEN: // CTRL-L - success = clearScreen(); - redrawLine(); - break; - - case OVERWRITE_MODE: - buf.setOverTyping(!buf.isOverTyping()); - break; - - case SELF_INSERT: - putString(opBuffer); - break; - - case ACCEPT_LINE: - return accept(); - - case ABORT: - if (searchTerm == null) { - abort(); - } - break; - - case INTERRUPT: - if (handleUserInterrupt) { - println(); - flush(); - String partialLine = buf.buffer.toString(); - buf.clear(); - history.moveToEnd(); - throw new UserInterruptException(partialLine); - } - break; - - /* - * VI_MOVE_ACCEPT_LINE is the result of an ENTER - * while in move mode. This is the same as a normal - * ACCEPT_LINE, except that we need to enter - * insert mode as well. - */ - case VI_MOVE_ACCEPT_LINE: - consoleKeys.setKeyMap(KeyMap.VI_INSERT); - return accept(); - - case BACKWARD_WORD: - success = previousWord(); - break; - - case FORWARD_WORD: - success = nextWord(); - break; - - case PREVIOUS_HISTORY: - success = moveHistory(false); - break; - - /* - * According to bash/readline move through history - * in "vi" mode will move the cursor to the - * start of the line. If there is no previous - * history, then the cursor doesn't move. - */ - case VI_PREVIOUS_HISTORY: - success = moveHistory(false, count) - && setCursorPosition(0); - break; - - case NEXT_HISTORY: - success = moveHistory(true); - break; - - /* - * According to bash/readline move through history - * in "vi" mode will move the cursor to the - * start of the line. If there is no next history, - * then the cursor doesn't move. - */ - case VI_NEXT_HISTORY: - success = moveHistory(true, count) - && setCursorPosition(0); - break; - - case BACKWARD_DELETE_CHAR: // backspace - success = backspace(); - break; - - case EXIT_OR_DELETE_CHAR: - if (buf.buffer.length() == 0) { - return null; - } - success = deleteCurrentCharacter(); - break; - - case DELETE_CHAR: // delete - success = deleteCurrentCharacter(); - break; - - case BACKWARD_CHAR: - success = moveCursor(-(count)) != 0; - break; - - case FORWARD_CHAR: - success = moveCursor(count) != 0; - break; - - case UNIX_LINE_DISCARD: - success = resetLine(); - break; - - case UNIX_WORD_RUBOUT: - success = unixWordRubout(count); - break; - - case BACKWARD_KILL_WORD: - success = deletePreviousWord(); - break; - - case KILL_WORD: - success = deleteNextWord(); - break; - - case BEGINNING_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 HISTORY_SEARCH_BACKWARD: - searchTerm = new StringBuffer(buf.upToCursor()); - searchIndex = searchBackwards(searchTerm.toString(), history.index(), true); - - if (searchIndex == -1) { - beep(); - } else { - // Maintain cursor position while searching. - success = history.moveTo(searchIndex); - if (success) { - setBufferKeepPos(history.current()); - } - } - break; - - case HISTORY_SEARCH_FORWARD: - searchTerm = new StringBuffer(buf.upToCursor()); - int index = history.index() + 1; - - if (index == history.size()) { - history.moveToEnd(); - setBufferKeepPos(searchTerm.toString()); - } else if (index < history.size()) { - searchIndex = searchForwards(searchTerm.toString(), index, true); - if (searchIndex == -1) { - beep(); - } else { - // Maintain cursor position while searching. - success = history.moveTo(searchIndex); - if (success) { - setBufferKeepPos(history.current()); - } - } - } - break; - - case REVERSE_SEARCH_HISTORY: - originalBuffer = new CursorBuffer(); - originalBuffer.write(buf.buffer); - originalBuffer.cursor = buf.cursor; - if (searchTerm != null) { - previousSearchTerm = searchTerm.toString(); - } - searchTerm = new StringBuffer(buf.buffer); - state = 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 FORWARD_SEARCH_HISTORY: - originalBuffer = new CursorBuffer(); - originalBuffer.write(buf.buffer); - originalBuffer.cursor = buf.cursor; - if (searchTerm != null) { - previousSearchTerm = searchTerm.toString(); - } - searchTerm = new StringBuffer(buf.buffer); - state = State.FORWARD_SEARCH; - if (searchTerm.length() > 0) { - searchIndex = searchForwards(searchTerm.toString()); - if (searchIndex == -1) { - beep(); - } - printForwardSearchStatus(searchTerm.toString(), - searchIndex > -1 ? history.get(searchIndex).toString() : ""); - } else { - searchIndex = -1; - printForwardSearchStatus("", ""); - } - break; - - case CAPITALIZE_WORD: - success = capitalizeWord(); - break; - - case UPCASE_WORD: - success = upCaseWord(); - break; - - case DOWNCASE_WORD: - success = downCaseWord(); - break; - - case END_OF_LINE: - success = moveToEnd(); - break; - - case TAB_INSERT: - putString( "\t" ); - break; - - case RE_READ_INIT_FILE: - consoleKeys.loadKeys(appName, inputrcUrl); - break; - - case START_KBD_MACRO: - recording = true; - break; - - case END_KBD_MACRO: - recording = false; - macro = macro.substring(0, macro.length() - opBuffer.length()); - break; - - case CALL_LAST_KBD_MACRO: - for (int i = 0; i < macro.length(); i++) { - pushBackChar.push(macro.charAt(macro.length() - 1 - i)); - } - opBuffer.setLength(0); - break; - - case VI_EDITING_MODE: - consoleKeys.setKeyMap(KeyMap.VI_INSERT); - break; - - case VI_MOVEMENT_MODE: - /* - * If we are re-entering move mode from an - * aborted yank-to, delete-to, change-to then - * don't move the cursor back. The cursor is - * only move on an expclit entry to movement - * mode. - */ - if (state == State.NORMAL) { - moveCursor(-1); - } - consoleKeys.setKeyMap(KeyMap.VI_MOVE); - break; - - case VI_INSERTION_MODE: - consoleKeys.setKeyMap(KeyMap.VI_INSERT); - break; - - case VI_APPEND_MODE: - moveCursor(1); - consoleKeys.setKeyMap(KeyMap.VI_INSERT); - break; - - case VI_APPEND_EOL: - success = moveToEnd(); - consoleKeys.setKeyMap(KeyMap.VI_INSERT); - break; - - /* - * Handler for CTRL-D. Attempts to follow readline - * behavior. If the line is empty, then it is an EOF - * otherwise it is as if the user hit enter. - */ - case VI_EOF_MAYBE: - if (buf.buffer.length() == 0) { - return null; - } - return accept(); - - case TRANSPOSE_CHARS: - success = transposeChars(count); - break; - - case INSERT_COMMENT: - return insertComment (false); - - case INSERT_CLOSE_CURLY: - insertClose("}"); - break; - - case INSERT_CLOSE_PAREN: - insertClose(")"); - break; - - case INSERT_CLOSE_SQUARE: - insertClose("]"); - break; - - case VI_INSERT_COMMENT: - return insertComment (true); - - case VI_MATCH: - success = viMatch (); - break; - - case VI_SEARCH: - int lastChar = viSearch(opBuffer.charAt(0)); - if (lastChar != -1) { - pushBackChar.push((char)lastChar); - } - break; - - case VI_ARG_DIGIT: - repeatCount = (repeatCount * 10) + opBuffer.charAt(0) - '0'; - isArgDigit = true; - break; - - case VI_BEGINNING_OF_LINE_OR_ARG_DIGIT: - if (repeatCount > 0) { - repeatCount = (repeatCount * 10) + opBuffer.charAt(0) - '0'; - isArgDigit = true; - } - else { - success = setCursorPosition(0); - } - break; - - case VI_FIRST_PRINT: - success = setCursorPosition(0) && viNextWord(1); - break; - - case VI_PREV_WORD: - success = viPreviousWord(count); - break; - - case VI_NEXT_WORD: - success = viNextWord(count); - break; - - case VI_END_WORD: - success = viEndWord(count); - break; - - case VI_INSERT_BEG: - success = setCursorPosition(0); - consoleKeys.setKeyMap(KeyMap.VI_INSERT); - break; - - case VI_RUBOUT: - success = viRubout(count); - break; - - case VI_DELETE: - success = viDelete(count); - break; - - case VI_DELETE_TO: - /* - * This is a weird special case. In vi - * "dd" deletes the current line. So if we - * get a delete-to, followed by a delete-to, - * we delete the line. - */ - if (state == State.VI_DELETE_TO) { - success = setCursorPosition(0) && killLine(); - state = origState = State.NORMAL; - } - else { - state = State.VI_DELETE_TO; - } - break; - - case VI_YANK_TO: - // Similar to delete-to, a "yy" yanks the whole line. - if (state == State.VI_YANK_TO) { - yankBuffer = buf.buffer.toString(); - state = origState = State.NORMAL; - } - else { - state = State.VI_YANK_TO; - } - break; - - case VI_CHANGE_TO: - if (state == State.VI_CHANGE_TO) { - success = setCursorPosition(0) && killLine(); - state = origState = State.NORMAL; - consoleKeys.setKeyMap(KeyMap.VI_INSERT); - } - else { - state = State.VI_CHANGE_TO; - } - break; - - case VI_KILL_WHOLE_LINE: - success = setCursorPosition(0) && killLine(); - consoleKeys.setKeyMap(KeyMap.VI_INSERT); - break; - - case VI_PUT: - success = viPut(count); - break; - - case VI_CHAR_SEARCH: { - // ';' and ',' don't need another character. They indicate repeat next or repeat prev. - int searchChar = (c != ';' && c != ',') - ? (pushBackChar.isEmpty() - ? readCharacter() - : pushBackChar.pop ()) - : 0; - - success = viCharSearch(count, c, searchChar); - } - break; - - case VI_CHANGE_CASE: - success = viChangeCase(count); - break; - - case VI_CHANGE_CHAR: - success = viChangeChar(count, - pushBackChar.isEmpty() - ? readCharacter() - : pushBackChar.pop()); - break; - - case VI_DELETE_TO_EOL: - success = viDeleteTo(buf.cursor, buf.buffer.length(), false); - break; - - case VI_CHANGE_TO_EOL: - success = viDeleteTo(buf.cursor, buf.buffer.length(), true); - consoleKeys.setKeyMap(KeyMap.VI_INSERT); - break; - - case EMACS_EDITING_MODE: - consoleKeys.setKeyMap(KeyMap.EMACS); - break; - - case QUIT: - getCursorBuffer().clear(); - return accept(); - - case QUOTED_INSERT: - quotedInsert = true; - break; - - case PASTE_FROM_CLIPBOARD: - paste(); - break; - - default: - break; - } - - /* - * If we were in a yank-to, delete-to, move-to - * when this operation started, then fall back to - */ - if (origState != State.NORMAL) { - if (origState == State.VI_DELETE_TO) { - success = viDeleteTo(cursorStart, buf.cursor, false); - } - else if (origState == State.VI_CHANGE_TO) { - success = viDeleteTo(cursorStart, buf.cursor, true); - consoleKeys.setKeyMap(KeyMap.VI_INSERT); - } - else if (origState == State.VI_YANK_TO) { - success = viYankTo(cursorStart, buf.cursor); - } - state = State.NORMAL; - } - - /* - * Another subtly. The check for the NORMAL state is - * to ensure that we do not cleanUp out the repeat - * count when in delete-to, yank-to, or move-to modes. - */ - if (state == State.NORMAL && !isArgDigit) { - /* - * If the operation performed wasn't a vi argument - * digit, then cleanUp out the current repeatCount; - */ - repeatCount = 0; - } - - if (state != State.SEARCH && state != State.FORWARD_SEARCH) { - originalBuffer = null; - previousSearchTerm = ""; - searchTerm = null; - searchIndex = -1; - } - } - } - if (!success) { - beep(); - } - opBuffer.setLength(0); - - flush(); - } - } - finally { - if (!terminal.isSupported()) { - afterReadLine(); - } - if (handleUserInterrupt) { - terminal.enableInterruptCharacter(); - } - } - } - - /** - * Read a line for unsupported terminals. - */ - private String readLineSimple() throws IOException { - StringBuilder buff = new StringBuilder(); - - if (skipLF) { - skipLF = false; - - int i = readCharacter(); - - if (i == -1 || i == '\r') { - return buff.toString(); - } else if (i == '\n') { - // ignore - } else { - buff.append((char) i); - } - } - - while (true) { - int i = readCharacter(); - - if (i == -1 && buff.length() == 0) { - return null; - } - - if (i == -1 || i == '\n') { - return buff.toString(); - } else if (i == '\r') { - skipLF = true; - return buff.toString(); - } else { - buff.append((char) i); - } - } - } - - // - // Completion - // - - private final List completers = new LinkedList(); - - private lib.jline.console.completer.CompletionHandler completionHandler = new lib.jline.console.completer.CandidateListCompletionHandler(); - - /** - * Add the specified {@link lib.jline.console.completer.Completer} to the list of handlers for tab-completion. - * - * @param completer the {@link lib.jline.console.completer.Completer} to add - * @return true if it was successfully added - */ - public boolean addCompleter(final lib.jline.console.completer.Completer completer) { - return completers.add(completer); - } - - /** - * Remove the specified {@link lib.jline.console.completer.Completer} from the list of handlers for tab-completion. - * - * @param completer The {@link lib.jline.console.completer.Completer} to remove - * @return True if it was successfully removed - */ - public boolean removeCompleter(final lib.jline.console.completer.Completer completer) { - return completers.remove(completer); - } - - /** - * Returns an unmodifiable list of all the completers. - */ - public Collection getCompleters() { - return Collections.unmodifiableList(completers); - } - - public void setCompletionHandler(final lib.jline.console.completer.CompletionHandler handler) { - this.completionHandler = lib.jline.internal.Preconditions.checkNotNull(handler); - } - - public lib.jline.console.completer.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 candidates = new LinkedList(); - String bufstr = buf.buffer.toString(); - int cursor = buf.cursor; - - int position = -1; - - for (lib.jline.console.completer.Completer comp : completers) { - if ((position = comp.complete(bufstr, cursor, candidates)) != -1) { - break; - } - } - - return candidates.size() != 0 && getCompletionHandler().complete(this, candidates, position); - } - - protected void printCompletionCandidates() throws IOException { - // debug ("tab for (" + buf + ")"); - if (completers.size() == 0) { - return; - } - - List candidates = new LinkedList(); - String bufstr = buf.buffer.toString(); - int cursor = buf.cursor; - - for (lib.jline.console.completer.Completer comp : completers) { - if (comp.complete(bufstr, cursor, candidates) != -1) { - break; - } - } - lib.jline.console.completer.CandidateListCompletionHandler.printCandidates(this, candidates); - drawLine(); - } - - /** - * The number of tab-completion candidates above which a warning will be - * prompted before showing all the candidates. - */ - private int autoprintThreshold = lib.jline.internal.Configuration.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; - } - - /** - * Used in "vi" mode for argumented history move, to move a specific - * number of history entries forward or back. - * - * @param next If true, move forward - * @param count The number of entries to move - * @return true if the move was successful - */ - private boolean moveHistory(final boolean next, int count) throws IOException { - boolean ok = true; - for (int i = 0; i < count && (ok = moveHistory(next)); i++) { - /* empty */ - } - return ok; - } - - /** - * 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 - // - - /** - * Output the specified characters to the output stream without manipulating the current buffer. - */ - private int fmtPrint(final CharSequence buff, int cursorPos) throws IOException { - return fmtPrint(buff, 0, buff.length(), cursorPos); - } - - private int fmtPrint(final CharSequence buff, int start, int end) throws IOException { - return fmtPrint(buff, start, end, getCursorPosition()); - } - - private int fmtPrint(final CharSequence buff, int start, int end, int cursorPos) throws IOException { - lib.jline.internal.Preconditions.checkNotNull(buff); - for (int i = start; i < end; i++) { - char c = buff.charAt(i); - if (c == '\t') { - int nb = nextTabStop(cursorPos); - cursorPos += nb; - while (nb-- > 0) { - out.write(' '); - } - } else if (c < 32) { - out.write('^'); - out.write((char) (c + '@')); - cursorPos += 2; - } else { - int w = WCWidth.wcwidth(c); - if (w > 0) { - out.write(c); - cursorPos += w; - } - } - } - cursorOk = false; - return cursorPos; - } - - /** - * Output the specified string to the output stream (but not the buffer). - */ - public void print(final CharSequence s) throws IOException { - rawPrint(s.toString()); - } - - public void println(final CharSequence s) throws IOException { - print(s); - println(); - } - - /** - * Output a platform-dependant newline. - */ - public void println() throws IOException { - tputs("carriage_return"); - rawPrint('\n'); - } - - /** - * Raw output printing - */ - final void rawPrint(final int c) throws IOException { - out.write(c); - cursorOk = false; - } - - final void rawPrint(final String str) throws IOException { - out.write(str); - cursorOk = false; - } - - private void rawPrint(final char c, final int num) throws IOException { - for (int i = 0; i < num; i++) { - rawPrint(c); - } - } - - private void rawPrintln(final String s) throws IOException { - rawPrint(s); - println(); - } - - - // - // Actions - // - - /** - * Issue a delete. - * - * @return true if successful - */ - public boolean delete() throws IOException { - if (buf.cursor == buf.buffer.length()) { - return false; - } - - buf.buffer.delete(buf.cursor, buf.cursor + 1); - drawBuffer(1); - - return true; - } - - /** - * 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 = len - cp; - int pos = getCursorPosition(); - int width = wcwidth(buf.buffer, cp, len, pos); - clearAhead(width, pos); - - char[] killed = new char[num]; - buf.buffer.getChars(cp, (cp + num), killed, 0); - buf.buffer.delete(cp, (cp + num)); - - String copy = new String(killed); - killRing.add(copy); - - return true; - } - - public boolean yank() throws IOException { - String yanked = killRing.yank(); - - if (yanked == null) { - return false; - } - putString(yanked); - return true; - } - - public boolean yankPop() throws IOException { - if (!killRing.lastYank()) { - return false; - } - String current = killRing.yank(); - if (current == null) { - // This shouldn't happen. - return false; - } - backspace(current.length()); - String yanked = killRing.yankPop(); - if (yanked == null) { - // This shouldn't happen. - return false; - } - - putString(yanked); - return true; - } - - /** - * Clear the screen by issuing the ANSI "cleanUp screen" code. - */ - public boolean clearScreen() throws IOException { - if (!tputs("clear_screen")) { - println(); - } - return true; - } - - /** - * Issue an audible keyboard bell. - */ - public void beep() throws IOException { - if (bellEnabled) { - if (tputs("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 { - @SuppressWarnings("deprecation") - 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) { - lib.jline.internal.Log.error("Paste failed: ", e); - - return false; - } - } - - /** - * Adding a triggered Command allows to give another curse of action if a character passed the pre-processing. - *

- * 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) { - getKeys().bind(Character.toString(c), listener); - } - - // - // Formatted Output - // - - /** - * Output the specified {@link Collection} in proper columns. - */ - public void printColumns(final Collection items) throws IOException { - if (items == null || items.isEmpty()) { - return; - } - - int width = getTerminal().getWidth(); - int height = getTerminal().getHeight(); - - int maxWidth = 0; - for (CharSequence item : items) { - // we use 0 here, as we don't really support tabulations inside candidates - int len = wcwidth(Ansi.stripAnsi(item.toString()), 0); - maxWidth = Math.max(maxWidth, len); - } - maxWidth = maxWidth + 3; - lib.jline.internal.Log.debug("Max width: ", maxWidth); - - int showLines; - if (isPaginationEnabled()) { - showLines = height - 1; // page limit - } - else { - showLines = Integer.MAX_VALUE; - } - - StringBuilder buff = new StringBuilder(); - int realLength = 0; - for (CharSequence item : items) { - if ((realLength + maxWidth) > width) { - rawPrintln(buff.toString()); - buff.setLength(0); - realLength = 0; - - if (--showLines == 0) { - // Overflow - print(resources.getString("DISPLAY_MORE")); - flush(); - int c = readCharacter(); - if (c == '\r' || c == '\n') { - // one step forward - showLines = 1; - } - else if (c != 'q') { - // page forward - showLines = height - 1; - } - - tputs("carriage_return"); - if (c == 'q') { - // cancel - break; - } - } - } - - // NOTE: toString() is important here due to AnsiString being retarded - buff.append(item.toString()); - int strippedItemLength = wcwidth(Ansi.stripAnsi(item.toString()), 0); - for (int i = 0; i < (maxWidth - strippedItemLength); i++) { - buff.append(' '); - } - realLength += maxWidth; - } - - if (buff.length() > 0) { - rawPrintln(buff.toString()); - } - } - - // - // 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); - int promptLength = 0; - if (this.prompt != null) { - promptLength = this.prompt.length(); - } - - buf.cursor += promptLength; - setPrompt(""); - backspaceAll(); - - setPrompt(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 { - printSearchStatus(searchTerm, match, "(reverse-i-search)`"); - } - - public void printForwardSearchStatus(String searchTerm, String match) throws IOException { - printSearchStatus(searchTerm, match, "(i-search)`"); - } - - private void printSearchStatus(String searchTerm, String match, String searchLabel) throws IOException { - String prompt = searchLabel + searchTerm + "': "; - int cursorDest = match.indexOf(searchTerm); - resetPromptLine(prompt, match, 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 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; - } - - /** - * Search forward 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 searchForwards(String searchTerm, int startIndex) { - return searchForwards(searchTerm, startIndex, false); - } - /** - * Search forwards 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 searchForwards(String searchTerm) { - return searchForwards(searchTerm, history.index()); - } - - public int searchForwards(String searchTerm, int startIndex, boolean startsWith) { - if (startIndex >= history.size()) { - startIndex = history.size() - 1; - } - - ListIterator it = history.entries(startIndex); - - if (searchIndex != -1 && it.hasNext()) { - it.next(); - } - - while (it.hasNext()) { - History.Entry e = it.next(); - 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 static boolean isDelimiter(final char c) { - return !Character.isLetterOrDigit(c); - } - - /** - * Checks to see if a character is a whitespace character. Currently - * this delegates to {@link Character#isWhitespace(char)}, however - * eventually it should be hooked up so that the definition of whitespace - * can be configured, as readline does. - * - * @param c The character to check - * @return true if the character is a whitespace - */ - private static boolean isWhitespace(final char c) { - return Character.isWhitespace (c); - } - - private boolean tputs(String cap, Object... params) throws IOException { - String str = terminal.getStringCapability(cap); - if (str == null) { - return false; - } - Curses.tputs(out, str, params); - return true; - } - -} diff --git a/kshell-console-jline2/src/main/java/lib/jline/console/CursorBuffer.java b/kshell-console-jline2/src/main/java/lib/jline/console/CursorBuffer.java deleted file mode 100644 index aaaa863..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/console/CursorBuffer.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline.console; - -/** - * A holder for a {@link StringBuilder} that also contains the current cursor position. - * - * @author Marc Prud'hommeaux - * @author Jason Dillon - * @since 2.0 - */ -public class CursorBuffer -{ - private boolean overTyping = false; - - public int cursor = 0; - - public final StringBuilder buffer = new StringBuilder(); - - public CursorBuffer copy () { - CursorBuffer that = new CursorBuffer(); - that.overTyping = this.overTyping; - that.cursor = this.cursor; - that.buffer.append (this.toString()); - - return that; - } - - public boolean isOverTyping() { - return overTyping; - } - - public void setOverTyping(final boolean b) { - overTyping = b; - } - - public int length() { - return buffer.length(); - } - - public char nextChar() { - if (cursor == buffer.length()) { - return 0; - } else { - return buffer.charAt(cursor); - } - } - - public char current() { - if (cursor <= 0) { - return 0; - } - - return buffer.charAt(cursor - 1); - } - - /** - * 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) { - lib.jline.internal.Preconditions.checkNotNull(str); - - 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; - } - - public String upToCursor() { - if (cursor <= 0) { - return ""; - } - - return buffer.substring(0, cursor); - } - - @Override - public String toString() { - return buffer.toString(); - } -} diff --git a/kshell-console-jline2/src/main/java/lib/jline/console/KeyMap.java b/kshell-console-jline2/src/main/java/lib/jline/console/KeyMap.java deleted file mode 100644 index c80023d..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/console/KeyMap.java +++ /dev/null @@ -1,577 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline.console; - -import java.util.HashMap; -import java.util.Map; - -/** - * The KeyMap class contains all bindings from keys to operations. - * - * @author Guillaume Nodet - * @since 2.6 - */ -public class KeyMap { - - public static final String VI_MOVE = "vi-move"; - public static final String VI_INSERT = "vi-insert"; - public static final String EMACS = "emacs"; - public static final String EMACS_STANDARD = "emacs-standard"; - public static final String EMACS_CTLX = "emacs-ctlx"; - public static final String EMACS_META = "emacs-meta"; - - private static final int KEYMAP_LENGTH = 256; - - private static final Object NULL_FUNCTION = new Object(); - - private Object[] mapping = new Object[KEYMAP_LENGTH]; - private Object anotherKey = null; - private String name; - - public KeyMap(String name) { - this(name, new Object[KEYMAP_LENGTH]); - } - - @Deprecated - public KeyMap(String name, boolean unused) { - this(name); - } - - protected KeyMap(String name, Object[] mapping) { - this.mapping = mapping; - this.name = name; - } - - public String getName() { - return name; - } - - public Object getAnotherKey() { - return anotherKey; - } - - public void from(KeyMap other) { - this.mapping = other.mapping; - this.anotherKey = other.anotherKey; - } - - public Object getBound( CharSequence keySeq ) { - if (keySeq != null && keySeq.length() > 0) { - KeyMap map = this; - for (int i = 0; i < keySeq.length(); i++) { - char c = keySeq.charAt(i); - if (c > 255) { - return Operation.SELF_INSERT; - } - if (map.mapping[c] instanceof KeyMap) { - if (i == keySeq.length() - 1) { - return map.mapping[c]; - } else { - map = (KeyMap) map.mapping[c]; - } - } else { - return map.mapping[c]; - } - } - } - return null; - } - - public void bindIfNotBound( CharSequence keySeq, Object function ) { - - bind (this, keySeq, function, true); - } - - public void bind( CharSequence keySeq, Object function ) { - - bind (this, keySeq, function, false); - } - - private static void bind( KeyMap map, CharSequence keySeq, Object function ) { - - bind (map, keySeq, function, false); - } - - private static void bind( KeyMap map, CharSequence keySeq, Object function, - boolean onlyIfNotBound ) { - - if (keySeq != null && keySeq.length() > 0) { - for (int i = 0; i < keySeq.length(); i++) { - char c = keySeq.charAt(i); - if (c >= map.mapping.length) { - return; - } - if (i < keySeq.length() - 1) { - if (!(map.mapping[c] instanceof KeyMap)) { - KeyMap m = new KeyMap("anonymous"); - if (map.mapping[c] != Operation.DO_LOWERCASE_VERSION) { - m.anotherKey = map.mapping[c]; - } - map.mapping[c] = m; - } - map = (KeyMap) map.mapping[c]; - } else { - if (function == null) { - function = NULL_FUNCTION; - } - if (map.mapping[c] instanceof KeyMap) { - map.anotherKey = function; - } else { - Object op = map.mapping[c]; - if (onlyIfNotBound == false - || op == null - || op == Operation.DO_LOWERCASE_VERSION - || op == Operation.VI_MOVEMENT_MODE ) { - - } - - map.mapping[c] = function; - } - } - } - } - } - - public void setBlinkMatchingParen(boolean on) { - if (on) { - bind( "}", Operation.INSERT_CLOSE_CURLY ); - bind( ")", Operation.INSERT_CLOSE_PAREN ); - bind( "]", Operation.INSERT_CLOSE_SQUARE ); - } - } - - private static void bindArrowKeys(KeyMap map) { - - // MS-DOS - bind( map, "\033[0A", Operation.PREVIOUS_HISTORY ); - bind( map, "\033[0B", Operation.BACKWARD_CHAR ); - bind( map, "\033[0C", Operation.FORWARD_CHAR ); - bind( map, "\033[0D", Operation.NEXT_HISTORY ); - - // Windows - bind( map, "\340\000", Operation.KILL_WHOLE_LINE ); - bind( map, "\340\107", Operation.BEGINNING_OF_LINE ); - bind( map, "\340\110", Operation.PREVIOUS_HISTORY ); - bind( map, "\340\111", Operation.BEGINNING_OF_HISTORY ); - bind( map, "\340\113", Operation.BACKWARD_CHAR ); - bind( map, "\340\115", Operation.FORWARD_CHAR ); - bind( map, "\340\117", Operation.END_OF_LINE ); - bind( map, "\340\120", Operation.NEXT_HISTORY ); - bind( map, "\340\121", Operation.END_OF_HISTORY ); - bind( map, "\340\122", Operation.OVERWRITE_MODE ); - bind( map, "\340\123", Operation.DELETE_CHAR ); - - bind( map, "\000\107", Operation.BEGINNING_OF_LINE ); - bind( map, "\000\110", Operation.PREVIOUS_HISTORY ); - bind( map, "\000\111", Operation.BEGINNING_OF_HISTORY ); - bind( map, "\000\110", Operation.PREVIOUS_HISTORY ); - bind( map, "\000\113", Operation.BACKWARD_CHAR ); - bind( map, "\000\115", Operation.FORWARD_CHAR ); - bind( map, "\000\117", Operation.END_OF_LINE ); - bind( map, "\000\120", Operation.NEXT_HISTORY ); - bind( map, "\000\121", Operation.END_OF_HISTORY ); - bind( map, "\000\122", Operation.OVERWRITE_MODE ); - bind( map, "\000\123", Operation.DELETE_CHAR ); - - bind( map, "\033[A", Operation.PREVIOUS_HISTORY ); - bind( map, "\033[B", Operation.NEXT_HISTORY ); - bind( map, "\033[C", Operation.FORWARD_CHAR ); - bind( map, "\033[D", Operation.BACKWARD_CHAR ); - bind( map, "\033[H", Operation.BEGINNING_OF_LINE ); - bind( map, "\033[F", Operation.END_OF_LINE ); - - bind( map, "\033OA", Operation.PREVIOUS_HISTORY ); - bind( map, "\033OB", Operation.NEXT_HISTORY ); - bind( map, "\033OC", Operation.FORWARD_CHAR ); - bind( map, "\033OD", Operation.BACKWARD_CHAR ); - bind( map, "\033OH", Operation.BEGINNING_OF_LINE ); - bind( map, "\033OF", Operation.END_OF_LINE ); - - bind( map, "\033[1~", Operation.BEGINNING_OF_LINE); - bind( map, "\033[4~", Operation.END_OF_LINE); - bind( map, "\033[3~", Operation.DELETE_CHAR); - - // MINGW32 - bind( map, "\0340H", Operation.PREVIOUS_HISTORY ); - bind( map, "\0340P", Operation.NEXT_HISTORY ); - bind( map, "\0340M", Operation.FORWARD_CHAR ); - bind( map, "\0340K", Operation.BACKWARD_CHAR ); - } - -// public boolean isConvertMetaCharsToAscii() { -// return convertMetaCharsToAscii; -// } - -// public void setConvertMetaCharsToAscii(boolean convertMetaCharsToAscii) { -// this.convertMetaCharsToAscii = convertMetaCharsToAscii; -// } - - public static boolean isMeta( char c ) { - return c > 0x7f && c <= 0xff; - } - - public static char unMeta( char c ) { - return (char) (c & 0x7F); - } - - public static char meta( char c ) { - return (char) (c | 0x80); - } - - public static Map keyMaps() { - Map keyMaps = new HashMap(); - - KeyMap emacs = emacs(); - bindArrowKeys(emacs); - keyMaps.put(EMACS, emacs); - keyMaps.put(EMACS_STANDARD, emacs); - keyMaps.put(EMACS_CTLX, (KeyMap) emacs.getBound("\u0018")); - keyMaps.put(EMACS_META, (KeyMap) emacs.getBound("\u001b")); - - KeyMap viMov = viMovement(); - bindArrowKeys(viMov); - keyMaps.put(VI_MOVE, viMov); - keyMaps.put("vi-command", viMov); - keyMaps.put("vi", viMov); - - KeyMap viIns = viInsertion(); - bindArrowKeys(viIns); - keyMaps.put(VI_INSERT, viIns); - - return keyMaps; - } - - public static KeyMap emacs() { - Object[] map = new Object[KEYMAP_LENGTH]; - Object[] ctrl = new Object[] { - // Control keys. - Operation.SET_MARK, /* Control-@ */ - Operation.BEGINNING_OF_LINE, /* Control-A */ - Operation.BACKWARD_CHAR, /* Control-B */ - Operation.INTERRUPT, /* Control-C */ - Operation.EXIT_OR_DELETE_CHAR, /* Control-D */ - Operation.END_OF_LINE, /* Control-E */ - Operation.FORWARD_CHAR, /* Control-F */ - Operation.ABORT, /* Control-G */ - Operation.BACKWARD_DELETE_CHAR, /* Control-H */ - Operation.COMPLETE, /* Control-I */ - Operation.ACCEPT_LINE, /* Control-J */ - Operation.KILL_LINE, /* Control-K */ - Operation.CLEAR_SCREEN, /* Control-L */ - Operation.ACCEPT_LINE, /* Control-M */ - Operation.NEXT_HISTORY, /* Control-N */ - null, /* Control-O */ - Operation.PREVIOUS_HISTORY, /* Control-P */ - Operation.QUOTED_INSERT, /* Control-Q */ - Operation.REVERSE_SEARCH_HISTORY, /* Control-R */ - Operation.FORWARD_SEARCH_HISTORY, /* Control-S */ - Operation.TRANSPOSE_CHARS, /* Control-T */ - Operation.UNIX_LINE_DISCARD, /* Control-U */ - Operation.QUOTED_INSERT, /* Control-V */ - Operation.UNIX_WORD_RUBOUT, /* Control-W */ - emacsCtrlX(), /* Control-X */ - Operation.YANK, /* Control-Y */ - null, /* Control-Z */ - emacsMeta(), /* Control-[ */ - null, /* Control-\ */ - Operation.CHARACTER_SEARCH, /* Control-] */ - null, /* Control-^ */ - Operation.UNDO, /* Control-_ */ - }; - System.arraycopy( ctrl, 0, map, 0, ctrl.length ); - for (int i = 32; i < 256; i++) { - map[i] = Operation.SELF_INSERT; - } - map[DELETE] = Operation.BACKWARD_DELETE_CHAR; - return new KeyMap(EMACS, map); - } - - public static final char CTRL_D = (char) 4; - public static final char CTRL_G = (char) 7; - public static final char CTRL_H = (char) 8; - public static final char CTRL_I = (char) 9; - public static final char CTRL_J = (char) 10; - public static final char CTRL_M = (char) 13; - public static final char CTRL_R = (char) 18; - public static final char CTRL_S = (char) 19; - public static final char CTRL_U = (char) 21; - public static final char CTRL_X = (char) 24; - public static final char CTRL_Y = (char) 25; - public static final char ESCAPE = (char) 27; /* Ctrl-[ */ - public static final char CTRL_OB = (char) 27; /* Ctrl-[ */ - public static final char CTRL_CB = (char) 29; /* Ctrl-] */ - - public static final int DELETE = (char) 127; - - public static KeyMap emacsCtrlX() { - Object[] map = new Object[KEYMAP_LENGTH]; - map[CTRL_G] = Operation.ABORT; - map[CTRL_R] = Operation.RE_READ_INIT_FILE; - map[CTRL_U] = Operation.UNDO; - map[CTRL_X] = Operation.EXCHANGE_POINT_AND_MARK; - map['('] = Operation.START_KBD_MACRO; - map[')'] = Operation.END_KBD_MACRO; - for (int i = 'A'; i <= 'Z'; i++) { - map[i] = Operation.DO_LOWERCASE_VERSION; - } - map['e'] = Operation.CALL_LAST_KBD_MACRO; - map[DELETE] = Operation.KILL_LINE; - return new KeyMap(EMACS_CTLX, map); - } - - public static KeyMap emacsMeta() { - Object[] map = new Object[KEYMAP_LENGTH]; - map[CTRL_G] = Operation.ABORT; - map[CTRL_H] = Operation.BACKWARD_KILL_WORD; - map[CTRL_I] = Operation.TAB_INSERT; - map[CTRL_J] = Operation.VI_EDITING_MODE; - map[CTRL_M] = Operation.VI_EDITING_MODE; - map[CTRL_R] = Operation.REVERT_LINE; - map[CTRL_Y] = Operation.YANK_NTH_ARG; - map[CTRL_OB] = Operation.COMPLETE; - map[CTRL_CB] = Operation.CHARACTER_SEARCH_BACKWARD; - map[' '] = Operation.SET_MARK; - map['#'] = Operation.INSERT_COMMENT; - map['&'] = Operation.TILDE_EXPAND; - map['*'] = Operation.INSERT_COMPLETIONS; - map['-'] = Operation.DIGIT_ARGUMENT; - map['.'] = Operation.YANK_LAST_ARG; - map['<'] = Operation.BEGINNING_OF_HISTORY; - map['='] = Operation.POSSIBLE_COMPLETIONS; - map['>'] = Operation.END_OF_HISTORY; - map['?'] = Operation.POSSIBLE_COMPLETIONS; - for (int i = 'A'; i <= 'Z'; i++) { - map[i] = Operation.DO_LOWERCASE_VERSION; - } - map['\\'] = Operation.DELETE_HORIZONTAL_SPACE; - map['_'] = Operation.YANK_LAST_ARG; - map['b'] = Operation.BACKWARD_WORD; - map['c'] = Operation.CAPITALIZE_WORD; - map['d'] = Operation.KILL_WORD; - map['f'] = Operation.FORWARD_WORD; - map['l'] = Operation.DOWNCASE_WORD; - map['p'] = Operation.NON_INCREMENTAL_REVERSE_SEARCH_HISTORY; - map['r'] = Operation.REVERT_LINE; - map['t'] = Operation.TRANSPOSE_WORDS; - map['u'] = Operation.UPCASE_WORD; - map['y'] = Operation.YANK_POP; - map['~'] = Operation.TILDE_EXPAND; - map[DELETE] = Operation.BACKWARD_KILL_WORD; - return new KeyMap(EMACS_META, map); - } - - public static KeyMap viInsertion() { - Object[] map = new Object[KEYMAP_LENGTH]; - Object[] ctrl = new Object[] { - // Control keys. - null, /* Control-@ */ - Operation.SELF_INSERT, /* Control-A */ - Operation.SELF_INSERT, /* Control-B */ - Operation.SELF_INSERT, /* Control-C */ - Operation.VI_EOF_MAYBE, /* Control-D */ - Operation.SELF_INSERT, /* Control-E */ - Operation.SELF_INSERT, /* Control-F */ - Operation.SELF_INSERT, /* Control-G */ - Operation.BACKWARD_DELETE_CHAR, /* Control-H */ - Operation.COMPLETE, /* Control-I */ - Operation.ACCEPT_LINE, /* Control-J */ - Operation.SELF_INSERT, /* Control-K */ - Operation.SELF_INSERT, /* Control-L */ - Operation.ACCEPT_LINE, /* Control-M */ - Operation.MENU_COMPLETE, /* Control-N */ - Operation.SELF_INSERT, /* Control-O */ - Operation.MENU_COMPLETE_BACKWARD, /* Control-P */ - Operation.SELF_INSERT, /* Control-Q */ - Operation.REVERSE_SEARCH_HISTORY, /* Control-R */ - Operation.FORWARD_SEARCH_HISTORY, /* Control-S */ - Operation.TRANSPOSE_CHARS, /* Control-T */ - Operation.UNIX_LINE_DISCARD, /* Control-U */ - Operation.QUOTED_INSERT, /* Control-V */ - Operation.UNIX_WORD_RUBOUT, /* Control-W */ - Operation.SELF_INSERT, /* Control-X */ - Operation.YANK, /* Control-Y */ - Operation.SELF_INSERT, /* Control-Z */ - Operation.VI_MOVEMENT_MODE, /* Control-[ */ - Operation.SELF_INSERT, /* Control-\ */ - Operation.SELF_INSERT, /* Control-] */ - Operation.SELF_INSERT, /* Control-^ */ - Operation.UNDO, /* Control-_ */ - }; - System.arraycopy( ctrl, 0, map, 0, ctrl.length ); - for (int i = 32; i < 256; i++) { - map[i] = Operation.SELF_INSERT; - } - map[DELETE] = Operation.BACKWARD_DELETE_CHAR; - return new KeyMap(VI_INSERT, map); - } - - public static KeyMap viMovement() { - Object[] map = new Object[KEYMAP_LENGTH]; - Object[] low = new Object[] { - // Control keys. - null, /* Control-@ */ - null, /* Control-A */ - null, /* Control-B */ - Operation.INTERRUPT, /* Control-C */ - /* - * ^D is supposed to move down half a screen. In bash - * appears to be ignored. - */ - Operation.VI_EOF_MAYBE, /* Control-D */ - Operation.EMACS_EDITING_MODE, /* Control-E */ - null, /* Control-F */ - Operation.ABORT, /* Control-G */ - Operation.BACKWARD_CHAR, /* Control-H */ - null, /* Control-I */ - Operation.VI_MOVE_ACCEPT_LINE, /* Control-J */ - Operation.KILL_LINE, /* Control-K */ - Operation.CLEAR_SCREEN, /* Control-L */ - Operation.VI_MOVE_ACCEPT_LINE, /* Control-M */ - Operation.VI_NEXT_HISTORY, /* Control-N */ - null, /* Control-O */ - Operation.VI_PREVIOUS_HISTORY, /* Control-P */ - /* - * My testing with readline is the ^Q is ignored. - * Maybe this should be null? - */ - Operation.QUOTED_INSERT, /* Control-Q */ - - /* - * TODO - Very broken. While in forward/reverse - * history search the VI keyset should go out the - * window and we need to enter a very simple keymap. - */ - Operation.REVERSE_SEARCH_HISTORY, /* Control-R */ - /* TODO */ - Operation.FORWARD_SEARCH_HISTORY, /* Control-S */ - Operation.TRANSPOSE_CHARS, /* Control-T */ - Operation.UNIX_LINE_DISCARD, /* Control-U */ - /* TODO */ - Operation.QUOTED_INSERT, /* Control-V */ - Operation.UNIX_WORD_RUBOUT, /* Control-W */ - null, /* Control-X */ - /* TODO */ - Operation.YANK, /* Control-Y */ - null, /* Control-Z */ - emacsMeta(), /* Control-[ */ - null, /* Control-\ */ - /* TODO */ - Operation.CHARACTER_SEARCH, /* Control-] */ - null, /* Control-^ */ - /* TODO */ - Operation.UNDO, /* Control-_ */ - Operation.FORWARD_CHAR, /* SPACE */ - null, /* ! */ - null, /* " */ - Operation.VI_INSERT_COMMENT, /* # */ - Operation.END_OF_LINE, /* $ */ - Operation.VI_MATCH, /* % */ - Operation.VI_TILDE_EXPAND, /* & */ - null, /* ' */ - null, /* ( */ - null, /* ) */ - /* TODO */ - Operation.VI_COMPLETE, /* * */ - Operation.VI_NEXT_HISTORY, /* + */ - Operation.VI_CHAR_SEARCH, /* , */ - Operation.VI_PREVIOUS_HISTORY, /* - */ - /* TODO */ - Operation.VI_REDO, /* . */ - Operation.VI_SEARCH, /* / */ - Operation.VI_BEGINNING_OF_LINE_OR_ARG_DIGIT, /* 0 */ - Operation.VI_ARG_DIGIT, /* 1 */ - Operation.VI_ARG_DIGIT, /* 2 */ - Operation.VI_ARG_DIGIT, /* 3 */ - Operation.VI_ARG_DIGIT, /* 4 */ - Operation.VI_ARG_DIGIT, /* 5 */ - Operation.VI_ARG_DIGIT, /* 6 */ - Operation.VI_ARG_DIGIT, /* 7 */ - Operation.VI_ARG_DIGIT, /* 8 */ - Operation.VI_ARG_DIGIT, /* 9 */ - null, /* : */ - Operation.VI_CHAR_SEARCH, /* ; */ - null, /* < */ - Operation.VI_COMPLETE, /* = */ - null, /* > */ - Operation.VI_SEARCH, /* ? */ - null, /* @ */ - Operation.VI_APPEND_EOL, /* A */ - Operation.VI_PREV_WORD, /* B */ - Operation.VI_CHANGE_TO_EOL, /* C */ - Operation.VI_DELETE_TO_EOL, /* D */ - Operation.VI_END_WORD, /* E */ - Operation.VI_CHAR_SEARCH, /* F */ - /* I need to read up on what this does */ - Operation.VI_FETCH_HISTORY, /* G */ - null, /* H */ - Operation.VI_INSERT_BEG, /* I */ - null, /* J */ - null, /* K */ - null, /* L */ - null, /* M */ - Operation.VI_SEARCH_AGAIN, /* N */ - null, /* O */ - Operation.VI_PUT, /* P */ - null, /* Q */ - /* TODO */ - Operation.VI_REPLACE, /* R */ - Operation.VI_KILL_WHOLE_LINE, /* S */ - Operation.VI_CHAR_SEARCH, /* T */ - /* TODO */ - Operation.REVERT_LINE, /* U */ - null, /* V */ - Operation.VI_NEXT_WORD, /* W */ - Operation.VI_RUBOUT, /* X */ - Operation.VI_YANK_TO, /* Y */ - null, /* Z */ - null, /* [ */ - Operation.VI_COMPLETE, /* \ */ - null, /* ] */ - Operation.VI_FIRST_PRINT, /* ^ */ - Operation.VI_YANK_ARG, /* _ */ - Operation.VI_GOTO_MARK, /* ` */ - Operation.VI_APPEND_MODE, /* a */ - Operation.VI_PREV_WORD, /* b */ - Operation.VI_CHANGE_TO, /* c */ - Operation.VI_DELETE_TO, /* d */ - Operation.VI_END_WORD, /* e */ - Operation.VI_CHAR_SEARCH, /* f */ - null, /* g */ - Operation.BACKWARD_CHAR, /* h */ - Operation.VI_INSERTION_MODE, /* i */ - Operation.NEXT_HISTORY, /* j */ - Operation.PREVIOUS_HISTORY, /* k */ - Operation.FORWARD_CHAR, /* l */ - Operation.VI_SET_MARK, /* m */ - Operation.VI_SEARCH_AGAIN, /* n */ - null, /* o */ - Operation.VI_PUT, /* p */ - null, /* q */ - Operation.VI_CHANGE_CHAR, /* r */ - Operation.VI_SUBST, /* s */ - Operation.VI_CHAR_SEARCH, /* t */ - Operation.UNDO, /* u */ - null, /* v */ - Operation.VI_NEXT_WORD, /* w */ - Operation.VI_DELETE, /* x */ - Operation.VI_YANK_TO, /* y */ - null, /* z */ - null, /* { */ - Operation.VI_COLUMN, /* | */ - null, /* } */ - Operation.VI_CHANGE_CASE, /* ~ */ - Operation.VI_DELETE /* DEL */ - }; - System.arraycopy( low, 0, map, 0, low.length ); - for (int i = 128; i < 256; i++) { - map[i] = null; - } - return new KeyMap(VI_MOVE, map); - } -} diff --git a/kshell-console-jline2/src/main/java/lib/jline/console/KillRing.java b/kshell-console-jline2/src/main/java/lib/jline/console/KillRing.java deleted file mode 100644 index 4144764..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/console/KillRing.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline.console; - -/** - * The kill ring class keeps killed text in a fixed size ring. In this - * class we also keep record of whether or not the last command was a - * kill or a yank. Depending on this, the class may behave - * different. For instance, two consecutive kill-word commands fill - * the same slot such that the next yank will return the two - * previously killed words instead that only the last one. Likewise - * yank pop requires that the previous command was either a yank or a - * yank-pop. - */ -public final class KillRing { - - /** - * Default size is 60, like in emacs. - */ - private static final int DEFAULT_SIZE = 60; - - private final String[] slots; - private int head = 0; - private boolean lastKill = false; - private boolean lastYank = false; - - /** - * Creates a new kill ring of the given size. - */ - public KillRing(int size) { - slots = new String[size]; - } - - /** - * Creates a new kill ring of the default size. See {@link #DEFAULT_SIZE}. - */ - public KillRing() { - this(DEFAULT_SIZE); - } - - /** - * Resets the last-yank state. - */ - public void resetLastYank() { - lastYank = false; - } - - /** - * Resets the last-kill state. - */ - public void resetLastKill() { - lastKill = false; - } - - /** - * Returns {@code true} if the last command was a yank. - */ - public boolean lastYank() { - return lastYank; - } - - /** - * Adds the string to the kill-ring. Also sets lastYank to false - * and lastKill to true. - */ - public void add(String str) { - lastYank = false; - - if (lastKill) { - if (slots[head] != null) { - slots[head] += str; - return; - } - } - - lastKill = true; - next(); - slots[head] = str; - } - - /** - * Adds the string to the kill-ring product of killing - * backwards. If the previous command was a kill text one then - * adds the text at the beginning of the previous kill to avoid - * that two consecutive backwards kills followed by a yank leaves - * things reversed. - */ - public void addBackwards(String str) { - lastYank = false; - - if (lastKill) { - if (slots[head] != null) { - slots[head] = str + slots[head]; - return; - } - } - - lastKill = true; - next(); - slots[head] = str; - } - - /** - * Yanks a previously killed text. Returns {@code null} if the - * ring is empty. - */ - public String yank() { - lastKill = false; - lastYank = true; - return slots[head]; - } - - /** - * Moves the pointer to the current slot back and returns the text - * in that position. If the previous command was not yank returns - * null. - */ - public String yankPop() { - lastKill = false; - if (lastYank) { - prev(); - return slots[head]; - } - return null; - } - - /** - * Moves the pointer to the current slot forward. If the end of - * the slots is reached then points back to the beginning. - */ - private void next() { - if (head == 0 && slots[0] == null) { - return; - } - head++; - if (head == slots.length) { - head = 0; - } - } - - /** - * Moves the pointer to the current slot backwards. If the - * beginning of the slots is reached then traverses the slot - * backwards until one with not null content is found. - */ - private void prev() { - head--; - if (head == -1) { - int x = slots.length - 1; - for (; x >= 0; x--) { - if (slots[x] != null) { - break; - } - } - head = x; - } - } -} diff --git a/kshell-console-jline2/src/main/java/lib/jline/console/Operation.java b/kshell-console-jline2/src/main/java/lib/jline/console/Operation.java deleted file mode 100644 index ae70a22..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/console/Operation.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline.console; - -/** - * List of all operations. - * - * @author Guillaume Nodet - * @since 2.6 - */ -public enum Operation { - - ABORT, - ACCEPT_LINE, - ARROW_KEY_PREFIX, - BACKWARD_BYTE, - BACKWARD_CHAR, - BACKWARD_DELETE_CHAR, - BACKWARD_KILL_LINE, - BACKWARD_KILL_WORD, - BACKWARD_WORD, - BEGINNING_OF_HISTORY, - BEGINNING_OF_LINE, - CALL_LAST_KBD_MACRO, - CAPITALIZE_WORD, - CHARACTER_SEARCH, - CHARACTER_SEARCH_BACKWARD, - CLEAR_SCREEN, - COMPLETE, - COPY_BACKWARD_WORD, - COPY_FORWARD_WORD, - COPY_REGION_AS_KILL, - DELETE_CHAR, - DELETE_CHAR_OR_LIST, - DELETE_HORIZONTAL_SPACE, - DIGIT_ARGUMENT, - DO_LOWERCASE_VERSION, - DOWNCASE_WORD, - DUMP_FUNCTIONS, - DUMP_MACROS, - DUMP_VARIABLES, - EMACS_EDITING_MODE, - END_KBD_MACRO, - END_OF_HISTORY, - END_OF_LINE, - EXCHANGE_POINT_AND_MARK, - EXIT_OR_DELETE_CHAR, - FORWARD_BACKWARD_DELETE_CHAR, - FORWARD_BYTE, - FORWARD_CHAR, - FORWARD_SEARCH_HISTORY, - FORWARD_WORD, - HISTORY_SEARCH_BACKWARD, - HISTORY_SEARCH_FORWARD, - INSERT_CLOSE_CURLY, - INSERT_CLOSE_PAREN, - INSERT_CLOSE_SQUARE, - INSERT_COMMENT, - INSERT_COMPLETIONS, - INTERRUPT, - KILL_WHOLE_LINE, - KILL_LINE, - KILL_REGION, - KILL_WORD, - MENU_COMPLETE, - MENU_COMPLETE_BACKWARD, - NEXT_HISTORY, - NON_INCREMENTAL_FORWARD_SEARCH_HISTORY, - NON_INCREMENTAL_REVERSE_SEARCH_HISTORY, - NON_INCREMENTAL_FORWARD_SEARCH_HISTORY_AGAIN, - NON_INCREMENTAL_REVERSE_SEARCH_HISTORY_AGAIN, - OLD_MENU_COMPLETE, - OVERWRITE_MODE, - PASTE_FROM_CLIPBOARD, - POSSIBLE_COMPLETIONS, - PREVIOUS_HISTORY, - QUOTED_INSERT, - QUIT, - RE_READ_INIT_FILE, - REDRAW_CURRENT_LINE, - REVERSE_SEARCH_HISTORY, - REVERT_LINE, - SELF_INSERT, - SET_MARK, - SKIP_CSI_SEQUENCE, - START_KBD_MACRO, - TAB_INSERT, - TILDE_EXPAND, - TRANSPOSE_CHARS, - TRANSPOSE_WORDS, - TTY_STATUS, - UNDO, - UNIVERSAL_ARGUMENT, - UNIX_FILENAME_RUBOUT, - UNIX_LINE_DISCARD, - UNIX_WORD_RUBOUT, - UPCASE_WORD, - YANK, - YANK_LAST_ARG, - YANK_NTH_ARG, - YANK_POP, - VI_APPEND_EOL, - VI_APPEND_MODE, - VI_ARG_DIGIT, - VI_BACK_TO_INDENT, - VI_BACKWARD_BIGWORD, - VI_BACKWARD_WORD, - VI_BWORD, - VI_CHANGE_CASE, - VI_CHANGE_CHAR, - VI_CHANGE_TO, - VI_CHANGE_TO_EOL, - VI_CHAR_SEARCH, - VI_COLUMN, - VI_COMPLETE, - VI_DELETE, - VI_DELETE_TO, - VI_DELETE_TO_EOL, - VI_EDITING_MODE, - VI_END_BIGWORD, - VI_END_WORD, - VI_EOF_MAYBE, - VI_EWORD, - VI_FWORD, - VI_FETCH_HISTORY, - VI_FIRST_PRINT, - VI_FORWARD_BIGWORD, - VI_FORWARD_WORD, - VI_GOTO_MARK, - VI_INSERT_BEG, - VI_INSERTION_MODE, - VI_KILL_WHOLE_LINE, - VI_MATCH, - VI_MOVEMENT_MODE, - VI_NEXT_WORD, - VI_OVERSTRIKE, - VI_OVERSTRIKE_DELETE, - VI_PREV_WORD, - VI_PUT, - VI_REDO, - VI_REPLACE, - VI_RUBOUT, - VI_SEARCH, - VI_SEARCH_AGAIN, - VI_SET_MARK, - VI_SUBST, - VI_TILDE_EXPAND, - VI_YANK_ARG, - VI_YANK_TO, - VI_MOVE_ACCEPT_LINE, - VI_NEXT_HISTORY, - VI_PREVIOUS_HISTORY, - VI_INSERT_COMMENT, - VI_BEGINNING_OF_LINE_OR_ARG_DIGIT, -} diff --git a/kshell-console-jline2/src/main/java/lib/jline/console/UserInterruptException.java b/kshell-console-jline2/src/main/java/lib/jline/console/UserInterruptException.java deleted file mode 100644 index d597859..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/console/UserInterruptException.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline.console; - -/** - * This exception is thrown by {@link ConsoleReader#readLine} when - * user interrupt handling is enabled and the user types the - * interrupt character (ctrl-C). The partially entered line is - * available via the {@link #getPartialLine()} method. - */ -public class UserInterruptException - extends RuntimeException -{ - private static final long serialVersionUID = 6172232572140736750L; - - private final String partialLine; - - public UserInterruptException(String partialLine) - { - this.partialLine = partialLine; - } - - /** - * @return the partially entered line when ctrl-C was pressed - */ - public String getPartialLine() - { - return partialLine; - } -} diff --git a/kshell-console-jline2/src/main/java/lib/jline/console/WCWidth.java b/kshell-console-jline2/src/main/java/lib/jline/console/WCWidth.java deleted file mode 100644 index 592860e..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/console/WCWidth.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline.console; - -public class WCWidth { - - /* The following two functions define the column width of an ISO 10646 - * character as follows: - * - * - The null character (U+0000) has a column width of 0. - * - * - Other C0/C1 control characters and DEL will lead to a return - * value of -1. - * - * - Non-spacing and enclosing combining characters (general - * category code Mn or Me in the Unicode database) have a - * column width of 0. - * - * - SOFT HYPHEN (U+00AD) has a column width of 1. - * - * - Other format characters (general category code Cf in the Unicode - * database) and ZERO WIDTH SPACE (U+200B) have a column width of 0. - * - * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF) - * have a column width of 0. - * - * - Spacing characters in the East Asian Wide (W) or East Asian - * Full-width (F) category as defined in Unicode Technical - * Report #11 have a column width of 2. - * - * - All remaining characters (including all printable - * ISO 8859-1 and WGL4 characters, Unicode control characters, - * etc.) have a column width of 1. - * - * This implementation assumes that wchar_t characters are encoded - * in ISO 10646. - */ - public static int wcwidth(int ucs) - { - - /* test for 8-bit control characters */ - if (ucs == 0) - return 0; - if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) - return -1; - - /* binary search in table of non-spacing characters */ - if (bisearch(ucs, combining, combining.length - 1)) - return 0; - - /* if we arrive here, ucs is not a combining or C0/C1 control character */ - return 1 + - ((ucs >= 0x1100 && - (ucs <= 0x115f || /* Hangul Jamo init. consonants */ - ucs == 0x2329 || ucs == 0x232a || - (ucs >= 0x2e80 && ucs <= 0xa4cf && - ucs != 0x303f) || /* CJK ... Yi */ - (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */ - (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */ - (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */ - (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */ - (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */ - (ucs >= 0xffe0 && ucs <= 0xffe6) || - (ucs >= 0x20000 && ucs <= 0x2fffd) || - (ucs >= 0x30000 && ucs <= 0x3fffd))) ? 1 : 0); - } - - /* sorted list of non-overlapping intervals of non-spacing characters */ - /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */ - static Interval[] combining = { - new Interval( 0x0300, 0x036F ), new Interval( 0x0483, 0x0486 ), new Interval( 0x0488, 0x0489 ), - new Interval( 0x0591, 0x05BD ), new Interval( 0x05BF, 0x05BF ), new Interval( 0x05C1, 0x05C2 ), - new Interval( 0x05C4, 0x05C5 ), new Interval( 0x05C7, 0x05C7 ), new Interval( 0x0600, 0x0603 ), - new Interval( 0x0610, 0x0615 ), new Interval( 0x064B, 0x065E ), new Interval( 0x0670, 0x0670 ), - new Interval( 0x06D6, 0x06E4 ), new Interval( 0x06E7, 0x06E8 ), new Interval( 0x06EA, 0x06ED ), - new Interval( 0x070F, 0x070F ), new Interval( 0x0711, 0x0711 ), new Interval( 0x0730, 0x074A ), - new Interval( 0x07A6, 0x07B0 ), new Interval( 0x07EB, 0x07F3 ), new Interval( 0x0901, 0x0902 ), - new Interval( 0x093C, 0x093C ), new Interval( 0x0941, 0x0948 ), new Interval( 0x094D, 0x094D ), - new Interval( 0x0951, 0x0954 ), new Interval( 0x0962, 0x0963 ), new Interval( 0x0981, 0x0981 ), - new Interval( 0x09BC, 0x09BC ), new Interval( 0x09C1, 0x09C4 ), new Interval( 0x09CD, 0x09CD ), - new Interval( 0x09E2, 0x09E3 ), new Interval( 0x0A01, 0x0A02 ), new Interval( 0x0A3C, 0x0A3C ), - new Interval( 0x0A41, 0x0A42 ), new Interval( 0x0A47, 0x0A48 ), new Interval( 0x0A4B, 0x0A4D ), - new Interval( 0x0A70, 0x0A71 ), new Interval( 0x0A81, 0x0A82 ), new Interval( 0x0ABC, 0x0ABC ), - new Interval( 0x0AC1, 0x0AC5 ), new Interval( 0x0AC7, 0x0AC8 ), new Interval( 0x0ACD, 0x0ACD ), - new Interval( 0x0AE2, 0x0AE3 ), new Interval( 0x0B01, 0x0B01 ), new Interval( 0x0B3C, 0x0B3C ), - new Interval( 0x0B3F, 0x0B3F ), new Interval( 0x0B41, 0x0B43 ), new Interval( 0x0B4D, 0x0B4D ), - new Interval( 0x0B56, 0x0B56 ), new Interval( 0x0B82, 0x0B82 ), new Interval( 0x0BC0, 0x0BC0 ), - new Interval( 0x0BCD, 0x0BCD ), new Interval( 0x0C3E, 0x0C40 ), new Interval( 0x0C46, 0x0C48 ), - new Interval( 0x0C4A, 0x0C4D ), new Interval( 0x0C55, 0x0C56 ), new Interval( 0x0CBC, 0x0CBC ), - new Interval( 0x0CBF, 0x0CBF ), new Interval( 0x0CC6, 0x0CC6 ), new Interval( 0x0CCC, 0x0CCD ), - new Interval( 0x0CE2, 0x0CE3 ), new Interval( 0x0D41, 0x0D43 ), new Interval( 0x0D4D, 0x0D4D ), - new Interval( 0x0DCA, 0x0DCA ), new Interval( 0x0DD2, 0x0DD4 ), new Interval( 0x0DD6, 0x0DD6 ), - new Interval( 0x0E31, 0x0E31 ), new Interval( 0x0E34, 0x0E3A ), new Interval( 0x0E47, 0x0E4E ), - new Interval( 0x0EB1, 0x0EB1 ), new Interval( 0x0EB4, 0x0EB9 ), new Interval( 0x0EBB, 0x0EBC ), - new Interval( 0x0EC8, 0x0ECD ), new Interval( 0x0F18, 0x0F19 ), new Interval( 0x0F35, 0x0F35 ), - new Interval( 0x0F37, 0x0F37 ), new Interval( 0x0F39, 0x0F39 ), new Interval( 0x0F71, 0x0F7E ), - new Interval( 0x0F80, 0x0F84 ), new Interval( 0x0F86, 0x0F87 ), new Interval( 0x0F90, 0x0F97 ), - new Interval( 0x0F99, 0x0FBC ), new Interval( 0x0FC6, 0x0FC6 ), new Interval( 0x102D, 0x1030 ), - new Interval( 0x1032, 0x1032 ), new Interval( 0x1036, 0x1037 ), new Interval( 0x1039, 0x1039 ), - new Interval( 0x1058, 0x1059 ), new Interval( 0x1160, 0x11FF ), new Interval( 0x135F, 0x135F ), - new Interval( 0x1712, 0x1714 ), new Interval( 0x1732, 0x1734 ), new Interval( 0x1752, 0x1753 ), - new Interval( 0x1772, 0x1773 ), new Interval( 0x17B4, 0x17B5 ), new Interval( 0x17B7, 0x17BD ), - new Interval( 0x17C6, 0x17C6 ), new Interval( 0x17C9, 0x17D3 ), new Interval( 0x17DD, 0x17DD ), - new Interval( 0x180B, 0x180D ), new Interval( 0x18A9, 0x18A9 ), new Interval( 0x1920, 0x1922 ), - new Interval( 0x1927, 0x1928 ), new Interval( 0x1932, 0x1932 ), new Interval( 0x1939, 0x193B ), - new Interval( 0x1A17, 0x1A18 ), new Interval( 0x1B00, 0x1B03 ), new Interval( 0x1B34, 0x1B34 ), - new Interval( 0x1B36, 0x1B3A ), new Interval( 0x1B3C, 0x1B3C ), new Interval( 0x1B42, 0x1B42 ), - new Interval( 0x1B6B, 0x1B73 ), new Interval( 0x1DC0, 0x1DCA ), new Interval( 0x1DFE, 0x1DFF ), - new Interval( 0x200B, 0x200F ), new Interval( 0x202A, 0x202E ), new Interval( 0x2060, 0x2063 ), - new Interval( 0x206A, 0x206F ), new Interval( 0x20D0, 0x20EF ), new Interval( 0x302A, 0x302F ), - new Interval( 0x3099, 0x309A ), new Interval( 0xA806, 0xA806 ), new Interval( 0xA80B, 0xA80B ), - new Interval( 0xA825, 0xA826 ), new Interval( 0xFB1E, 0xFB1E ), new Interval( 0xFE00, 0xFE0F ), - new Interval( 0xFE20, 0xFE23 ), new Interval( 0xFEFF, 0xFEFF ), new Interval( 0xFFF9, 0xFFFB ), - new Interval( 0x10A01, 0x10A03 ), new Interval( 0x10A05, 0x10A06 ), new Interval( 0x10A0C, 0x10A0F ), - new Interval( 0x10A38, 0x10A3A ), new Interval( 0x10A3F, 0x10A3F ), new Interval( 0x1D167, 0x1D169 ), - new Interval( 0x1D173, 0x1D182 ), new Interval( 0x1D185, 0x1D18B ), new Interval( 0x1D1AA, 0x1D1AD ), - new Interval( 0x1D242, 0x1D244 ), new Interval( 0xE0001, 0xE0001 ), new Interval( 0xE0020, 0xE007F ), - new Interval( 0xE0100, 0xE01EF ) - }; - - private static class Interval { - public final int first; - public final int last; - - public Interval(int first, int last) { - this.first = first; - this.last = last; - } - } - - /* auxiliary function for binary search in interval table */ - private static boolean bisearch(int ucs, Interval[] table, int max) { - int min = 0; - int mid; - - if (ucs < table[0].first || ucs > table[max].last) - return false; - while (max >= min) { - mid = (min + max) / 2; - if (ucs > table[mid].last) - min = mid + 1; - else if (ucs < table[mid].first) - max = mid - 1; - else - return true; - } - - return false; - } - - -} diff --git a/kshell-console-jline2/src/main/java/lib/jline/console/completer/AggregateCompleter.java b/kshell-console-jline2/src/main/java/lib/jline/console/completer/AggregateCompleter.java deleted file mode 100644 index 4e05637..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/console/completer/AggregateCompleter.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.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 Jason Dillon - * @since 2.3 - */ -public class AggregateCompleter - implements Completer -{ - private final List completers = new ArrayList(); - - public AggregateCompleter() { - // empty - } - - /** - * Construct an AggregateCompleter with the given collection of completers. - * The completers will be used in the iteration order of the collection. - * - * @param completers the collection of completers - */ - public AggregateCompleter(final Collection completers) { - lib.jline.internal.Preconditions.checkNotNull(completers); - this.completers.addAll(completers); - } - - /** - * Construct an AggregateCompleter with the given completers. - * The completers will be used in the order given. - * - * @param completers the completers - */ - public AggregateCompleter(final Completer... completers) { - this(Arrays.asList(completers)); - } - - /** - * Retrieve the collection of completers currently being aggregated. - * - * @return the aggregated completers - */ - public Collection getCompleters() { - return completers; - } - - /** - * Perform a completion operation across all aggregated completers. - * - * @see Completer#complete(String, int, java.util.List) - * @return the highest completion return value from all completers - */ - public int complete(final String buffer, final int cursor, final List candidates) { - // buffer could be null - lib.jline.internal.Preconditions.checkNotNull(candidates); - - List completions = new ArrayList(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; - } - - /** - * @return a string representing the aggregated completers - */ - @Override - public String toString() { - return getClass().getSimpleName() + "{" + - "completers=" + completers + - '}'; - } - - private class Completion - { - public final List candidates; - - public int cursor; - - public Completion(final List candidates) { - lib.jline.internal.Preconditions.checkNotNull(candidates); - this.candidates = new LinkedList(candidates); - } - - public void complete(final Completer completer, final String buffer, final int cursor) { - lib.jline.internal.Preconditions.checkNotNull(completer); - this.cursor = completer.complete(buffer, cursor, candidates); - } - } -} diff --git a/kshell-console-jline2/src/main/java/lib/jline/console/completer/AnsiStringsCompleter.java b/kshell-console-jline2/src/main/java/lib/jline/console/completer/AnsiStringsCompleter.java deleted file mode 100644 index 2d61f17..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/console/completer/AnsiStringsCompleter.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline.console.completer; - -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.SortedMap; -import java.util.TreeMap; - -import lib.jline.internal.Ansi; - -/** - * Completer for a set of strings. - * - * @author Jason Dillon - * @since 2.3 - */ -public class AnsiStringsCompleter - implements Completer -{ - private final SortedMap strings = new TreeMap(); - - public AnsiStringsCompleter() { - // empty - } - - public AnsiStringsCompleter(final Collection strings) { - lib.jline.internal.Preconditions.checkNotNull(strings); - for (String str : strings) { - this.strings.put(Ansi.stripAnsi(str), str); - } - } - - public AnsiStringsCompleter(final String... strings) { - this(Arrays.asList(strings)); - } - - public Collection getStrings() { - return strings.values(); - } - - public int complete(String buffer, final int cursor, final List candidates) { - // buffer could be null - lib.jline.internal.Preconditions.checkNotNull(candidates); - - if (buffer == null) { - candidates.addAll(strings.values()); - } - else { - buffer = Ansi.stripAnsi(buffer); - for (Map.Entry match : strings.tailMap(buffer).entrySet()) { - if (!match.getKey().startsWith(buffer)) { - break; - } - - candidates.add(match.getValue()); - } - } - - return candidates.isEmpty() ? -1 : 0; - } -} \ No newline at end of file diff --git a/kshell-console-jline2/src/main/java/lib/jline/console/completer/ArgumentCompleter.java b/kshell-console-jline2/src/main/java/lib/jline/console/completer/ArgumentCompleter.java deleted file mode 100644 index 4e9d72e..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/console/completer/ArgumentCompleter.java +++ /dev/null @@ -1,456 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline.console.completer; - -import lib.jline.internal.Preconditions; -import lib.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 separator argument. - * This can be used instead of the individual completers having to know about argument parsing semantics. - * - * @author Marc Prud'hommeaux - * @author Jason Dillon - * @since 2.3 - */ -public class ArgumentCompleter - implements Completer -{ - private final ArgumentDelimiter delimiter; - - private final List completers = new ArrayList(); - - 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 completers) { - this.delimiter = Preconditions.checkNotNull(delimiter); - Preconditions.checkNotNull(completers); - 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 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 getCompleters() { - return completers; - } - - public int complete(final String buffer, final int cursor, final List candidates) { - // buffer can be null - Preconditions.checkNotNull(candidates); - - ArgumentDelimiter delim = getDelimiter(); - ArgumentList list = delim.delimit(buffer, cursor); - int argpos = list.getArgumentPosition(); - int argIndex = list.getCursorArgumentIndex(); - - if (argIndex < 0) { - return -1; - } - - List 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 subCandidates = new LinkedList(); - - if (sub.complete(arg, arg.length(), subCandidates) == -1) { - return -1; - } - - if (!subCandidates.contains(arg)) { - 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 Marc Prud'hommeaux - */ - 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 Marc Prud'hommeaux - */ - public abstract static class AbstractArgumentDelimiter - implements ArgumentDelimiter - { - 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 args = new LinkedList(); - StringBuilder arg = new StringBuilder(); - int argpos = -1; - int bindex = -1; - int quoteStart = -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 (quoteStart < 0 && isQuoteChar(buffer, i)) { - // Start a quote block - quoteStart = i; - } else if (quoteStart >= 0) { - // In a quote block - if (buffer.charAt(quoteStart) == buffer.charAt(i) && !isEscaped(buffer, i)) { - // End the block; arg could be empty, but that's fine - args.add(arg.toString()); - arg.setLength(0); - quoteStart = -1; - } else if (!isEscapeChar(buffer, i)) { - // Take the next character - arg.append(buffer.charAt(i)); - } - } else { - // Not in a quote block - if (isDelimiter(buffer, i)) { - if (arg.length() > 0) { - args.add(arg.toString()); - arg.setLength(0); // reset the arg - } - } else if (!isEscapeChar(buffer, i)) { - arg.append(buffer.charAt(i)); - } - } - } - - if (cursor == buffer.length()) { - bindex = args.size(); - // the position in the current argument is just the - // length of the current argument - argpos = arg.length(); - } - if (arg.length() > 0) { - args.add(arg.toString()); - } - - 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 isQuoteChar(final CharSequence buffer, final int pos) { - if (pos < 0) { - return false; - } - - for (int i = 0; (quoteChars != null) && (i < quoteChars.length); i++) { - if (buffer.charAt(pos) == quoteChars[i]) { - return !isEscaped(buffer, pos); - } - } - - return false; - } - - /** - * Check if this character is a valid escape char (i.e. one that has not been escaped) - */ - public boolean isEscapeChar(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); // escape escape - } - } - - return false; - } - - /** - * Check if a character is escaped (i.e. if the previous character is an escape) - * - * @param buffer - * the buffer to check in - * @param pos - * the position of the character to check - * @return true if the character at the specified position in the given buffer is an escape character and the character immediately preceding it is not an - * escape character. - */ - public boolean isEscaped(final CharSequence buffer, final int pos) { - if (pos <= 0) { - return false; - } - - return isEscapeChar(buffer, pos - 1); - } - - /** - * 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 Marc Prud'hommeaux - */ - 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 Marc Prud'hommeaux - */ - 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) { - this.arguments = Preconditions.checkNotNull(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/kshell-console-jline2/src/main/java/lib/jline/console/completer/CandidateListCompletionHandler.java b/kshell-console-jline2/src/main/java/lib/jline/console/completer/CandidateListCompletionHandler.java deleted file mode 100644 index b1d2f60..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/console/completer/CandidateListCompletionHandler.java +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline.console.completer; - -import lib.jline.internal.Ansi; -import lib.jline.console.ConsoleReader; - -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 - * readline library. - * - * @author Marc Prud'hommeaux - * @author Jason Dillon - * @since 2.3 - */ -public class CandidateListCompletionHandler - implements CompletionHandler -{ - private boolean printSpaceAfterFullCompletion = true; - private boolean stripAnsi; - - public boolean getPrintSpaceAfterFullCompletion() { - return printSpaceAfterFullCompletion; - } - - public void setPrintSpaceAfterFullCompletion(boolean printSpaceAfterFullCompletion) { - this.printSpaceAfterFullCompletion = printSpaceAfterFullCompletion; - } - - public boolean isStripAnsi() { - return stripAnsi; - } - - public void setStripAnsi(boolean stripAnsi) { - this.stripAnsi = stripAnsi; - } - - // TODO: handle quotes and escaped quotes && enable automatic escaping of whitespace - - public boolean complete(final ConsoleReader reader, final List candidates, final int pos) throws - IOException - { - lib.jline.console.CursorBuffer buf = reader.getCursorBuffer(); - - // if there is only one completion, then fill in the buffer - if (candidates.size() == 1) { - String value = Ansi.stripAnsi(candidates.get(0).toString()); - - if (buf.cursor == buf.buffer.length() - && printSpaceAfterFullCompletion - && !value.endsWith(" ")) { - value += " "; - } - - // 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 candidates) throws - IOException - { - Set distinct = new HashSet(candidates); - - if (distinct.size() > reader.getAutoprintThreshold()) { - //noinspection StringConcatenation - reader.println(); - reader.print(Messages.DISPLAY_CANDIDATES.format(distinct.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 copy = new ArrayList(); - - 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 - * foobar, foobaz, foobuz, the method will return foob. - */ - private String getUnambiguousCompletions(final List candidates) { - if (candidates == null || candidates.isEmpty()) { - return null; - } - - if (candidates.size() == 1) { - return candidates.get(0).toString(); - } - - // convert to an array for speed - String first = null; - String[] strings = new String[candidates.size() - 1]; - for (int i = 0; i < candidates.size(); i++) { - String str = candidates.get(i).toString(); - if (stripAnsi) { - str = Ansi.stripAnsi(str); - } - if (first == null) { - first = str; - } else { - strings[i - 1] = str; - } - } - - 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 candidates start with starts - */ - private static boolean startsWith(final String starts, final String[] candidates) { - for (String candidate : candidates) { - if (!candidate.toLowerCase().startsWith(starts.toLowerCase())) { - 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/kshell-console-jline2/src/main/java/lib/jline/console/completer/Completer.java b/kshell-console-jline2/src/main/java/lib/jline/console/completer/Completer.java deleted file mode 100644 index a928a48..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/console/completer/Completer.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline.console.completer; - -import java.util.List; - -/** - * A completer is the mechanism by which tab-completion candidates will be resolved. - * - * @author Marc Prud'hommeaux - * @author Jason Dillon - * @since 2.3 - */ -public interface Completer -{ - // - // FIXME: Check if we can use CharSequece for buffer? - // - - /** - * Populates candidates with a list of possible completions for the buffer. - * - * The candidates 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 buffer - * @param candidates The {@link List} of candidates to populate - * @return The index of the buffer for which the completion will be relative - */ - int complete(String buffer, int cursor, List candidates); -} diff --git a/kshell-console-jline2/src/main/java/lib/jline/console/completer/CompletionHandler.java b/kshell-console-jline2/src/main/java/lib/jline/console/completer/CompletionHandler.java deleted file mode 100644 index dc1dc52..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/console/completer/CompletionHandler.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline.console.completer; - -import lib.jline.console.ConsoleReader; - -import java.io.IOException; -import java.util.List; - -/** - * Handler for dealing with candidates for tab-completion. - * - * @author Marc Prud'hommeaux - * @author Jason Dillon - * @since 2.3 - */ -public interface CompletionHandler -{ - boolean complete(ConsoleReader reader, List candidates, int position) throws IOException; -} diff --git a/kshell-console-jline2/src/main/java/lib/jline/console/completer/EnumCompleter.java b/kshell-console-jline2/src/main/java/lib/jline/console/completer/EnumCompleter.java deleted file mode 100644 index 44352f9..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/console/completer/EnumCompleter.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline.console.completer; - -/** - * {@link Completer} for {@link Enum} names. - * - * @author Jason Dillon - * @since 2.3 - */ -public class EnumCompleter - extends StringsCompleter -{ - public EnumCompleter(Class> source) { - this(source, true); - } - - public EnumCompleter(Class> source, boolean toLowerCase) { - lib.jline.internal.Preconditions.checkNotNull(source); - - for (Enum n : source.getEnumConstants()) { - this.getStrings().add(toLowerCase ? n.name().toLowerCase() : n.name()); - } - } -} \ No newline at end of file diff --git a/kshell-console-jline2/src/main/java/lib/jline/console/completer/FileNameCompleter.java b/kshell-console-jline2/src/main/java/lib/jline/console/completer/FileNameCompleter.java deleted file mode 100644 index 113f6f9..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/console/completer/FileNameCompleter.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline.console.completer; - -import lib.jline.internal.Configuration; -import lib.jline.internal.Preconditions; - -import java.io.File; -import java.util.List; - -/** - * A file name completer takes the buffer and issues a list of - * potential completions. - *

- * This completer tries to behave as similar as possible to - * bash's file name completion (using GNU readline) - * with the following exceptions: - *

- *

    - *
  • Candidates that are directories will end with "/"
  • - *
  • Wildcard regular expressions are not evaluated or replaced
  • - *
  • 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
  • - *
- * - * @author Marc Prud'hommeaux - * @author Jason Dillon - * @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 candidates) { - // buffer can be null - Preconditions.checkNotNull(candidates); - - 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 (!(new File(translated).isAbsolute())) { - 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 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) { - return name; - } -} diff --git a/kshell-console-jline2/src/main/java/lib/jline/console/completer/NullCompleter.java b/kshell-console-jline2/src/main/java/lib/jline/console/completer/NullCompleter.java deleted file mode 100644 index 8a2c89b..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/console/completer/NullCompleter.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline.console.completer; - -import java.util.List; - -/** - * Null completer. - * - * @author Marc Prud'hommeaux - * @author Jason Dillon - * @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 candidates) { - return -1; - } -} \ No newline at end of file diff --git a/kshell-console-jline2/src/main/java/lib/jline/console/completer/StringsCompleter.java b/kshell-console-jline2/src/main/java/lib/jline/console/completer/StringsCompleter.java deleted file mode 100644 index 1107611..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/console/completer/StringsCompleter.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.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 Jason Dillon - * @since 2.3 - */ -public class StringsCompleter - implements Completer -{ - private final SortedSet strings = new TreeSet(); - - public StringsCompleter() { - // empty - } - - public StringsCompleter(final Collection strings) { - lib.jline.internal.Preconditions.checkNotNull(strings); - getStrings().addAll(strings); - } - - public StringsCompleter(final String... strings) { - this(Arrays.asList(strings)); - } - - public Collection getStrings() { - return strings; - } - - public int complete(final String buffer, final int cursor, final List candidates) { - // buffer could be null - lib.jline.internal.Preconditions.checkNotNull(candidates); - - if (buffer == null) { - candidates.addAll(strings); - } - else { - for (String match : strings.tailSet(buffer)) { - if (!match.startsWith(buffer)) { - break; - } - - candidates.add(match); - } - } - - return candidates.isEmpty() ? -1 : 0; - } -} \ No newline at end of file diff --git a/kshell-console-jline2/src/main/java/lib/jline/console/completer/package-info.java b/kshell-console-jline2/src/main/java/lib/jline/console/completer/package-info.java deleted file mode 100644 index 86795c5..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/console/completer/package-info.java +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -/** - * Console completer support. - * - * @since 2.3 - */ -package lib.jline.console.completer; \ No newline at end of file diff --git a/kshell-console-jline2/src/main/java/lib/jline/console/history/FileHistory.java b/kshell-console-jline2/src/main/java/lib/jline/console/history/FileHistory.java deleted file mode 100644 index 15e72ad..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/console/history/FileHistory.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline.console.history; - -import lib.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; - -/** - * {@link History} using a file for persistent backing. - *

- * Implementers should install shutdown hook to call {@link FileHistory#flush} - * to save history to disk. - * - * @author Jason Dillon - * @since 2.0 - */ -public class FileHistory - extends MemoryHistory - implements lib.jline.console.history.PersistentHistory, Flushable -{ - private final File file; - - public FileHistory(final File file) throws IOException { - this.file = lib.jline.internal.Preconditions.checkNotNull(file).getAbsoluteFile(); - load(file); - } - - public File getFile() { - return file; - } - - public void load(final File file) throws IOException { - lib.jline.internal.Preconditions.checkNotNull(file); - if (file.exists()) { - Log.trace("Loading history from: ", file); - FileReader reader = null; - try{ - reader = new FileReader(file); - load(reader); - } finally{ - if(reader != null){ - reader.close(); - } - } - } - } - - public void load(final InputStream input) throws IOException { - lib.jline.internal.Preconditions.checkNotNull(input); - load(new InputStreamReader(input)); - } - - public void load(final Reader reader) throws IOException { - lib.jline.internal.Preconditions.checkNotNull(reader); - BufferedReader input = new BufferedReader(reader); - - String item; - while ((item = input.readLine()) != null) { - internalAdd(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/kshell-console-jline2/src/main/java/lib/jline/console/history/History.java b/kshell-console-jline2/src/main/java/lib/jline/console/history/History.java deleted file mode 100644 index ed625a9..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/console/history/History.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline.console.history; - -import java.util.Iterator; -import java.util.ListIterator; - -/** - * Console history. - * - * @author Marc Prud'hommeaux - * @author Jason Dillon - * @since 2.3 - */ -public interface History - extends Iterable -{ - int size(); - - boolean isEmpty(); - - int index(); - - void clear(); - - CharSequence get(int index); - - void add(CharSequence line); - - /** - * Set the history item at the given index to the given CharSequence. - * - * @param index the index of the history offset - * @param item the new item - * @since 2.7 - */ - void set(int index, CharSequence item); - - /** - * Remove the history element at the given index. - * - * @param i the index of the element to remove - * @return the removed element - * @since 2.7 - */ - CharSequence remove(int i); - - /** - * Remove the first element from history. - * - * @return the removed element - * @since 2.7 - */ - CharSequence removeFirst(); - - /** - * Remove the last element from history - * - * @return the removed element - * @since 2.7 - */ - CharSequence removeLast(); - - void replace(CharSequence item); - - // - // Entries - // - - interface Entry - { - int index(); - - CharSequence value(); - } - - ListIterator entries(int index); - - ListIterator entries(); - - Iterator iterator(); - - // - // Navigation - // - - CharSequence current(); - - boolean previous(); - - boolean next(); - - boolean moveToFirst(); - - boolean moveToLast(); - - boolean moveTo(int index); - - void moveToEnd(); -} diff --git a/kshell-console-jline2/src/main/java/lib/jline/console/history/MemoryHistory.java b/kshell-console-jline2/src/main/java/lib/jline/console/history/MemoryHistory.java deleted file mode 100644 index 427ec79..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/console/history/MemoryHistory.java +++ /dev/null @@ -1,344 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline.console.history; - -import java.util.Iterator; -import java.util.LinkedList; -import java.util.ListIterator; -import java.util.NoSuchElementException; - -/** - * Non-persistent {@link History}. - * - * @author Marc Prud'hommeaux - * @author Jason Dillon - * @since 2.3 - */ -public class MemoryHistory - implements History -{ - public static final int DEFAULT_MAX_SIZE = 500; - - private final LinkedList items = new LinkedList(); - - 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 set(int index, CharSequence item) { - items.set(index - offset, item); - } - - public void add(CharSequence item) { - lib.jline.internal.Preconditions.checkNotNull(item); - - if (isAutoTrim()) { - item = String.valueOf(item).trim(); - } - - if (isIgnoreDuplicates()) { - if (!items.isEmpty() && item.equals(items.getLast())) { - return; - } - } - - internalAdd(item); - } - - public CharSequence remove(int i) { - return items.remove(i); - } - - public CharSequence removeFirst() { - return items.removeFirst(); - } - - public CharSequence removeLast() { - return items.removeLast(); - } - - protected void internalAdd(CharSequence item) { - 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 entries(final int index) { - return new EntriesIterator(index - offset); - } - - public ListIterator entries() { - return entries(offset); - } - - public Iterator 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 - { - private final ListIterator 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 - */ - 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; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - for (Entry e : this) { - sb.append(e.toString() + "\n"); - } - return sb.toString(); - } -} diff --git a/kshell-console-jline2/src/main/java/lib/jline/console/history/PersistentHistory.java b/kshell-console-jline2/src/main/java/lib/jline/console/history/PersistentHistory.java deleted file mode 100644 index ebba200..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/console/history/PersistentHistory.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline.console.history; - -import java.io.IOException; - -/** - * Persistent {@link History}. - * - * @author Jason Dillon - * @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/kshell-console-jline2/src/main/java/lib/jline/console/history/package-info.java b/kshell-console-jline2/src/main/java/lib/jline/console/history/package-info.java deleted file mode 100644 index ab18fb8..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/console/history/package-info.java +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -/** - * Console history support. - * - * @since 2.0 - */ -package lib.jline.console.history; \ No newline at end of file diff --git a/kshell-console-jline2/src/main/java/lib/jline/console/internal/ConsoleReaderInputStream.java b/kshell-console-jline2/src/main/java/lib/jline/console/internal/ConsoleReaderInputStream.java deleted file mode 100644 index 7328dfa..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/console/internal/ConsoleReaderInputStream.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline.console.internal; - -import java.io.IOException; -import java.io.InputStream; -import java.io.SequenceInputStream; -import java.util.Enumeration; - -// FIXME: Clean up API and move to jline.console.runner package - -/** - * An {@link InputStream} implementation that wraps a {@link lib.jline.console.ConsoleReader}. - * It is useful for setting up the {@link System#in} for a generic console. - * - * @author Marc Prud'hommeaux - * @since 2.7 - */ -class ConsoleReaderInputStream - extends SequenceInputStream -{ - private static InputStream systemIn = System.in; - - public static void setIn() throws IOException { - setIn(new lib.jline.console.ConsoleReader()); - } - - public static void setIn(final lib.jline.console.ConsoleReader reader) { - System.setIn(new ConsoleReaderInputStream(reader)); - } - - /** - * Restore the original {@link System#in} input stream. - */ - public static void restoreIn() { - System.setIn(systemIn); - } - - public ConsoleReaderInputStream(final lib.jline.console.ConsoleReader reader) { - super(new ConsoleEnumeration(reader)); - } - - private static class ConsoleEnumeration - implements Enumeration - { - private final lib.jline.console.ConsoleReader reader; - private ConsoleLineInputStream next = null; - private ConsoleLineInputStream prev = null; - - public ConsoleEnumeration(final lib.jline.console.ConsoleReader reader) { - this.reader = reader; - } - - public InputStream nextElement() { - if (next != null) { - InputStream n = next; - prev = next; - next = null; - - return n; - } - - return new ConsoleLineInputStream(reader); - } - - public boolean hasMoreElements() { - // the last line was null - if ((prev != null) && (prev.wasNull == true)) { - return false; - } - - if (next == null) { - next = (ConsoleLineInputStream) nextElement(); - } - - return next != null; - } - } - - private static class ConsoleLineInputStream - extends InputStream - { - private final lib.jline.console.ConsoleReader reader; - private String line = null; - private int index = 0; - private boolean eol = false; - protected boolean wasNull = false; - - public ConsoleLineInputStream(final lib.jline.console.ConsoleReader reader) { - this.reader = reader; - } - - public int read() throws IOException { - if (eol) { - return -1; - } - - if (line == null) { - line = reader.readLine(); - } - - if (line == null) { - wasNull = true; - return -1; - } - - if (index >= line.length()) { - eol = true; - return '\n'; // lines are ended with a newline - } - - return line.charAt(index++); - } - } -} \ No newline at end of file diff --git a/kshell-console-jline2/src/main/java/lib/jline/console/internal/ConsoleRunner.java b/kshell-console-jline2/src/main/java/lib/jline/console/internal/ConsoleRunner.java deleted file mode 100644 index c12c693..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/console/internal/ConsoleRunner.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline.console.internal; - -import lib.jline.console.completer.ArgumentCompleter; - -import java.io.File; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.StringTokenizer; - -// FIXME: Clean up API and move to jline.console.runner package - -/** - * A pass-through application that sets the system input stream to a - * {@link lib.jline.console.ConsoleReader} and invokes the specified main method. - * - * @author Marc Prud'hommeaux - * @since 2.7 - */ -public class ConsoleRunner -{ - public static final String property = "jline.history"; - - // FIXME: This is really ugly... re-write this - - public static void main(final String[] args) throws Exception { - List argList = new ArrayList(Arrays.asList(args)); - if (argList.size() == 0) { - usage(); - return; - } - - String historyFileName = System.getProperty(ConsoleRunner.property, null); - - String mainClass = argList.remove(0); - lib.jline.console.ConsoleReader reader = new lib.jline.console.ConsoleReader(); - - if (historyFileName != null) { - reader.setHistory(new lib.jline.console.history.FileHistory(new File(lib.jline.internal.Configuration.getUserHome(), - String.format(".jline-%s.%s.history", mainClass, historyFileName)))); - } - else { - reader.setHistory(new lib.jline.console.history.FileHistory(new File(lib.jline.internal.Configuration.getUserHome(), - String.format(".jline-%s.history", mainClass)))); - } - - String completors = System.getProperty(ConsoleRunner.class.getName() + ".completers", ""); - List completorList = new ArrayList(); - - for (StringTokenizer tok = new StringTokenizer(completors, ","); tok.hasMoreTokens();) { - Object obj = Class.forName(tok.nextToken()).newInstance(); - completorList.add((lib.jline.console.completer.Completer) obj); - } - - if (completorList.size() > 0) { - reader.addCompleter(new ArgumentCompleter(completorList)); - } - - ConsoleReaderInputStream.setIn(reader); - - try { - Class type = Class.forName(mainClass); - Method method = type.getMethod("main", String[].class); - String[] mainArgs = argList.toArray(new String[argList.size()]); - method.invoke(null, (Object) mainArgs); - } - finally { - // just in case this main method is called from another program - ConsoleReaderInputStream.restoreIn(); - if (reader.getHistory() instanceof lib.jline.console.history.PersistentHistory) { - ((lib.jline.console.history.PersistentHistory) reader.getHistory()).flush(); - } - } - } - - private static void usage() { - System.out.println("Usage: \n java " + "[-Djline.history='name'] " - + ConsoleRunner.class.getName() - + " [args]" - + "\n\nThe -Djline.history option will avoid history" - + "\nmangling when running ConsoleRunner on the same application." - + "\n\nargs will be passed directly to the target class name."); - } -} diff --git a/kshell-console-jline2/src/main/java/lib/jline/console/package-info.java b/kshell-console-jline2/src/main/java/lib/jline/console/package-info.java deleted file mode 100644 index 37cb80d..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/console/package-info.java +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -/** - * Console support. - * - * @since 2.0 - */ -package lib.jline.console; \ No newline at end of file diff --git a/kshell-console-jline2/src/main/java/lib/jline/internal/Ansi.java b/kshell-console-jline2/src/main/java/lib/jline/internal/Ansi.java deleted file mode 100644 index 22ae1e0..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/internal/Ansi.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline.internal; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -import org.fusesource.jansi.AnsiOutputStream; - -/** - * Ansi support. - * - * @author Guillaume Nodet - * @since 2.13 - */ -public class Ansi { - - public static String stripAnsi(String str) { - if (str == null) return ""; - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - AnsiOutputStream aos = new AnsiOutputStream(baos); - aos.write(str.getBytes()); - aos.close(); - return baos.toString(); - } catch (IOException e) { - return str; - } - } - -} diff --git a/kshell-console-jline2/src/main/java/lib/jline/internal/Configuration.java b/kshell-console-jline2/src/main/java/lib/jline/internal/Configuration.java deleted file mode 100644 index 9b22dad..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/internal/Configuration.java +++ /dev/null @@ -1,259 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline.internal; - -import java.io.BufferedInputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.FileNotFoundException; -import java.net.URL; -import java.nio.charset.Charset; -import java.nio.charset.IllegalCharsetNameException; -import java.util.Map; -import java.util.Properties; - -/** - * Provides access to configuration values. - * - * @author Jason Dillon - * @author Guillaume Nodet - * @since 2.4 - */ -public class Configuration -{ - /** - * System property which can point to a file or URL containing configuration properties to load. - * - * @since 2.7 - */ - public static final String JLINE_CONFIGURATION = "jline.configuration"; - - /** - * Default configuration file name loaded from user's home directory. - */ - public static final String JLINE_RC = ".jline.rc"; - - private static volatile Properties properties; - - private static Properties initProperties() { - URL url = determineUrl(); - Properties props = new Properties(); - try { - loadProperties(url, props); - } - catch (FileNotFoundException e) { - // debug here and no stack trace, as this can happen normally if default jline.rc file is missing - Log.debug("Unable to read configuration: ", e.toString()); - } - catch (IOException e) { - Log.warn("Unable to read configuration from: ", url, e); - } - return props; - } - - private static void loadProperties(final URL url, final Properties props) throws IOException { - Log.debug("Loading properties from: ", url); - InputStream input = url.openStream(); - try { - props.load(new BufferedInputStream(input)); - } - finally { - try { - input.close(); - } - catch (IOException e) { - // ignore - } - } - - if (Log.DEBUG) { - Log.debug("Loaded properties:"); - for (Map.Entry entry : props.entrySet()) { - Log.debug(" ", entry.getKey(), "=", entry.getValue()); - } - } - } - - private static URL determineUrl() { - // See if user has customized the configuration location via sysprop - String tmp = System.getProperty(JLINE_CONFIGURATION); - if (tmp != null) { - return Urls.create(tmp); - } - else { - // Otherwise try the default - File file = new File(getUserHome(), JLINE_RC); - return Urls.create(file); - } - } - - /** - * @since 2.7 - */ - public static void reset() { - Log.debug("Resetting"); - properties = null; - - // force new properties to load - getProperties(); - } - - /** - * @since 2.7 - */ - public static Properties getProperties() { - // Not sure its worth to guard this with any synchronization, volatile field probably sufficient - if (properties == null) { - properties = initProperties(); - } - return properties; - } - - public static String getString(final String name, final String defaultValue) { - Preconditions.checkNotNull(name); - - String value; - - // Check sysprops first, it always wins - value = System.getProperty(name); - - if (value == null) { - // Next try userprops - value = getProperties().getProperty(name); - - if (value == null) { - // 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) { - return getBoolean(name, false); - } - - public static boolean getBoolean(final String name, final boolean defaultValue) { - String value = getString(name); - if (value == null) { - return defaultValue; - } - return value.length() == 0 - || value.equalsIgnoreCase("1") - || value.equalsIgnoreCase("on") - || value.equalsIgnoreCase("true"); - } - - /** - * @since 2.6 - */ - public static int getInteger(final String name, final int defaultValue) { - String str = getString(name); - if (str == null) { - return defaultValue; - } - return Integer.parseInt(str); - } - - /** - * @since 2.6 - */ - public static long getLong(final String name, final long defaultValue) { - String str = getString(name); - if (str == null) { - return defaultValue; - } - return Long.parseLong(str); - } - - // - // System property helpers - // - - /** - * @since 2.7 - */ - public static String getLineSeparator() { - return System.getProperty("line.separator"); - } - - public static File getUserHome() { - return new File(System.getProperty("user.home")); - } - - public static String getOsName() { - return System.getProperty("os.name").toLowerCase(); - } - - /** - * @since 2.7 - */ - public static boolean isWindows() { - return getOsName().startsWith("windows"); - } - - public static boolean isHpux() { - return getOsName().startsWith("hp"); - } - - // FIXME: Sort out use of property access of file.encoding in InputStreamReader, should consolidate configuration access here - - public static String getFileEncoding() { - return System.getProperty("file.encoding"); - } - - /** - * Get the default encoding. Will first look at the LC_ALL, LC_CTYPE, and LANG environment variables, then the input.encoding - * system property, then the default charset according to the JVM. - * - * @return The default encoding to use when none is specified. - */ - public static String getEncoding() { - // Check for standard locale environment variables, in order of precedence, first. - // See http://www.gnu.org/s/libc/manual/html_node/Locale-Categories.html - for (String envOption : new String[]{"LC_ALL", "LC_CTYPE", "LANG"}) { - String envEncoding = extractEncodingFromCtype(System.getenv(envOption)); - if (envEncoding != null) { - try { - if (Charset.isSupported(envEncoding)) { - return envEncoding; - } - } catch (IllegalCharsetNameException e) { - continue; - } - } - } - return getString("input.encoding", Charset.defaultCharset().name()); - } - - /** - * Parses the LC_CTYPE value to extract the encoding according to the POSIX standard, which says that the LC_CTYPE - * environment variable may be of the format [language[_territory][.codeset][@modifier]] - * - * @param ctype The ctype to parse, may be null - * @return The encoding, if one was present, otherwise null - */ - static String extractEncodingFromCtype(String ctype) { - if (ctype != null && ctype.indexOf('.') > 0) { - String encodingAndModifier = ctype.substring(ctype.indexOf('.') + 1); - if (encodingAndModifier.indexOf('@') > 0) { - return encodingAndModifier.substring(0, encodingAndModifier.indexOf('@')); - } else { - return encodingAndModifier; - } - } - return null; - } -} \ No newline at end of file diff --git a/kshell-console-jline2/src/main/java/lib/jline/internal/Curses.java b/kshell-console-jline2/src/main/java/lib/jline/internal/Curses.java deleted file mode 100644 index 53bdd83..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/internal/Curses.java +++ /dev/null @@ -1,341 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline.internal; - -import java.io.IOException; -import java.io.Writer; -import java.util.Stack; - -/** - * Curses helper methods. - * - * @author Guillaume Nodet - */ -public class Curses { - - private static Object[] sv = new Object[26]; - private static Object[] dv = new Object[26]; - - private static final int IFTE_NONE = 0; - private static final int IFTE_IF = 1; - private static final int IFTE_THEN = 2; - private static final int IFTE_ELSE = 3; - - /** - * Print the given terminal capabilities - * - * @param out the output stream - * @param str the capability to output - * @param params optional parameters - * @throws IOException if an error occurs - */ - public static void tputs(Writer out, String str, Object... params) throws IOException { - int index = 0; - int length = str.length(); - int ifte = IFTE_NONE; - boolean exec = true; - Stack stack = new Stack(); - while (index < length) { - char ch = str.charAt(index++); - switch (ch) { - case '\\': - ch = str.charAt(index++); - if (ch >= '0' && ch <= '9') { - throw new UnsupportedOperationException(); // todo - } else { - switch (ch) { - case 'e': - case 'E': - if (exec) { - out.write(27); // escape - } - break; - case 'n': - out.write('\n'); - break; -// case 'l': -// rawPrint('\l'); -// break; - case 'r': - if (exec) { - out.write('\r'); - } - break; - case 't': - if (exec) { - out.write('\t'); - } - break; - case 'b': - if (exec) { - out.write('\b'); - } - break; - case 'f': - if (exec) { - out.write('\f'); - } - break; - case 's': - if (exec) { - out.write(' '); - } - break; - case ':': - case '^': - case '\\': - if (exec) { - out.write(ch); - } - break; - default: - throw new IllegalArgumentException(); - } - } - break; - case '^': - ch = str.charAt(index++); - if (exec) { - out.write(ch - '@'); - } - break; - case '%': - ch = str.charAt(index++); - switch (ch) { - case '%': - if (exec) { - out.write('%'); - } - break; - case 'p': - ch = str.charAt(index++); - if (exec) { - stack.push(params[ch - '1']); - } - break; - case 'P': - ch = str.charAt(index++); - if (ch >= 'a' && ch <= 'z') { - if (exec) { - dv[ch - 'a'] = stack.pop(); - } - } else if (ch >= 'A' && ch <= 'Z') { - if (exec) { - sv[ch - 'A'] = stack.pop(); - } - } else { - throw new IllegalArgumentException(); - } - break; - case 'g': - ch = str.charAt(index++); - if (ch >= 'a' && ch <= 'z') { - if (exec) { - stack.push(dv[ch - 'a']); - } - } else if (ch >= 'A' && ch <= 'Z') { - if (exec) { - stack.push(sv[ch - 'A']); - } - } else { - throw new IllegalArgumentException(); - } - break; - case '\'': - ch = str.charAt(index++); - if (exec) { - stack.push((int) ch); - } - ch = str.charAt(index++); - if (ch != '\'') { - throw new IllegalArgumentException(); - } - break; - case '{': - int start = index; - while (str.charAt(index++) != '}') ; - if (exec) { - int v = Integer.valueOf(str.substring(start, index - 1)); - stack.push(v); - } - break; - case 'l': - if (exec) { - stack.push(stack.pop().toString().length()); - } - break; - case '+': - if (exec) { - int v2 = toInteger(stack.pop()); - int v1 = toInteger(stack.pop()); - stack.push(v1 + v2); - } - break; - case '-': - if (exec) { - int v2 = toInteger(stack.pop()); - int v1 = toInteger(stack.pop()); - stack.push(v1 - v2); - } - break; - case '*': - if (exec) { - int v2 = toInteger(stack.pop()); - int v1 = toInteger(stack.pop()); - stack.push(v1 * v2); - } - break; - case '/': - if (exec) { - int v2 = toInteger(stack.pop()); - int v1 = toInteger(stack.pop()); - stack.push(v1 / v2); - } - break; - case 'm': - if (exec) { - int v2 = toInteger(stack.pop()); - int v1 = toInteger(stack.pop()); - stack.push(v1 % v2); - } - break; - case '&': - if (exec) { - int v2 = toInteger(stack.pop()); - int v1 = toInteger(stack.pop()); - stack.push(v1 & v2); - } - break; - case '|': - if (exec) { - int v2 = toInteger(stack.pop()); - int v1 = toInteger(stack.pop()); - stack.push(v1 | v2); - } - break; - case '^': - if (exec) { - int v2 = toInteger(stack.pop()); - int v1 = toInteger(stack.pop()); - stack.push(v1 ^ v2); - } - break; - case '=': - if (exec) { - int v2 = toInteger(stack.pop()); - int v1 = toInteger(stack.pop()); - stack.push(v1 == v2); - } - break; - case '>': - if (exec) { - int v2 = toInteger(stack.pop()); - int v1 = toInteger(stack.pop()); - stack.push(v1 > v2); - } - break; - case '<': - if (exec) { - int v2 = toInteger(stack.pop()); - int v1 = toInteger(stack.pop()); - stack.push(v1 < v2); - } - break; - case 'A': - if (exec) { - int v2 = toInteger(stack.pop()); - int v1 = toInteger(stack.pop()); - stack.push(v1 != 0 && v2 != 0); - } - break; - case '!': - if (exec) { - int v1 = toInteger(stack.pop()); - stack.push(v1 == 0); - } - break; - case '~': - if (exec) { - int v1 = toInteger(stack.pop()); - stack.push(~v1); - } - break; - case 'O': - if (exec) { - int v2 = toInteger(stack.pop()); - int v1 = toInteger(stack.pop()); - stack.push(v1 != 0 || v2 != 0); - } - break; - case '?': - if (ifte != IFTE_NONE) { - throw new IllegalArgumentException(); - } else { - ifte = IFTE_IF; - } - break; - case 't': - if (ifte != IFTE_IF && ifte != IFTE_ELSE) { - throw new IllegalArgumentException(); - } else { - ifte = IFTE_THEN; - } - exec = toInteger(stack.pop()) != 0; - break; - case 'e': - if (ifte != IFTE_THEN) { - throw new IllegalArgumentException(); - } else { - ifte = IFTE_ELSE; - } - exec = !exec; - break; - case ';': - if (ifte == IFTE_NONE || ifte == IFTE_IF) { - throw new IllegalArgumentException(); - } else { - ifte = IFTE_NONE; - } - exec = true; - break; - case 'i': - if (params.length >= 1) { - params[0] = toInteger(params[0]) + 1; - } - if (params.length >= 2) { - params[1] = toInteger(params[1]) + 1; - } - break; - case 'd': - out.write(Integer.toString(toInteger(stack.pop()))); - break; - default: - throw new UnsupportedOperationException(); - } - break; - default: - if (exec) { - out.write(ch); - } - break; - } - } - } - - private static int toInteger(Object pop) { - if (pop instanceof Number) { - return ((Number) pop).intValue(); - } else if (pop instanceof Boolean) { - return (Boolean) pop ? 1 : 0; - } else { - return Integer.valueOf(pop.toString()); - } - } - -} diff --git a/kshell-console-jline2/src/main/java/lib/jline/internal/InfoCmp.java b/kshell-console-jline2/src/main/java/lib/jline/internal/InfoCmp.java deleted file mode 100644 index 62531a6..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/internal/InfoCmp.java +++ /dev/null @@ -1,586 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline.internal; - -import java.io.IOException; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Infocmp helper methods. - * - * @author Guillaume Nodet - */ -public class InfoCmp { - - private static final Map CAPS = new HashMap(); - - public static String getInfoCmp( - String terminal - ) throws IOException, InterruptedException { - String caps = CAPS.get(terminal); - if (caps == null) { - Process p = new ProcessBuilder("infocmp", terminal).start(); - caps = TerminalLineSettings.waitAndCapture(p); - CAPS.put(terminal, caps); - } - return caps; - } - - public static String getAnsiCaps() { - return ANSI_CAPS; - } - - public static void parseInfoCmp( - String capabilities, - Set bools, - Map ints, - Map strings - ) { - String[] lines = capabilities.split("\n"); - for (int i = 2; i < lines.length; i++) { - Matcher m = Pattern.compile("\\s*(([^,]|\\\\,)+)\\s*[,$]").matcher(lines[i]); - while (m.find()) { - String cap = m.group(1); - if (cap.contains("#")) { - int index = cap.indexOf('#'); - String key = cap.substring(0, index); - String val = cap.substring(index + 1); - int iVal = Integer.valueOf(val); - for (String name : getNames(key)) { - ints.put(name, iVal); - } - } else if (cap.contains("=")) { - int index = cap.indexOf('='); - String key = cap.substring(0, index); - String val = cap.substring(index + 1); - for (String name : getNames(key)) { - strings.put(name, val); - } - } else { - for (String name : getNames(cap)) { - bools.add(name); - } - } - } - } - } - - public static String[] getNames(String name) { - String[] names = NAMES.get(name); - return names != null ? names : new String[] { name }; - } - - private static final Map NAMES; - static { - String[][] list = { - { "auto_left_margin", "bw", "bw" }, - { "auto_right_margin", "am", "am" }, - { "back_color_erase", "bce", "ut" }, - { "can_change", "ccc", "cc" }, - { "ceol_standout_glitch", "xhp", "xs" }, - { "col_addr_glitch", "xhpa", "YA" }, - { "cpi_changes_res", "cpix", "YF" }, - { "cr_cancels_micro_mode", "crxm", "YB" }, - { "dest_tabs_magic_smso", "xt", "xt" }, - { "eat_newline_glitch", "xenl", "xn" }, - { "erase_overstrike", "eo", "eo" }, - { "generic_type", "gn", "gn" }, - { "hard_copy", "hc", "hc" }, - { "hard_cursor", "chts", "HC" }, - { "has_meta_key", "km", "km" }, - { "has_print_wheel", "daisy", "YC" }, - { "has_status_line", "hs", "hs" }, - { "hue_lightness_saturation", "hls", "hl" }, - { "insert_null_glitch", "in", "in" }, - { "lpi_changes_res", "lpix", "YG" }, - { "memory_above", "da", "da" }, - { "memory_below", "db", "db" }, - { "move_insert_mode", "mir", "mi" }, - { "move_standout_mode", "msgr", "ms" }, - { "needs_xon_xoff", "nxon", "nx" }, - { "no_esc_ctlc", "xsb", "xb" }, - { "no_pad_char", "npc", "NP" }, - { "non_dest_scroll_region", "ndscr", "ND" }, - { "non_rev_rmcup", "nrrmc", "NR" }, - { "over_strike", "os", "os" }, - { "prtr_silent", "mc5i", "5i" }, - { "row_addr_glitch", "xvpa", "YD" }, - { "semi_auto_right_margin", "sam", "YE" }, - { "status_line_esc_ok", "eslok", "es" }, - { "tilde_glitch", "hz", "hz" }, - { "transparent_underline", "ul", "ul" }, - { "xon_xoff", "xon", "xo" }, - { "columns", "cols", "co" }, - { "init_tabs", "it", "it" }, - { "label_height", "lh", "lh" }, - { "label_width", "lw", "lw" }, - { "lines", "lines", "li" }, - { "lines_of_memory", "lm", "lm" }, - { "magic_cookie_glitch", "xmc", "sg" }, - { "max_attributes", "ma", "ma" }, - { "max_colors", "colors", "Co" }, - { "max_pairs", "pairs", "pa" }, - { "maximum_windows", "wnum", "MW" }, - { "no_color_video", "ncv", "NC" }, - { "num_labels", "nlab", "Nl" }, - { "padding_baud_rate", "pb", "pb" }, - { "virtual_terminal", "vt", "vt" }, - { "width_status_line", "wsl", "ws" }, - { "bit_image_entwining", "bitwin", "Yo" }, - { "bit_image_type", "bitype", "Yp" }, - { "buffer_capacity", "bufsz", "Ya" }, - { "buttons", "btns", "BT" }, - { "dot_horz_spacing", "spinh", "Yc" }, - { "dot_vert_spacing", "spinv", "Yb" }, - { "max_micro_address", "maddr", "Yd" }, - { "max_micro_jump", "mjump", "Ye" }, - { "micro_col_size", "mcs", "Yf" }, - { "micro_line_size", "mls", "Yg" }, - { "number_of_pins", "npins", "Yh" }, - { "output_res_char", "orc", "Yi" }, - { "output_res_horz_inch", "orhi", "Yk" }, - { "output_res_line", "orl", "Yj" }, - { "output_res_vert_inch", "orvi", "Yl" }, - { "print_rate", "cps", "Ym" }, - { "wide_char_size", "widcs", "Yn" }, - { "acs_chars", "acsc", "ac" }, - { "back_tab", "cbt", "bt" }, - { "bell", "bel", "bl" }, - { "carriage_return", "cr", "cr" }, - { "change_char_pitch", "cpi", "ZA" }, - { "change_line_pitch", "lpi", "ZB" }, - { "change_res_horz", "chr", "ZC" }, - { "change_res_vert", "cvr", "ZD" }, - { "change_scroll_region", "csr", "cs" }, - { "char_padding", "rmp", "rP" }, - { "clear_all_tabs", "tbc", "ct" }, - { "clear_margins", "mgc", "MC" }, - { "clear_screen", "cleanUp", "cl" }, - { "clr_bol", "el1", "cb" }, - { "clr_eol", "el", "ce" }, - { "clr_eos", "ed", "cd" }, - { "column_address", "hpa", "ch" }, - { "command_character", "cmdch", "CC" }, - { "create_window", "cwin", "CW" }, - { "cursor_address", "cup", "cm" }, - { "cursor_down", "cud1", "do" }, - { "cursor_home", "home", "ho" }, - { "cursor_invisible", "civis", "vi" }, - { "cursor_left", "cub1", "le" }, - { "cursor_mem_address", "mrcup", "CM" }, - { "cursor_normal", "cnorm", "ve" }, - { "cursor_right", "cuf1", "nd" }, - { "cursor_to_ll", "ll", "ll" }, - { "cursor_up", "cuu1", "up" }, - { "cursor_visible", "cvvis", "vs" }, - { "define_char", "defc", "ZE" }, - { "delete_character", "dch1", "dc" }, - { "delete_line", "dl1", "dl" }, - { "dial_phone", "dial", "DI" }, - { "dis_status_line", "dsl", "ds" }, - { "display_clock", "dclk", "DK" }, - { "down_half_line", "hd", "hd" }, - { "ena_acs", "enacs", "eA" }, - { "enter_alt_charset_mode", "smacs", "as" }, - { "enter_am_mode", "smam", "SA" }, - { "enter_blink_mode", "blink", "mb" }, - { "enter_bold_mode", "bold", "md" }, - { "enter_ca_mode", "smcup", "ti" }, - { "enter_delete_mode", "smdc", "dm" }, - { "enter_dim_mode", "dim", "mh" }, - { "enter_doublewide_mode", "swidm", "ZF" }, - { "enter_draft_quality", "sdrfq", "ZG" }, - { "enter_insert_mode", "smir", "im" }, - { "enter_italics_mode", "sitm", "ZH" }, - { "enter_leftward_mode", "slm", "ZI" }, - { "enter_micro_mode", "smicm", "ZJ" }, - { "enter_near_letter_quality", "snlq", "ZK" }, - { "enter_normal_quality", "snrmq", "ZL" }, - { "enter_protected_mode", "prot", "mp" }, - { "enter_reverse_mode", "rev", "mr" }, - { "enter_secure_mode", "invis", "mk" }, - { "enter_shadow_mode", "sshm", "ZM" }, - { "enter_standout_mode", "smso", "so" }, - { "enter_subscript_mode", "ssubm", "ZN" }, - { "enter_superscript_mode", "ssupm", "ZO" }, - { "enter_underline_mode", "smul", "us" }, - { "enter_upward_mode", "sum", "ZP" }, - { "enter_xon_mode", "smxon", "SX" }, - { "erase_chars", "ech", "ec" }, - { "exit_alt_charset_mode", "rmacs", "ae" }, - { "exit_am_mode", "rmam", "RA" }, - { "exit_attribute_mode", "sgr0", "me" }, - { "exit_ca_mode", "rmcup", "te" }, - { "exit_delete_mode", "rmdc", "ed" }, - { "exit_doublewide_mode", "rwidm", "ZQ" }, - { "exit_insert_mode", "rmir", "ei" }, - { "exit_italics_mode", "ritm", "ZR" }, - { "exit_leftward_mode", "rlm", "ZS" }, - { "exit_micro_mode", "rmicm", "ZT" }, - { "exit_shadow_mode", "rshm", "ZU" }, - { "exit_standout_mode", "rmso", "se" }, - { "exit_subscript_mode", "rsubm", "ZV" }, - { "exit_superscript_mode", "rsupm", "ZW" }, - { "exit_underline_mode", "rmul", "ue" }, - { "exit_upward_mode", "rum", "ZX" }, - { "exit_xon_mode", "rmxon", "RX" }, - { "fixed_pause", "pause", "PA" }, - { "flash_hook", "hook", "fh" }, - { "flash_screen", "flash", "vb" }, - { "form_feed", "ff", "ff" }, - { "from_status_line", "fsl", "fs" }, - { "goto_window", "wingo", "WG" }, - { "hangup", "hup", "HU" }, - { "init_1string", "is1", "i1" }, - { "init_2string", "is2", "is" }, - { "init_3string", "is3", "i3" }, - { "init_file", "if", "if" }, - { "init_prog", "iprog", "iP" }, - { "initialize_color", "initc", "Ic" }, - { "initialize_pair", "initp", "Ip" }, - { "insert_character", "ich1", "ic" }, - { "insert_line", "il1", "al" }, - { "insert_padding", "ip", "ip" }, - { "key_a1", "ka1", "K1" }, - { "key_a3", "ka3", "K3" }, - { "key_b2", "kb2", "K2" }, - { "key_backspace", "kbs", "kb" }, - { "key_beg", "kbeg", "@1" }, - { "key_btab", "kcbt", "kB" }, - { "key_c1", "kc1", "K4" }, - { "key_c3", "kc3", "K5" }, - { "key_cancel", "kcan", "@2" }, - { "key_catab", "ktbc", "ka" }, - { "key_clear", "kclr", "kC" }, - { "key_close", "kclo", "@3" }, - { "key_command", "kcmd", "@4" }, - { "key_copy", "kcpy", "@5" }, - { "key_create", "kcrt", "@6" }, - { "key_ctab", "kctab", "kt" }, - { "key_dc", "kdch1", "kD" }, - { "key_dl", "kdl1", "kL" }, - { "key_down", "kcud1", "kd" }, - { "key_eic", "krmir", "kM" }, - { "key_end", "kend", "@7" }, - { "key_enter", "kent", "@8" }, - { "key_eol", "kel", "kE" }, - { "key_eos", "ked", "kS" }, - { "key_exit", "kext", "@9" }, - { "key_f0", "kf0", "k0" }, - { "key_f1", "kf1", "k1" }, - { "key_f10", "kf10", "k;" }, - { "key_f11", "kf11", "F1" }, - { "key_f12", "kf12", "F2" }, - { "key_f13", "kf13", "F3" }, - { "key_f14", "kf14", "F4" }, - { "key_f15", "kf15", "F5" }, - { "key_f16", "kf16", "F6" }, - { "key_f17", "kf17", "F7" }, - { "key_f18", "kf18", "F8" }, - { "key_f19", "kf19", "F9" }, - { "key_f2", "kf2", "k2" }, - { "key_f20", "kf20", "FA" }, - { "key_f21", "kf21", "FB" }, - { "key_f22", "kf22", "FC" }, - { "key_f23", "kf23", "FD" }, - { "key_f24", "kf24", "FE" }, - { "key_f25", "kf25", "FF" }, - { "key_f26", "kf26", "FG" }, - { "key_f27", "kf27", "FH" }, - { "key_f28", "kf28", "FI" }, - { "key_f29", "kf29", "FJ" }, - { "key_f3", "kf3", "k3" }, - { "key_f30", "kf30", "FK" }, - { "key_f31", "kf31", "FL" }, - { "key_f32", "kf32", "FM" }, - { "key_f33", "kf33", "FN" }, - { "key_f34", "kf34", "FO" }, - { "key_f35", "kf35", "FP" }, - { "key_f36", "kf36", "FQ" }, - { "key_f37", "kf37", "FR" }, - { "key_f38", "kf38", "FS" }, - { "key_f39", "kf39", "FT" }, - { "key_f4", "kf4", "k4" }, - { "key_f40", "kf40", "FU" }, - { "key_f41", "kf41", "FV" }, - { "key_f42", "kf42", "FW" }, - { "key_f43", "kf43", "FX" }, - { "key_f44", "kf44", "FY" }, - { "key_f45", "kf45", "FZ" }, - { "key_f46", "kf46", "Fa" }, - { "key_f47", "kf47", "Fb" }, - { "key_f48", "kf48", "Fc" }, - { "key_f49", "kf49", "Fd" }, - { "key_f5", "kf5", "k5" }, - { "key_f50", "kf50", "Fe" }, - { "key_f51", "kf51", "Ff" }, - { "key_f52", "kf52", "Fg" }, - { "key_f53", "kf53", "Fh" }, - { "key_f54", "kf54", "Fi" }, - { "key_f55", "kf55", "Fj" }, - { "key_f56", "kf56", "Fk" }, - { "key_f57", "kf57", "Fl" }, - { "key_f58", "kf58", "Fm" }, - { "key_f59", "kf59", "Fn" }, - { "key_f6", "kf6", "k6" }, - { "key_f60", "kf60", "Fo" }, - { "key_f61", "kf61", "Fp" }, - { "key_f62", "kf62", "Fq" }, - { "key_f63", "kf63", "Fr" }, - { "key_f7", "kf7", "k7" }, - { "key_f8", "kf8", "k8" }, - { "key_f9", "kf9", "k9" }, - { "key_find", "kfnd", "@0" }, - { "key_help", "khlp", "%1" }, - { "key_home", "khome", "kh" }, - { "key_ic", "kich1", "kI" }, - { "key_il", "kil1", "kA" }, - { "key_left", "kcub1", "kl" }, - { "key_ll", "kll", "kH" }, - { "key_mark", "kmrk", "%2" }, - { "key_message", "kmsg", "%3" }, - { "key_move", "kmov", "%4" }, - { "key_next", "knxt", "%5" }, - { "key_npage", "knp", "kN" }, - { "key_open", "kopn", "%6" }, - { "key_options", "kopt", "%7" }, - { "key_ppage", "kpp", "kP" }, - { "key_previous", "kprv", "%8" }, - { "key_print", "kprt", "%9" }, - { "key_redo", "krdo", "%0" }, - { "key_reference", "kref", "&1" }, - { "key_refresh", "krfr", "&2" }, - { "key_replace", "krpl", "&3" }, - { "key_restart", "krst", "&4" }, - { "key_resume", "kres", "&5" }, - { "key_right", "kcuf1", "kr" }, - { "key_save", "ksav", "&6" }, - { "key_sbeg", "kBEG", "&9" }, - { "key_scancel", "kCAN", "&0" }, - { "key_scommand", "kCMD", "*1" }, - { "key_scopy", "kCPY", "*2" }, - { "key_screate", "kCRT", "*3" }, - { "key_sdc", "kDC", "*4" }, - { "key_sdl", "kDL", "*5" }, - { "key_select", "kslt", "*6" }, - { "key_send", "kEND", "*7" }, - { "key_seol", "kEOL", "*8" }, - { "key_sexit", "kEXT", "*9" }, - { "key_sf", "kind", "kF" }, - { "key_sfind", "kFND", "*0" }, - { "key_shelp", "kHLP", "#1" }, - { "key_shome", "kHOM", "#2" }, - { "key_sic", "kIC", "#3" }, - { "key_sleft", "kLFT", "#4" }, - { "key_smessage", "kMSG", "%a" }, - { "key_smove", "kMOV", "%b" }, - { "key_snext", "kNXT", "%c" }, - { "key_soptions", "kOPT", "%d" }, - { "key_sprevious", "kPRV", "%e" }, - { "key_sprint", "kPRT", "%f" }, - { "key_sr", "kri", "kR" }, - { "key_sredo", "kRDO", "%g" }, - { "key_sreplace", "kRPL", "%h" }, - { "key_sright", "kRIT", "%i" }, - { "key_srsume", "kRES", "%j" }, - { "key_ssave", "kSAV", "!1" }, - { "key_ssuspend", "kSPD", "!2" }, - { "key_stab", "khts", "kT" }, - { "key_sundo", "kUND", "!3" }, - { "key_suspend", "kspd", "&7" }, - { "key_undo", "kund", "&8" }, - { "key_up", "kcuu1", "ku" }, - { "keypad_local", "rmkx", "ke" }, - { "keypad_xmit", "smkx", "ks" }, - { "lab_f0", "lf0", "l0" }, - { "lab_f1", "lf1", "l1" }, - { "lab_f10", "lf10", "la" }, - { "lab_f2", "lf2", "l2" }, - { "lab_f3", "lf3", "l3" }, - { "lab_f4", "lf4", "l4" }, - { "lab_f5", "lf5", "l5" }, - { "lab_f6", "lf6", "l6" }, - { "lab_f7", "lf7", "l7" }, - { "lab_f8", "lf8", "l8" }, - { "lab_f9", "lf9", "l9" }, - { "label_format", "fln", "Lf" }, - { "label_off", "rmln", "LF" }, - { "label_on", "smln", "LO" }, - { "meta_off", "rmm", "mo" }, - { "meta_on", "smm", "mm" }, - { "micro_column_address", "mhpa", "ZY" }, - { "micro_down", "mcud1", "ZZ" }, - { "micro_left", "mcub1", "Za" }, - { "micro_right", "mcuf1", "Zb" }, - { "micro_row_address", "mvpa", "Zc" }, - { "micro_up", "mcuu1", "Zd" }, - { "newline", "nel", "nw" }, - { "order_of_pins", "porder", "Ze" }, - { "orig_colors", "oc", "oc" }, - { "orig_pair", "op", "op" }, - { "pad_char", "pad", "pc" }, - { "parm_dch", "dch", "DC" }, - { "parm_delete_line", "dl", "DL" }, - { "parm_down_cursor", "cud", "DO" }, - { "parm_down_micro", "mcud", "Zf" }, - { "parm_ich", "ich", "IC" }, - { "parm_index", "indn", "SF" }, - { "parm_insert_line", "il", "AL" }, - { "parm_left_cursor", "cub", "LE" }, - { "parm_left_micro", "mcub", "Zg" }, - { "parm_right_cursor", "cuf", "RI" }, - { "parm_right_micro", "mcuf", "Zh" }, - { "parm_rindex", "rin", "SR" }, - { "parm_up_cursor", "cuu", "UP" }, - { "parm_up_micro", "mcuu", "Zi" }, - { "pkey_key", "pfkey", "pk" }, - { "pkey_local", "pfloc", "pl" }, - { "pkey_xmit", "pfx", "px" }, - { "plab_norm", "pln", "pn" }, - { "print_screen", "mc0", "ps" }, - { "prtr_non", "mc5p", "pO" }, - { "prtr_off", "mc4", "pf" }, - { "prtr_on", "mc5", "po" }, - { "pulse", "pulse", "PU" }, - { "quick_dial", "qdial", "QD" }, - { "remove_clock", "rmclk", "RC" }, - { "repeat_char", "rep", "rp" }, - { "req_for_input", "rfi", "RF" }, - { "reset_1string", "rs1", "r1" }, - { "reset_2string", "rs2", "r2" }, - { "reset_3string", "rs3", "r3" }, - { "reset_file", "rf", "rf" }, - { "restore_cursor", "rc", "rc" }, - { "row_address", "vpa", "cv" }, - { "save_cursor", "sc", "sc" }, - { "scroll_forward", "ind", "sf" }, - { "scroll_reverse", "ri", "sr" }, - { "select_char_set", "scs", "Zj" }, - { "set_attributes", "sgr", "sa" }, - { "set_background", "setb", "Sb" }, - { "set_bottom_margin", "smgb", "Zk" }, - { "set_bottom_margin_parm", "smgbp", "Zl" }, - { "set_clock", "sclk", "SC" }, - { "set_color_pair", "scp", "sp" }, - { "set_foreground", "setf", "Sf" }, - { "set_left_margin", "smgl", "ML" }, - { "set_left_margin_parm", "smglp", "Zm" }, - { "set_right_margin", "smgr", "MR" }, - { "set_right_margin_parm", "smgrp", "Zn" }, - { "set_tab", "hts", "st" }, - { "set_top_margin", "smgt", "Zo" }, - { "set_top_margin_parm", "smgtp", "Zp" }, - { "set_window", "wind", "wi" }, - { "start_bit_image", "sbim", "Zq" }, - { "start_char_set_def", "scsd", "Zr" }, - { "stop_bit_image", "rbim", "Zs" }, - { "stop_char_set_def", "rcsd", "Zt" }, - { "subscript_characters", "subcs", "Zu" }, - { "superscript_characters", "supcs", "Zv" }, - { "tab", "ht", "ta" }, - { "these_cause_cr", "docr", "Zw" }, - { "to_status_line", "tsl", "ts" }, - { "tone", "tone", "TO" }, - { "underline_char", "uc", "uc" }, - { "up_half_line", "hu", "hu" }, - { "user0", "u0", "u0" }, - { "user1", "u1", "u1" }, - { "user2", "u2", "u2" }, - { "user3", "u3", "u3" }, - { "user4", "u4", "u4" }, - { "user5", "u5", "u5" }, - { "user6", "u6", "u6" }, - { "user7", "u7", "u7" }, - { "user8", "u8", "u8" }, - { "user9", "u9", "u9" }, - { "wait_tone", "wait", "WA" }, - { "xoff_character", "xoffc", "XF" }, - { "xon_character", "xonc", "XN" }, - { "zero_motion", "zerom", "Zx" }, - { "alt_scancode_esc", "scesa", "S8" }, - { "bit_image_carriage_return", "bicr", "Yv" }, - { "bit_image_newline", "binel", "Zz" }, - { "bit_image_repeat", "birep", "Xy" }, - { "char_set_names", "csnm", "Zy" }, - { "code_set_init", "csin", "ci" }, - { "color_names", "colornm", "Yw" }, - { "define_bit_image_region", "defbi", "Yx" }, - { "device_type", "devt", "dv" }, - { "display_pc_char", "dispc", "S1" }, - { "end_bit_image_region", "endbi", "Yy" }, - { "enter_pc_charset_mode", "smpch", "S2" }, - { "enter_scancode_mode", "smsc", "S4" }, - { "exit_pc_charset_mode", "rmpch", "S3" }, - { "exit_scancode_mode", "rmsc", "S5" }, - { "get_mouse", "getm", "Gm" }, - { "key_mouse", "kmous", "Km" }, - { "mouse_info", "minfo", "Mi" }, - { "pc_term_options", "pctrm", "S6" }, - { "pkey_plab", "pfxl", "xl" }, - { "req_mouse_pos", "reqmp", "RQ" }, - { "scancode_escape", "scesc", "S7" }, - { "set0_des_seq", "s0ds", "s0" }, - { "set1_des_seq", "s1ds", "s1" }, - { "set2_des_seq", "s2ds", "s2" }, - { "set3_des_seq", "s3ds", "s3" }, - { "set_a_background", "setab", "AB" }, - { "set_a_foreground", "setaf", "AF" }, - { "set_color_band", "setcolor", "Yz" }, - { "set_lr_margin", "smglr", "ML" }, - { "set_page_length", "slines", "YZ" }, - { "set_tb_margin", "smgtb", "MT" }, - { "enter_horizontal_hl_mode", "ehhlm", "Xh" }, - { "enter_left_hl_mode", "elhlm", "Xl" }, - { "enter_low_hl_mode", "elohlm", "Xo" }, - { "enter_right_hl_mode", "erhlm", "Xr" }, - { "enter_top_hl_mode", "ethlm", "Xt" }, - { "enter_vertical_hl_mode", "evhlm", "Xv" }, - { "set_a_attributes", "sgr1", "sA" }, - { "set_pglen_inch", "slength", "sL" } - }; - - Map map = new HashMap(); - for (String[] names : list) { - for (String name : names) { - map.put(name, names); - } - } - NAMES = Collections.unmodifiableMap(map); - } - - private static String ANSI_CAPS = - "#\tReconstructed via infocmp from file: /usr/share/terminfo/61/ansi\n" + - "ansi|ansi/pc-term compatible with color,\n" + - "\tam, mc5i, mir, msgr,\n" + - "\tcolors#8, cols#80, it#8, lines#24, ncv#3, pairs#64,\n" + - "\tacsc=+\\020\\,\\021-\\030.^Y0\\333`\\004a\\261f\\370g\\361h\\260j\\331k\\277l\\332m\\300n\\305o~p\\304q\\304r\\304s_t\\303u\\264v\\301w\\302x\\263y\\363z\\362{\\343|\\330}\\234~\\376,\n" + - "\tbel=^G, blink=\\E[5m, bold=\\E[1m, cbt=\\E[Z, cleanUp=\\E[H\\E[J,\n" + - "\tcr=^M, cub=\\E[%p1%dD, cub1=\\E[D, cud=\\E[%p1%dB, cud1=\\E[B,\n" + - "\tcuf=\\E[%p1%dC, cuf1=\\E[C, cup=\\E[%i%p1%d;%p2%dH,\n" + - "\tcuu=\\E[%p1%dA, cuu1=\\E[A, dch=\\E[%p1%dP, dch1=\\E[P,\n" + - "\tdl=\\E[%p1%dM, dl1=\\E[M, ech=\\E[%p1%dX, ed=\\E[J, el=\\E[K,\n" + - "\tel1=\\E[1K, home=\\E[H, hpa=\\E[%i%p1%dG, ht=\\E[I, hts=\\EH,\n" + - "\tich=\\E[%p1%d@, il=\\E[%p1%dL, il1=\\E[L, ind=^J,\n" + - "\tindn=\\E[%p1%dS, invis=\\E[8m, kbs=^H, kcbt=\\E[Z, kcub1=\\E[D,\n" + - "\tkcud1=\\E[B, kcuf1=\\E[C, kcuu1=\\E[A, khome=\\E[H, kich1=\\E[L,\n" + - "\tmc4=\\E[4i, mc5=\\E[5i, nel=\\r\\E[S, op=\\E[39;49m,\n" + - "\trep=%p1%c\\E[%p2%{1}%-%db, rev=\\E[7m, rin=\\E[%p1%dT,\n" + - "\trmacs=\\E[10m, rmpch=\\E[10m, rmso=\\E[m, rmul=\\E[m,\n" + - "\ts0ds=\\E(B, s1ds=\\E)B, s2ds=\\E*B, s3ds=\\E+B,\n" + - "\tsetab=\\E[4%p1%dm, setaf=\\E[3%p1%dm,\n" + - "\tsgr=\\E[0;10%?%p1%t;7%;%?%p2%t;4%;%?%p3%t;7%;%?%p4%t;5%;%?%p6%t;1%;%?%p7%t;8%;%?%p9%t;11%;m,\n" + - "\tsgr0=\\E[0;10m, smacs=\\E[11m, smpch=\\E[11m, smso=\\E[7m,\n" + - "\tsmul=\\E[4m, tbc=\\E[2g, u6=\\E[%i%d;%dR, u7=\\E[6n,\n" + - "\tu8=\\E[?%[;0123456789]c, u9=\\E[c, vpa=\\E[%i%p1%dd,"; -} diff --git a/kshell-console-jline2/src/main/java/lib/jline/internal/InputStreamReader.java b/kshell-console-jline2/src/main/java/lib/jline/internal/InputStreamReader.java deleted file mode 100644 index 25daf3c..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/internal/InputStreamReader.java +++ /dev/null @@ -1,334 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline.internal; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStreamWriter; -import java.io.Reader; -import java.io.UnsupportedEncodingException; -import java.nio.ByteBuffer; -import java.nio.CharBuffer; -import java.nio.charset.Charset; -import java.nio.charset.CharsetDecoder; -import java.nio.charset.CoderResult; -import java.nio.charset.CodingErrorAction; -import java.nio.charset.MalformedInputException; -import java.nio.charset.UnmappableCharacterException; - - -/** - * - * NOTE for JLine: the default InputStreamReader that comes from the JRE - * usually read more bytes than needed from the input stream, which - * is not usable in a character per character model used in the console. - * We thus use the harmony code which only reads the minimal number of bytes, - * with a modification to ensure we can read larger characters (UTF-16 has - * up to 4 bytes, and UTF-32, rare as it is, may have up to 8). - */ -/** - * A class for turning a byte stream into a character stream. Data read from the - * source input stream is converted into characters by either a default or a - * provided character converter. The default encoding is taken from the - * "file.encoding" system property. {@code InputStreamReader} contains a buffer - * of bytes read from the source stream and converts these into characters as - * needed. The buffer size is 8K. - * - * @see OutputStreamWriter - */ -public class InputStreamReader extends Reader { - private InputStream in; - - private static final int BUFFER_SIZE = 8192; - - private boolean endOfInput = false; - - CharsetDecoder decoder; - - ByteBuffer bytes = ByteBuffer.allocate(BUFFER_SIZE); - - /** - * Constructs a new {@code InputStreamReader} on the {@link InputStream} - * {@code in}. This constructor sets the character converter to the encoding - * specified in the "file.encoding" property and falls back to ISO 8859_1 - * (ISO-Latin-1) if the property doesn't exist. - * - * @param in - * the input stream from which to read characters. - */ - public InputStreamReader(InputStream in) { - super(in); - this.in = in; - decoder = Charset.defaultCharset().newDecoder().onMalformedInput( - CodingErrorAction.REPLACE).onUnmappableCharacter( - CodingErrorAction.REPLACE); - bytes.limit(0); - } - - /** - * Constructs a new InputStreamReader on the InputStream {@code in}. The - * character converter that is used to decode bytes into characters is - * identified by name by {@code enc}. If the encoding cannot be found, an - * UnsupportedEncodingException error is thrown. - * - * @param in - * the InputStream from which to read characters. - * @param enc - * identifies the character converter to use. - * @throws NullPointerException - * if {@code enc} is {@code null}. - * @throws UnsupportedEncodingException - * if the encoding specified by {@code enc} cannot be found. - */ - public InputStreamReader(InputStream in, final String enc) - throws UnsupportedEncodingException { - super(in); - if (enc == null) { - throw new NullPointerException(); - } - this.in = in; - try { - decoder = Charset.forName(enc).newDecoder().onMalformedInput( - CodingErrorAction.REPLACE).onUnmappableCharacter( - CodingErrorAction.REPLACE); - } catch (IllegalArgumentException e) { - throw (UnsupportedEncodingException) - new UnsupportedEncodingException(enc).initCause(e); - } - bytes.limit(0); - } - - /** - * Constructs a new InputStreamReader on the InputStream {@code in} and - * CharsetDecoder {@code dec}. - * - * @param in - * the source InputStream from which to read characters. - * @param dec - * the CharsetDecoder used by the character conversion. - */ - public InputStreamReader(InputStream in, CharsetDecoder dec) { - super(in); - dec.averageCharsPerByte(); - this.in = in; - decoder = dec; - bytes.limit(0); - } - - /** - * Constructs a new InputStreamReader on the InputStream {@code in} and - * Charset {@code charset}. - * - * @param in - * the source InputStream from which to read characters. - * @param charset - * the Charset that defines the character converter - */ - public InputStreamReader(InputStream in, Charset charset) { - super(in); - this.in = in; - decoder = charset.newDecoder().onMalformedInput( - CodingErrorAction.REPLACE).onUnmappableCharacter( - CodingErrorAction.REPLACE); - bytes.limit(0); - } - - /** - * Closes this reader. This implementation closes the source InputStream and - * releases all local storage. - * - * @throws IOException - * if an error occurs attempting to close this reader. - */ - @Override - public void close() throws IOException { - synchronized (lock) { - decoder = null; - if (in != null) { - in.close(); - in = null; - } - } - } - - /** - * Returns the name of the encoding used to convert bytes into characters. - * The value {@code null} is returned if this reader has been closed. - * - * @return the name of the character converter or {@code null} if this - * reader is closed. - */ - public String getEncoding() { - if (!isOpen()) { - return null; - } - return decoder.charset().name(); - } - - /** - * Reads a single character from this reader and returns it as an integer - * with the two higher-order bytes set to 0. Returns -1 if the end of the - * reader has been reached. The byte value is either obtained from - * converting bytes in this reader's buffer or by first filling the buffer - * from the source InputStream and then reading from the buffer. - * - * @return the character read or -1 if the end of the reader has been - * reached. - * @throws IOException - * if this reader is closed or some other I/O error occurs. - */ - @Override - public int read() throws IOException { - synchronized (lock) { - if (!isOpen()) { - throw new IOException("InputStreamReader is closed."); - } - - char buf[] = new char[4]; - return read(buf, 0, 4) != -1 ? Character.codePointAt(buf, 0) : -1; - } - } - - /** - * Reads at most {@code length} characters from this reader and stores them - * at position {@code offset} in the character array {@code buf}. Returns - * the number of characters actually read or -1 if the end of the reader has - * been reached. The bytes are either obtained from converting bytes in this - * reader's buffer or by first filling the buffer from the source - * InputStream and then reading from the buffer. - * - * @param buf - * the array to store the characters read. - * @param offset - * the initial position in {@code buf} to store the characters - * read from this reader. - * @param length - * the maximum number of characters to read. - * @return the number of characters read or -1 if the end of the reader has - * been reached. - * @throws IndexOutOfBoundsException - * if {@code offset < 0} or {@code length < 0}, or if - * {@code offset + length} is greater than the length of - * {@code buf}. - * @throws IOException - * if this reader is closed or some other I/O error occurs. - */ - @Override - public int read(char[] buf, int offset, int length) throws IOException { - synchronized (lock) { - if (!isOpen()) { - throw new IOException("InputStreamReader is closed."); - } - if (offset < 0 || offset > buf.length - length || length < 0) { - throw new IndexOutOfBoundsException(); - } - if (length == 0) { - return 0; - } - - CharBuffer out = CharBuffer.wrap(buf, offset, length); - CoderResult result = CoderResult.UNDERFLOW; - - // bytes.remaining() indicates number of bytes in buffer - // when 1-st time entered, it'll be equal to zero - boolean needInput = !bytes.hasRemaining(); - - while (out.hasRemaining()) { - // fill the buffer if needed - if (needInput) { - try { - if ((in.available() == 0) - && (out.position() > offset)) { - // we could return the result without blocking read - break; - } - } catch (IOException e) { - // available didn't work so just try the read - } - - int to_read = bytes.capacity() - bytes.limit(); - int off = bytes.arrayOffset() + bytes.limit(); - int was_red = in.read(bytes.array(), off, to_read); - - if (was_red == -1) { - endOfInput = true; - break; - } else if (was_red == 0) { - break; - } - bytes.limit(bytes.limit() + was_red); - needInput = false; - } - - // decode bytes - result = decoder.decode(bytes, out, false); - - if (result.isUnderflow()) { - // compact the buffer if no space left - if (bytes.limit() == bytes.capacity()) { - bytes.compact(); - bytes.limit(bytes.position()); - bytes.position(0); - } - needInput = true; - } else { - break; - } - } - - if (result == CoderResult.UNDERFLOW && endOfInput) { - result = decoder.decode(bytes, out, true); - decoder.flush(out); - decoder.reset(); - } - if (result.isMalformed()) { - throw new MalformedInputException(result.length()); - } else if (result.isUnmappable()) { - throw new UnmappableCharacterException(result.length()); - } - - return out.position() - offset == 0 ? -1 : out.position() - offset; - } - } - - /* - * Answer a boolean indicating whether or not this InputStreamReader is - * open. - */ - private boolean isOpen() { - return in != null; - } - - /** - * Indicates whether this reader is ready to be read without blocking. If - * the result is {@code true}, the next {@code read()} will not block. If - * the result is {@code false} then this reader may or may not block when - * {@code read()} is called. This implementation returns {@code true} if - * there are bytes available in the buffer or the source stream has bytes - * available. - * - * @return {@code true} if the receiver will not block when {@code read()} - * is called, {@code false} if unknown or blocking will occur. - * @throws IOException - * if this reader is closed or some other I/O error occurs. - */ - @Override - public boolean ready() throws IOException { - synchronized (lock) { - if (in == null) { - throw new IOException("InputStreamReader is closed."); - } - try { - return bytes.hasRemaining() || in.available() > 0; - } catch (IOException e) { - return false; - } - } - } -} \ No newline at end of file diff --git a/kshell-console-jline2/src/main/java/lib/jline/internal/Log.java b/kshell-console-jline2/src/main/java/lib/jline/internal/Log.java deleted file mode 100644 index 2e4526f..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/internal/Log.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline.internal; - -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; -import java.util.logging.LogRecord; -import java.util.logging.Logger; - -/** - * Internal logger. - * - * @author Jason Dillon - * @author Guillaume Nodet - * @since 2.0 - */ -public final class Log -{ - ///CLOVER:OFF - - public static enum Level - { - TRACE, - DEBUG, - INFO, - WARN, - ERROR - } - - public static final boolean TRACE = Configuration.getBoolean(Log.class.getName() + ".trace"); - - public static final boolean DEBUG = TRACE || Configuration.getBoolean(Log.class.getName() + ".debug"); - - private static PrintStream output = System.err; - - private static boolean useJul = Configuration.getBoolean("jline.log.jul"); - - public static PrintStream getOutput() { - return output; - } - - public static void setOutput(final PrintStream out) { - output = Preconditions.checkNotNull(out); - } - - /** - * Helper to support rendering messages. - */ - @TestAccessible - static void render(final PrintStream out, final Object message) { - if (message.getClass().isArray()) { - Object[] array = (Object[]) message; - - out.print("["); - for (int i = 0; i < array.length; i++) { - out.print(array[i]); - if (i + 1 < array.length) { - out.print(","); - } - } - out.print("]"); - } - else { - out.print(message); - } - } - - @TestAccessible - static void log(final Level level, final Object... messages) { - if (useJul) { - logWithJul(level, messages); - return; - } - //noinspection SynchronizeOnNonFinalField - synchronized (output) { - output.format("[%s] ", level); - - for (int i=0; iVERY IMPORTANT NOTES - *
    - *
  • This class is not thread safe. It expects at most one reader. - *
  • The {@link #shutdown()} method must be called in order to shut down - * the thread that handles blocking I/O. - *
- * @since 2.7 - * @author Scott C. Gray - */ -public class NonBlockingInputStream - extends InputStream - implements Runnable -{ - private InputStream in; // The actual input stream - private int ch = -2; // Recently read character - - private boolean threadIsReading = false; - private boolean isShutdown = false; - private IOException exception = null; - private boolean nonBlockingEnabled; - - /** - * Creates a NonBlockingInputStream out of a normal blocking - * stream. Note that this call also spawn a separate thread to perform the - * blocking I/O on behalf of the thread that is using this class. The - * {@link #shutdown()} method must be called in order to shut this thread down. - * @param in The input stream to wrap - * @param isNonBlockingEnabled If true, then the non-blocking methods - * {@link #read(long)} and {@link #peek(long)} will be available and, - * more importantly, the thread will be started to provide support for the - * feature. If false, then this class acts as a clean-passthru for the - * underlying I/O stream and provides very little overhead. - */ - public NonBlockingInputStream (InputStream in, boolean isNonBlockingEnabled) { - this.in = in; - this.nonBlockingEnabled = isNonBlockingEnabled; - - if (isNonBlockingEnabled) { - Thread t = new Thread(this); - t.setName("NonBlockingInputStreamThread"); - t.setDaemon(true); - t.start(); - } - } - - /** - * Shuts down the thread that is handling blocking I/O. Note that if the - * thread is currently blocked waiting for I/O it will not actually - * shut down until the I/O is received. Shutting down the I/O thread - * does not prevent this class from being used, but causes the - * non-blocking methods to fail if called and causes {@link #isNonBlockingEnabled()} - * to return false. - */ - public synchronized void shutdown() { - if (!isShutdown && nonBlockingEnabled) { - isShutdown = true; - notify(); - } - } - - /** - * Non-blocking is considered enabled if the feature is enabled and the - * I/O thread has not been shut down. - * @return true if non-blocking mode is enabled. - */ - public boolean isNonBlockingEnabled() { - return nonBlockingEnabled && !isShutdown; - } - - @Override - public void close() throws IOException { - /* - * The underlying input stream is closed first. This means that if the - * I/O thread was blocked waiting on input, it will be woken for us. - */ - in.close(); - shutdown(); - } - - @Override - public int read() throws IOException { - if (nonBlockingEnabled) - return read(0L, false); - return in.read (); - } - - /** - * Peeks to see if there is a byte waiting in the input stream without - * actually consuming the byte. - * - * @param timeout The amount of time to wait, 0 == forever - * @return -1 on eof, -2 if the timeout expired with no available input - * or the character that was read (without consuming it). - */ - public int peek(long timeout) throws IOException { - if (!nonBlockingEnabled || isShutdown) { - throw new UnsupportedOperationException ("peek() " - + "cannot be called as non-blocking operation is disabled"); - } - return read(timeout, true); - } - - /** - * Attempts to read a character from the input stream for a specific - * period of time. - * @param timeout The amount of time to wait for the character - * @return The character read, -1 if EOF is reached, or -2 if the - * read timed out. - */ - public int read(long timeout) throws IOException { - if (!nonBlockingEnabled || isShutdown) { - throw new UnsupportedOperationException ("read() with timeout " - + "cannot be called as non-blocking operation is disabled"); - } - return read(timeout, false); - } - - /** - * Attempts to read a character from the input stream for a specific - * period of time. - * @param timeout The amount of time to wait for the character - * @return The character read, -1 if EOF is reached, or -2 if the - * read timed out. - */ - private synchronized int read(long timeout, boolean isPeek) throws IOException { - /* - * If the thread hit an IOException, we report it. - */ - if (exception != null) { - assert ch == -2; - IOException toBeThrown = exception; - if (!isPeek) - exception = null; - throw toBeThrown; - } - - /* - * If there was a pending character from the thread, then - * we send it. If the timeout is 0L or the thread was shut down - * then do a local read. - */ - if (ch >= -1) { - assert exception == null; - } - else if ((timeout == 0L || isShutdown) && !threadIsReading) { - ch = in.read(); - } - else { - /* - * If the thread isn't reading already, then ask it to do so. - */ - if (!threadIsReading) { - threadIsReading = true; - notify(); - } - - boolean isInfinite = timeout <= 0L; - - /* - * So the thread is currently doing the reading for us. So - * now we play the waiting game. - */ - while (isInfinite || timeout > 0L) { - long start = System.currentTimeMillis (); - - try { - wait(timeout); - } - catch (InterruptedException e) { - /* IGNORED */ - } - - if (exception != null) { - assert ch == -2; - - IOException toBeThrown = exception; - if (!isPeek) - exception = null; - throw toBeThrown; - } - - if (ch >= -1) { - assert exception == null; - break; - } - - if (!isInfinite) { - timeout -= System.currentTimeMillis() - start; - } - } - } - - /* - * ch is the character that was just read. Either we set it because - * a local read was performed or the read thread set it (or failed to - * change it). We will return it's value, but if this was a peek - * operation, then we leave it in place. - */ - int ret = ch; - if (!isPeek) { - ch = -2; - } - return ret; - } - - /** - * This version of read() is very specific to jline's purposes, it - * will always always return a single byte at a time, rather than filling - * the entire buffer. - */ - @Override - public int read (byte[] b, int off, int len) throws IOException { - if (b == null) { - throw new NullPointerException(); - } else if (off < 0 || len < 0 || len > b.length - off) { - throw new IndexOutOfBoundsException(); - } else if (len == 0) { - return 0; - } - - int c; - if (nonBlockingEnabled) - c = this.read(0L); - else - c = in.read(); - - if (c == -1) { - return -1; - } - b[off] = (byte)c; - return 1; - } - - //@Override - public void run () { - Log.debug("NonBlockingInputStream start"); - boolean needToShutdown = false; - boolean needToRead = false; - - while (!needToShutdown) { - - /* - * Synchronize to grab variables accessed by both this thread - * and the accessing thread. - */ - synchronized (this) { - needToShutdown = this.isShutdown; - needToRead = this.threadIsReading; - - try { - /* - * Nothing to do? Then wait. - */ - if (!needToShutdown && !needToRead) { - wait(0); - } - } - catch (InterruptedException e) { - /* IGNORED */ - } - } - - /* - * We're not shutting down, but we need to read. This cannot - * happen while we are holding the lock (which we aren't now). - */ - if (!needToShutdown && needToRead) { - int charRead = -2; - IOException failure = null; - try { - charRead = in.read(); - } - catch (IOException e) { - failure = e; - } - - /* - * Re-grab the lock to update the state. - */ - synchronized (this) { - exception = failure; - ch = charRead; - threadIsReading = false; - notify(); - } - } - } - - Log.debug("NonBlockingInputStream shutdown"); - } -} diff --git a/kshell-console-jline2/src/main/java/lib/jline/internal/Nullable.java b/kshell-console-jline2/src/main/java/lib/jline/internal/Nullable.java deleted file mode 100644 index 05640e3..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/internal/Nullable.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline.internal; - -import java.lang.annotation.*; - -/** - * Marker for reference which can be a null value. - * - * @since 2.7 - */ -@Documented -@Retention(RetentionPolicy.CLASS) -@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE}) -public @interface Nullable -{ - String value() default ""; -} diff --git a/kshell-console-jline2/src/main/java/lib/jline/internal/Preconditions.java b/kshell-console-jline2/src/main/java/lib/jline/internal/Preconditions.java deleted file mode 100644 index 63a9df2..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/internal/Preconditions.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline.internal; - -// Some bits lifted from Guava's ( http://code.google.com/p/guava-libraries/ ) Preconditions. - -/** - * Preconditions. - * - * @author Jason Dillon - * @since 2.7 - */ -public class Preconditions -{ - public static T checkNotNull(final T reference) { - if (reference == null) { - throw new NullPointerException(); - } - return reference; - } -} \ No newline at end of file diff --git a/kshell-console-jline2/src/main/java/lib/jline/internal/ShutdownHooks.java b/kshell-console-jline2/src/main/java/lib/jline/internal/ShutdownHooks.java deleted file mode 100644 index c083880..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/internal/ShutdownHooks.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline.internal; - -import java.util.ArrayList; -import java.util.List; - -/** - * Manages the JLine shutdown-hook thread and tasks to execute on shutdown. - * - * @author Jason Dillon - * @since 2.7 - */ -public class ShutdownHooks -{ - public static final String JLINE_SHUTDOWNHOOK = "jline.shutdownhook"; - - private static final boolean enabled = Configuration.getBoolean(JLINE_SHUTDOWNHOOK, true); - - private static final List tasks = new ArrayList(); - - private static Thread hook; - - public static synchronized T add(final T task) { - Preconditions.checkNotNull(task); - - // If not enabled ignore - if (!enabled) { - Log.debug("Shutdown-hook is disabled; not installing: ", task); - return task; - } - - // Install the hook thread if needed - if (hook == null) { - hook = addHook(new Thread("JLine Shutdown Hook") - { - @Override - public void run() { - runTasks(); - } - }); - } - - // Track the task - Log.debug("Adding shutdown-hook task: ", task); - tasks.add(task); - - return task; - } - - private static synchronized void runTasks() { - Log.debug("Running all shutdown-hook tasks"); - - // Iterate through copy of tasks list - for (Task task : tasks.toArray(new Task[tasks.size()])) { - Log.debug("Running task: ", task); - try { - task.run(); - } - catch (Throwable e) { - Log.warn("Task failed", e); - } - } - - tasks.clear(); - } - - private static Thread addHook(final Thread thread) { - Log.debug("Registering shutdown-hook: ", thread); - try { - Runtime.getRuntime().addShutdownHook(thread); - } - catch (AbstractMethodError e) { - // JDK 1.3+ only method. Bummer. - Log.debug("Failed to register shutdown-hook", e); - } - return thread; - } - - public static synchronized void remove(final Task task) { - Preconditions.checkNotNull(task); - - // ignore if not enabled or hook never installed - if (!enabled || hook == null) { - return; - } - - // Drop the task - tasks.remove(task); - - // If there are no more tasks, then remove the hook thread - if (tasks.isEmpty()) { - removeHook(hook); - hook = null; - } - } - - private static void removeHook(final Thread thread) { - Log.debug("Removing shutdown-hook: ", thread); - - try { - Runtime.getRuntime().removeShutdownHook(thread); - } - catch (AbstractMethodError e) { - // JDK 1.3+ only method. Bummer. - Log.debug("Failed to remove shutdown-hook", e); - } - catch (IllegalStateException e) { - // The VM is shutting down, not a big deal; ignore - } - } - - /** - * Essentially a {@link Runnable} which allows running to throw an exception. - */ - public static interface Task - { - void run() throws Exception; - } -} \ No newline at end of file diff --git a/kshell-console-jline2/src/main/java/lib/jline/internal/TerminalLineSettings.java b/kshell-console-jline2/src/main/java/lib/jline/internal/TerminalLineSettings.java deleted file mode 100644 index 9ed9846..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/internal/TerminalLineSettings.java +++ /dev/null @@ -1,358 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline.internal; - -import java.io.ByteArrayOutputStream; -import java.io.Closeable; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.lang.reflect.Method; -import java.text.MessageFormat; -import java.util.HashMap; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Provides access to terminal line settings via stty. - * - * @author Marc Prud'hommeaux - * @author Dale Kemp - * @author Jason Dillon - * @author Jean-Baptiste Onofré - * @author Guillaume Nodet - * @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 final String UNDEFINED; - - public static final String DEFAULT_TTY = "/dev/tty"; - - private static final boolean SUPPORTS_REDIRECT; - - private static final Object REDIRECT_INHERIT; - private static final Method REDIRECT_INPUT_METHOD; - - private static final Map SETTINGS = new HashMap(); - - static { - if (Configuration.isHpux()) { - UNDEFINED = "^-"; - } else { - UNDEFINED = "undef"; - } - - boolean supportsRedirect; - Object redirectInherit = null; - Method redirectInputMethod = null; - try { - Class redirect = Class.forName("java.lang.ProcessBuilder$Redirect"); - redirectInherit = redirect.getField("INHERIT").get(null); - redirectInputMethod = ProcessBuilder.class.getMethod("redirectInput", redirect); - supportsRedirect = System.class.getMethod("console").invoke(null) != null; - } catch (Throwable t) { - supportsRedirect = false; - } - SUPPORTS_REDIRECT = supportsRedirect; - REDIRECT_INHERIT = redirectInherit; - REDIRECT_INPUT_METHOD = redirectInputMethod; - } - - private String sttyCommand; - - private String shCommand; - - private String ttyDevice; - - private String config; - private String initialConfig; - - private long configLastFetched; - - private boolean useRedirect; - - @Deprecated - public TerminalLineSettings() throws IOException, InterruptedException { - this(DEFAULT_TTY); - } - - @Deprecated - public TerminalLineSettings(String ttyDevice) throws IOException, InterruptedException { - this(ttyDevice, false); - } - - private TerminalLineSettings(String ttyDevice, boolean unused) throws IOException, InterruptedException { - Preconditions.checkNotNull(ttyDevice); - this.sttyCommand = Configuration.getString(JLINE_STTY, DEFAULT_STTY); - this.shCommand = Configuration.getString(JLINE_SH, DEFAULT_SH); - this.ttyDevice = ttyDevice; - this.useRedirect = SUPPORTS_REDIRECT && DEFAULT_TTY.equals(ttyDevice); - this.initialConfig = get("-g").trim(); - this.config = get("-a"); - this.configLastFetched = System.currentTimeMillis(); - - Log.debug("Config: ", config); - - // sanity check - if (config.length() == 0) { - throw new IOException(MessageFormat.format("Unrecognized stty code: {0}", config)); - } - } - - public static synchronized TerminalLineSettings getSettings(String device) throws IOException, InterruptedException { - TerminalLineSettings settings = SETTINGS.get(device); - if (settings == null) { - settings = new TerminalLineSettings(device, false); - SETTINGS.put(device, settings); - } - return settings; - } - - public String getTtyDevice() { - return ttyDevice; - } - - public String getConfig() { - return config; - } - - public void restore() throws IOException, InterruptedException { - set(initialConfig); - } - - public String get(final String args) throws IOException, InterruptedException { - Preconditions.checkNotNull(args); - return stty(args); - } - - public void set(final String args) throws IOException, InterruptedException { - Preconditions.checkNotNull(args); - stty(args.split(" ")); - } - - public void set(final String... args) throws IOException, InterruptedException { - Preconditions.checkNotNull(args); - stty(args); - } - - public void undef(final String name) throws IOException, InterruptedException { - Preconditions.checkNotNull(name); - stty(name, UNDEFINED); - } - - /** - *

- * Get the value of a stty property, including the management of a cache. - *

- * - * @param name the stty property. - * @return the stty property value. - */ - public int getProperty(String name) { - Preconditions.checkNotNull(name); - if (!fetchConfig(name)) { - return -1; - } - return getProperty(name, config); - } - - public String getPropertyAsString(String name) { - Preconditions.checkNotNull(name); - if (!fetchConfig(name)) { - return null; - } - return getPropertyAsString(name, config); - } - - private boolean fetchConfig(String name) { - long currentTime = System.currentTimeMillis(); - try { - // tty properties are cached so we don't have to worry too much about getting term width/height - if (config == null || currentTime - configLastFetched > 1000) { - config = get("-a"); - } - } catch (Exception e) { - if (e instanceof InterruptedException) { - Thread.currentThread().interrupt(); - } - Log.debug("Failed to query stty ", name, "\n", e); - if (config == null) { - return false; - } - } - - // always update the last fetched time and try to parse the output - if (currentTime - configLastFetched > 1000) { - configLastFetched = currentTime; - } - return true; - } - - /** - *

- * Parses a stty output (provided by stty -a) and return the value of a given property. - *

- * - * @param name property name. - * @param stty string resulting of stty -a execution. - * @return value of the given property. - */ - protected static String getPropertyAsString(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 null; - } - } - } - return matcher.group(1); - } - - protected static int getProperty(String name, String stty) { - String str = getPropertyAsString(name, stty); - return str != null ? parseControlChar(str) : -1; - } - - private static int parseControlChar(String str) { - // under - if ("".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 String stty(final String... args) throws IOException, InterruptedException { - String[] s = new String[args.length + 1]; - s[0] = sttyCommand; - System.arraycopy(args, 0, s, 1, args.length); - return exec(s); - } - - private String exec(final String... cmd) throws IOException, InterruptedException { - Preconditions.checkNotNull(cmd); - - Log.trace("Running: ", cmd); - - Process p = null; - if (useRedirect) { - try { - p = inheritInput(new ProcessBuilder(cmd)).start(); - } catch (Throwable t) { - useRedirect = false; - } - } - if (p == null) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < cmd.length; i++) { - if (i > 0) { - sb.append(' '); - } - sb.append(cmd[i]); - } - sb.append(" < "); - sb.append(ttyDevice); - p = new ProcessBuilder(shCommand, "-c", sb.toString()).start(); - } - - String result = waitAndCapture(p); - - Log.trace("Result: ", result); - - return result; - } - - private static ProcessBuilder inheritInput(ProcessBuilder pb) throws Exception { - REDIRECT_INPUT_METHOD.invoke(pb, REDIRECT_INHERIT); - return pb; - } - - public static String waitAndCapture(Process p) throws IOException, InterruptedException { - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - 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); - } - - return bout.toString(); - } - - private static void close(final Closeable... closeables) { - for (Closeable c : closeables) { - if (c != null) { - try { - c.close(); - } catch (Exception e) { - // Ignore - } - } - } - } -} - diff --git a/kshell-console-jline2/src/main/java/lib/jline/internal/TestAccessible.java b/kshell-console-jline2/src/main/java/lib/jline/internal/TestAccessible.java deleted file mode 100644 index 4df0055..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/internal/TestAccessible.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline.internal; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -import static java.lang.annotation.ElementType.CONSTRUCTOR; -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * Marker annotation for members which are exposed for testing access. - * - * @since 2.7 - */ -@Retention(RUNTIME) -@Target({TYPE, CONSTRUCTOR, METHOD, FIELD, PARAMETER}) -@Documented -public @interface TestAccessible -{ - // empty -} \ No newline at end of file diff --git a/kshell-console-jline2/src/main/java/lib/jline/internal/Urls.java b/kshell-console-jline2/src/main/java/lib/jline/internal/Urls.java deleted file mode 100644 index 89b6016..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/internal/Urls.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -package lib.jline.internal; - -import java.io.File; -import java.net.MalformedURLException; -import java.net.URL; - -/** - * URL helpers. - * - * @author Jason Dillon - * @author Guillaume Nodet - * @since 2.7 - */ -public class Urls -{ - public static URL create(final String input) { - if (input == null) { - return null; - } - try { - return new URL(input); - } - catch (MalformedURLException e) { - return create(new File(input)); - } - } - - public static URL create(final File file) { - try { - return file != null ? file.toURI().toURL() : null; - } - catch (MalformedURLException e) { - throw new IllegalStateException(e); - } - } -} \ No newline at end of file diff --git a/kshell-console-jline2/src/main/java/lib/jline/internal/package-info.java b/kshell-console-jline2/src/main/java/lib/jline/internal/package-info.java deleted file mode 100644 index 9c8465f..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/internal/package-info.java +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -/** - * Internal support. - * - * @since 2.0 - */ -package lib.jline.internal; \ No newline at end of file diff --git a/kshell-console-jline2/src/main/java/lib/jline/package-info.java b/kshell-console-jline2/src/main/java/lib/jline/package-info.java deleted file mode 100644 index 18efbdd..0000000 --- a/kshell-console-jline2/src/main/java/lib/jline/package-info.java +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (c) 2002-2016, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * http://www.opensource.org/licenses/bsd-license.php - */ -/** - * JLine 2. - * - * @since 2.0 - */ -package lib.jline; \ No newline at end of file diff --git a/kshell-console-jline2/src/main/kotlin/sparklin/kshell/console/jline2/ConsoleReaderImpl.kt b/kshell-console-jline2/src/main/kotlin/sparklin/kshell/console/jline2/ConsoleReaderImpl.kt deleted file mode 100644 index 12df32a..0000000 --- a/kshell-console-jline2/src/main/kotlin/sparklin/kshell/console/jline2/ConsoleReaderImpl.kt +++ /dev/null @@ -1,58 +0,0 @@ -package sparklin.kshell.console.jline2 - -import sparklin.kshell.configuration.BooleanConverter -import sparklin.kshell.console.Completer -import sparklin.kshell.console.ConsoleReader -import sparklin.kshell.configuration.Configuration -import sparklin.kshell.configuration.IdentityConverter -import lib.jline.console.history.FileHistory -import java.io.File - -import lib.jline.console.ConsoleReader as JLine2ConsoleReader -import lib.jline.console.completer.Completer as JLine2Completer - -class ConsoleReaderImpl: ConsoleReader { - private val reader = JLine2ConsoleReader() - private lateinit var history: FileHistory - - override fun init(config: Configuration) { - reader.expandEvents = false - - val klassName = ConsoleReaderImpl :: class - val persistHistory = config.get("$klassName.persistHistory", BooleanConverter, true) - - if (persistHistory) { - val historyPath = config.get("$klassName.historyPath", IdentityConverter, - System.getProperty("user.home") + File.separator + ".kshell.history") - history = FileHistory(File(historyPath)) - reader.history = history - } - } - - override fun addCompleter(completer: Completer) { - reader.addCompleter { buffer, cursor, candidates -> - if (buffer != null) completer.complete(buffer, cursor, candidates!!) - else -1 - } - } - - override fun setPrompt(prompt: String) { - reader.prompt = prompt - } - - override fun dropHistory(n: Int) { - (1..n).forEach { history.removeLast() } - } - - override fun addHistoryItem(item: String) { - history.add(item) - } - - override fun readLine(): String? = reader.readLine() - - override fun println(s: String) = reader.println(s) - - override fun cleanUp() { - history.flush() - } -} \ No newline at end of file diff --git a/kshell-console-jline2/src/main/resources/lib/jline/console/completer/CandidateListCompletionHandler.properties b/kshell-console-jline2/src/main/resources/lib/jline/console/completer/CandidateListCompletionHandler.properties deleted file mode 100644 index 331256b..0000000 --- a/kshell-console-jline2/src/main/resources/lib/jline/console/completer/CandidateListCompletionHandler.properties +++ /dev/null @@ -1,13 +0,0 @@ -# -# Copyright (c) 2002-2016, the original author or authors. -# -# This software is distributable under the BSD license. See the terms of the -# BSD license in the documentation provided with this software. -# -# http://www.opensource.org/licenses/bsd-license.php -# - -DISPLAY_CANDIDATES=Display all %d possibilities? (y or n) -DISPLAY_CANDIDATES_YES=y -DISPLAY_CANDIDATES_NO=n -DISPLAY_MORE=--More-- diff --git a/kshell-repl-api/pom.xml b/kshell-repl-api/pom.xml index 2959b7c..9f806f0 100644 --- a/kshell-repl-api/pom.xml +++ b/kshell-repl-api/pom.xml @@ -71,6 +71,8 @@ src/main/kotlin src/main/java + src/test/kotlin + src/test/java diff --git a/kshell-repl-api/src/main/kotlin/sparklin/kshell/repl/CodeAnalyser.kt b/kshell-repl-api/src/main/kotlin/sparklin/kshell/repl/CodeAnalyser.kt index a1a8c77..97771ba 100644 --- a/kshell-repl-api/src/main/kotlin/sparklin/kshell/repl/CodeAnalyser.kt +++ b/kshell-repl-api/src/main/kotlin/sparklin/kshell/repl/CodeAnalyser.kt @@ -59,7 +59,7 @@ class CodeAnalyzer(environment: KotlinCoreEnvironment) { class Success(diagnostics: Diagnostics) : AnalyzerResult(diagnostics) } - fun doAnalyze(linePsi: KtFile, codeLine: CodeLine): AnalyzerResult { + fun doAnalyze(linePsi: KtFile): AnalyzerResult { trace.clearDiagnostics() scriptDeclarationFactory.setDelegateFactory(FileBasedDeclarationProviderFactory(resolveSession.storageManager, listOf(linePsi))) /*val context = */topDownAnalyzer.analyzeDeclarations(topDownAnalysisContext.topDownAnalysisMode, listOf(linePsi)) diff --git a/kshell-repl-api/src/main/kotlin/sparklin/kshell/repl/Repl.kt b/kshell-repl-api/src/main/kotlin/sparklin/kshell/repl/Repl.kt index 4f40361..3161a16 100644 --- a/kshell-repl-api/src/main/kotlin/sparklin/kshell/repl/Repl.kt +++ b/kshell-repl-api/src/main/kotlin/sparklin/kshell/repl/Repl.kt @@ -19,15 +19,14 @@ class Repl(disposable: Disposable, baseClassloader: ClassLoader? = Thread.currentThread().contextClassLoader) : this(Disposer.newDisposable(), compilerConfiguration, messageCollector, baseClasspath, baseClassloader) - private val compiler = ReplCompiler(disposable, compilerConfiguration, messageCollector) - private val evaluator = ReplEvaluator(baseClasspath, baseClassloader) - internal val state = ReplState(ReentrantReadWriteLock()) + val compiler = ReplCompiler(disposable, compilerConfiguration, messageCollector) + val evaluator = ReplEvaluator(baseClasspath, baseClassloader) + val state = ReplState(ReentrantReadWriteLock()) fun eval(code: String): Result { val res = compiler.compile(state, CodeLine(state.lineIndex.getAndIncrement(), code)) return when (res) { is Result.Error -> Result.Error(res.error) - is Result.Incomplete -> Result.Incomplete() is Result.Success -> evaluator.eval(state, res.data, null) } } diff --git a/kshell-repl-api/src/main/kotlin/sparklin/kshell/repl/ReplApi.kt b/kshell-repl-api/src/main/kotlin/sparklin/kshell/repl/ReplApi.kt index 955101d..58a9b7d 100644 --- a/kshell-repl-api/src/main/kotlin/sparklin/kshell/repl/ReplApi.kt +++ b/kshell-repl-api/src/main/kotlin/sparklin/kshell/repl/ReplApi.kt @@ -9,14 +9,13 @@ import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.locks.ReentrantReadWriteLock sealed class Result { - class Incomplete: Result() data class Error(val error: E): Result() data class Success(val data: S): Result() } -sealed class EvalError(val message: String) { - class CompileError(message: String, val location: CompilerMessageLocation? = null) : EvalError(message) - class RuntimeError(message: String, val cause: Exception? = null): EvalError(message) +sealed class EvalError(val isIncomplete: Boolean, val message: String?) { + class CompileError(val psiFile: KtFile, isIncomplete: Boolean, message: String? = null, val location: CompilerMessageLocation? = null) : EvalError(isIncomplete, message) + class RuntimeError(message: String, val cause: Exception? = null) : EvalError(false, message) } sealed class EvalResult { @@ -86,7 +85,20 @@ open class ReplState(val lock: ReentrantReadWriteLock) { val history: MutableList = mutableListOf() } -data class CodeLine(val no: Int, val code: String, val part: Int = 0) +interface SourceCode { + fun mkFileName(): String + fun nextPart(codePart: String): SourceCode + fun replace(code: String): SourceCode + val no: Int + val code: String + val part: Int +} + +data class CodeLine(override val no: Int, override val code: String, override val part: Int = 0): SourceCode { + override fun mkFileName(): String = "Line_$no" + if (part != 0) "_$part" else "" + override fun nextPart(codePart: String): CodeLine = CodeLine(no, codePart, part + 1) + override fun replace(code: String): CodeLine = CodeLine(no, code, part) +} data class CompilationData(val snippets: List, val classes: CompiledClasses) diff --git a/kshell-repl-api/src/main/kotlin/sparklin/kshell/repl/ReplChecker.kt b/kshell-repl-api/src/main/kotlin/sparklin/kshell/repl/ReplChecker.kt index 045a62e..88c2ec7 100644 --- a/kshell-repl-api/src/main/kotlin/sparklin/kshell/repl/ReplChecker.kt +++ b/kshell-repl-api/src/main/kotlin/sparklin/kshell/repl/ReplChecker.kt @@ -46,24 +46,24 @@ class ReplChecker( private fun createDiagnosticHolder() = ConsoleDiagnosticMessageHolder() - fun check(state: ReplState, codeLine: CodeLine, isScript: Boolean): Result { + fun check(state: ReplState, code: SourceCode, isScript: Boolean): Result { state.lock.write { - val fileName = makeFileBaseName(codeLine) + (if (isScript) ".kts" else ".kt") + val fileName = code.mkFileName() + (if (isScript) ".kts" else ".kt") val virtualFile = - LightVirtualFile(fileName, KotlinLanguage.INSTANCE, StringUtil.convertLineSeparators(codeLine.code)).apply { + LightVirtualFile(fileName, KotlinLanguage.INSTANCE, StringUtil.convertLineSeparators(code.code)).apply { charset = CharsetToolkit.UTF8_CHARSET } val psiFile: KtFile = psiFileFactory.trySetupPsiForFile(virtualFile, KotlinLanguage.INSTANCE, true, false) as KtFile? - ?: error("File not analyzed at line ${codeLine.no}: ${codeLine.code}") + ?: error("File not analyzed ${code.code}") val errorHolder = createDiagnosticHolder() val syntaxErrorReport = AnalyzerWithCompilerReport.reportSyntaxErrors(psiFile, errorHolder) return when { - syntaxErrorReport.isHasErrors && syntaxErrorReport.isAllErrorsAtEof -> Result.Incomplete() - syntaxErrorReport.isHasErrors -> Result.Error(EvalError.CompileError(errorHolder.renderMessage())) + syntaxErrorReport.isHasErrors && syntaxErrorReport.isAllErrorsAtEof -> Result.Error(EvalError.CompileError(psiFile, true)) + syntaxErrorReport.isHasErrors -> Result.Error(EvalError.CompileError(psiFile, false, errorHolder.renderMessage())) else -> Result.Success(CheckedCode(psiFile, errorHolder)) } } diff --git a/kshell-repl-api/src/main/kotlin/sparklin/kshell/repl/ReplCompiler.kt b/kshell-repl-api/src/main/kotlin/sparklin/kshell/repl/ReplCompiler.kt index 651274b..8878345 100644 --- a/kshell-repl-api/src/main/kotlin/sparklin/kshell/repl/ReplCompiler.kt +++ b/kshell-repl-api/src/main/kotlin/sparklin/kshell/repl/ReplCompiler.kt @@ -18,15 +18,14 @@ class ReplCompiler(disposable: Disposable, private val compilerConfiguration: CompilerConfiguration, messageCollector: MessageCollector ) { - private val checker = ReplChecker(disposable, compilerConfiguration, messageCollector) + val checker = ReplChecker(disposable, compilerConfiguration, messageCollector) private val analyzerEngine = CodeAnalyzer(checker.environment) - fun compile(state: ReplState, codeLine: CodeLine, previousStage: List = listOf()): Result { + fun compile(state: ReplState, codeLine: SourceCode, previousStage: List = listOf()): Result { state.lock.write { val lineResult = checker.check(state, codeLine, true) val checkedLine = when (lineResult) { - is Result.Incomplete -> return Result.Incomplete() is Result.Error -> return Result.Error(lineResult.error) is Result.Success -> lineResult.data } @@ -34,7 +33,7 @@ class ReplCompiler(disposable: Disposable, val psiFile = checkedLine.psiFile val errorHolder = checkedLine.errorHolder - val generatedClassname = makeFileBaseName(codeLine) + val generatedClassname = codeLine.mkFileName() val snippets = psiToSnippets(psiFile, generatedClassname) @@ -42,7 +41,7 @@ class ReplCompiler(disposable: Disposable, val conflict = snippets.filterIsInstance().find { permanents.contains(it.name) } if (conflict != null) { - return Result.Error(EvalError.CompileError("${conflict.name} cannot be replaced")) + return Result.Error(EvalError.CompileError(checkedLine.psiFile, false, "${conflict.name} cannot be replaced")) } val (actualSnippets, deferredSnippets) = checkOverloads(snippets) @@ -53,18 +52,18 @@ class ReplCompiler(disposable: Disposable, val code = generateKotlinCodeFor(generatedClassname, import + previousStage, actualSnippets) - val result = checker.check(state, CodeLine(codeLine.no, code, codeLine.part), false) + val result = checker.check(state, codeLine.replace(code), false) val psiForObject = when (result) { is Result.Success -> result.data.psiFile else -> throw IllegalStateException("Should never happen") } - val analysisResult = analyzerEngine.doAnalyze(psiForObject, codeLine) + val analysisResult = analyzerEngine.doAnalyze(psiForObject) AnalyzerWithCompilerReport.reportDiagnostics(analysisResult.diagnostics, errorHolder) if (analysisResult is CodeAnalyzer.AnalyzerResult.Error) - return Result.Error(EvalError.CompileError(errorHolder.renderedDiagnostics)) + return Result.Error(EvalError.CompileError(psiFile, false, errorHolder.renderedDiagnostics)) val expression = psiForObject.getChildOfType() ?.declarations @@ -118,11 +117,10 @@ class ReplCompiler(disposable: Disposable, return if (deferredSnippets.isNotEmpty()) { val otherCode = deferredSnippets.joinToString(separator = "\n") { it.code() } - val otherResult = compile(state, CodeLine(codeLine.no, otherCode, codeLine.part + 1), actualSnippets) + val otherResult = compile(state, codeLine.nextPart(otherCode), actualSnippets) when (otherResult) { is Result.Error -> otherResult - is Result.Incomplete -> throw IllegalStateException("Should never happen") is Result.Success -> Result.Success(CompilationData(actualSnippets + otherResult.data.snippets, classes + otherResult.data.classes)) } } else { diff --git a/kshell-repl-api/src/main/kotlin/sparklin/kshell/repl/ReplEvaluator.kt b/kshell-repl-api/src/main/kotlin/sparklin/kshell/repl/ReplEvaluator.kt index e291183..f587188 100644 --- a/kshell-repl-api/src/main/kotlin/sparklin/kshell/repl/ReplEvaluator.kt +++ b/kshell-repl-api/src/main/kotlin/sparklin/kshell/repl/ReplEvaluator.kt @@ -26,7 +26,7 @@ open class ReplEvaluator( scriptInstance } catch (e: Throwable) { - return Result.Error(EvalError.RuntimeError(e.message ?: e.javaClass.canonicalName, e as? Exception)) + return Result.Error(EvalError.RuntimeError(e.message ?: e.cause?.message ?: e.javaClass.canonicalName, e.cause as? Exception)) } commitSnippets(state, data.snippets) diff --git a/kshell-repl-api/src/main/kotlin/sparklin/kshell/repl/Util.kt b/kshell-repl-api/src/main/kotlin/sparklin/kshell/repl/Util.kt index 051c4af..665cb5d 100644 --- a/kshell-repl-api/src/main/kotlin/sparklin/kshell/repl/Util.kt +++ b/kshell-repl-api/src/main/kotlin/sparklin/kshell/repl/Util.kt @@ -19,9 +19,6 @@ internal fun getJavaVersion(): Int { } } -internal fun makeFileBaseName(codeLine: CodeLine) = - "Line_${codeLine.no}" + if (codeLine.part != 0) "_${codeLine.part}" else "" - internal fun List.containsWithName(name: String): Boolean = this.any { it is NamedSnippet && it.name == name } diff --git a/kshell-repl-api/src/test/kotlin/sparklin/kshell/repl/ReplTest.kt b/kshell-repl-api/src/test/kotlin/sparklin/kshell/repl/ReplTest.kt index e495ebe..243a56c 100644 --- a/kshell-repl-api/src/test/kotlin/sparklin/kshell/repl/ReplTest.kt +++ b/kshell-repl-api/src/test/kotlin/sparklin/kshell/repl/ReplTest.kt @@ -1,43 +1,10 @@ package sparklin.kshell.repl -import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys -import org.jetbrains.kotlin.cli.common.messages.MessageCollector -import org.jetbrains.kotlin.cli.common.messages.MessageRenderer -import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector -import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots -import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots -import org.jetbrains.kotlin.config.CommonConfigurationKeys -import org.jetbrains.kotlin.config.CompilerConfiguration -import org.jetbrains.kotlin.utils.PathUtil import org.junit.Assert.assertEquals import org.junit.Assert.fail -import org.junit.Before import org.junit.Test -import java.io.File -import java.net.URLClassLoader -class ReplTest { - private lateinit var repl: Repl - - @Before - fun setup() { - val messageCollector: MessageCollector = PrintingMessageCollector(System.out, MessageRenderer.WITHOUT_PATHS, false) - val moduleName = "my-module" - - val classpath = listOf(PathUtil.stdlibPathForJar()) - - val conf = CompilerConfiguration().apply { - addJvmClasspathRoots(PathUtil.getJdkClassesRoots(File(System.getProperty("java.home")))) - addJvmClasspathRoots(classpath) - put(CommonConfigurationKeys.MODULE_NAME, moduleName) - put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector) - } - - val baseClassloader = URLClassLoader(conf.jvmClasspathRoots.map { it.toURI().toURL() } - .toTypedArray(), javaClass.classLoader) - - repl = Repl(conf, messageCollector, classpath, baseClassloader) - } +class ReplTest : ReplTestBase() { @Test fun testSimpleValue() { @@ -167,6 +134,19 @@ class ReplTest { assertSuccess(repl.eval("fun f(v: JFile)=v.close()")) } + @Test + fun testWhile() { + assertValue(2, repl.eval(""" + var i = 10 + var x = 0 + while (i > 0) { + x = 2 * i + i -- + } + x + """)) + } + private fun assertValue(expected: Any?, result: Result) { when (result) { is Result.Error -> fail(result.error.message) diff --git a/kshell-repl-api/src/test/kotlin/sparklin/kshell/repl/ReplTestBase.kt b/kshell-repl-api/src/test/kotlin/sparklin/kshell/repl/ReplTestBase.kt new file mode 100644 index 0000000..b9c41f2 --- /dev/null +++ b/kshell-repl-api/src/test/kotlin/sparklin/kshell/repl/ReplTestBase.kt @@ -0,0 +1,37 @@ +package sparklin.kshell.repl + +import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys +import org.jetbrains.kotlin.cli.common.messages.MessageCollector +import org.jetbrains.kotlin.cli.common.messages.MessageRenderer +import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector +import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots +import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots +import org.jetbrains.kotlin.config.CommonConfigurationKeys +import org.jetbrains.kotlin.config.CompilerConfiguration +import org.jetbrains.kotlin.utils.PathUtil +import org.junit.Before +import java.io.File +import java.net.URLClassLoader + +open class ReplTestBase { + protected lateinit var repl: Repl + @Before + fun setup() { + val messageCollector: MessageCollector = PrintingMessageCollector(System.out, MessageRenderer.WITHOUT_PATHS, false) + val moduleName = "my-module" + + val classpath = listOf(PathUtil.stdlibPathForJar()) + + val conf = CompilerConfiguration().apply { + addJvmClasspathRoots(PathUtil.getJdkClassesRoots(File(System.getProperty("java.home")))) + addJvmClasspathRoots(classpath) + put(CommonConfigurationKeys.MODULE_NAME, moduleName) + put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector) + } + + val baseClassloader = URLClassLoader(conf.jvmClasspathRoots.map { it.toURI().toURL() } + .toTypedArray(), javaClass.classLoader) + + repl = Repl(conf, messageCollector, classpath, baseClassloader) + } +} \ No newline at end of file diff --git a/kshell/pom.xml b/kshell/pom.xml index 0e083d6..02aaa80 100644 --- a/kshell/pom.xml +++ b/kshell/pom.xml @@ -48,7 +48,11 @@ kshell-repl-api 0.2-SNAPSHOT - + + sparklin + jline3-shaded + 0.2-SNAPSHOT + diff --git a/kshell/src/main/kotlin/sparklin/kshell/BaseCommand.kt b/kshell/src/main/kotlin/sparklin/kshell/BaseCommand.kt index 1cd2874..40fc5bc 100644 --- a/kshell/src/main/kotlin/sparklin/kshell/BaseCommand.kt +++ b/kshell/src/main/kotlin/sparklin/kshell/BaseCommand.kt @@ -1,7 +1,5 @@ package sparklin.kshell -import sparklin.kshell.configuration.Configuration -import sparklin.kshell.console.Completer abstract class BaseCommand: sparklin.kshell.Command { abstract val description: String @@ -14,6 +12,4 @@ abstract class BaseCommand: sparklin.kshell.Command { val shortPhrase = if (short != null) "or $short " else "" return String.format("%-30s %s", ":$name $shortPhrase$params", description) } - - override fun completer(): Completer = Completer.DEFAULT_COMPLETER } \ No newline at end of file diff --git a/kshell/src/main/kotlin/sparklin/kshell/Command.kt b/kshell/src/main/kotlin/sparklin/kshell/Command.kt index 54dcf3f..b8860e8 100644 --- a/kshell/src/main/kotlin/sparklin/kshell/Command.kt +++ b/kshell/src/main/kotlin/sparklin/kshell/Command.kt @@ -1,6 +1,6 @@ package sparklin.kshell -import sparklin.kshell.console.Completer +import sparklin.kshell.org.jline.reader.Highlighter interface Command { fun execute(line: String) @@ -9,16 +9,19 @@ interface Command { val short: String? - fun completer(): Completer - fun help(): String fun desc(): String + + fun highlighter(): Highlighter? = null } -fun Command.match(line: String): Boolean { +fun Command.match(line: String): Boolean = match(line, { x, y -> x.equals(y, ignoreCase = true) }) + +fun Command.weakMatch(line: String): Boolean = match(line, { x, y -> x.startsWith(y, ignoreCase = true) }) + +inline fun Command.match(line: String, func: (String, String) -> Boolean): Boolean { val ind = line.indexOf(' ') val command = if (ind < 0) line else line.substring(0, ind) - return (short != null && command.equals(":$short", ignoreCase = true)) || - command.equals(":$name", ignoreCase = true) + return (short != null && func(command, ":$short")) || func(command, ":$name") } \ No newline at end of file diff --git a/kshell/src/main/kotlin/sparklin/kshell/ContextDependentCompleter.kt b/kshell/src/main/kotlin/sparklin/kshell/ContextDependentCompleter.kt deleted file mode 100644 index dd06a9e..0000000 --- a/kshell/src/main/kotlin/sparklin/kshell/ContextDependentCompleter.kt +++ /dev/null @@ -1,17 +0,0 @@ -package sparklin.kshell - -import sparklin.kshell.console.Completer - -class ContextDependentCompleter(private val commands: List, - private val actionModeAvailable: () -> Boolean, - private val defaultCompleter: Completer): Completer { - - override fun complete(buffer: String, cursor: Int, candidates: MutableList): Int { - return if (actionModeAvailable() && buffer.startsWith(":")) { - val action = commands.find { it.match(buffer) } - action?.completer()?.complete(buffer, cursor, candidates) ?: -1 - } else { - defaultCompleter.complete(buffer, cursor, candidates) - } - } -} \ No newline at end of file diff --git a/kshell/src/main/kotlin/sparklin/kshell/ContextHighlighter.kt b/kshell/src/main/kotlin/sparklin/kshell/ContextHighlighter.kt new file mode 100644 index 0000000..dff85cc --- /dev/null +++ b/kshell/src/main/kotlin/sparklin/kshell/ContextHighlighter.kt @@ -0,0 +1,29 @@ +package sparklin.kshell + +import sparklin.kshell.org.jline.reader.Highlighter +import sparklin.kshell.org.jline.reader.LineReader +import sparklin.kshell.org.jline.utils.AttributedString +import sparklin.kshell.org.jline.utils.AttributedStringBuilder +import sparklin.kshell.plugins.BaseHighlighter + +class ContextHighlighter(private val isSyntaxMode: (String) -> Boolean, + private val findCommand: (String) -> Command?): Highlighter { + + var syntaxHighlighter: BaseHighlighter = DEFAULT + + override fun highlight(reader: LineReader, buffer: String): AttributedString { + if (isSyntaxMode(buffer)) return syntaxHighlighter.highlight(reader, buffer) + val highlighter = findCommand(buffer)?.highlighter() ?: DEFAULT + return highlighter.highlight(reader, buffer) + } + + companion object { + private val DEFAULT = object : BaseHighlighter { + override fun highlight(buffer: String, offset: Int): AttributedString { + val sb = AttributedStringBuilder() + sb.append(buffer) + return sb.toAttributedString() + } + } + } +} \ No newline at end of file diff --git a/kshell/src/main/kotlin/sparklin/kshell/KShell.kt b/kshell/src/main/kotlin/sparklin/kshell/KShell.kt index eab9ce4..4f49ede 100644 --- a/kshell/src/main/kotlin/sparklin/kshell/KShell.kt +++ b/kshell/src/main/kotlin/sparklin/kshell/KShell.kt @@ -2,14 +2,17 @@ package sparklin.kshell import com.intellij.openapi.Disposable import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys -import sparklin.kshell.console.Completer import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots import org.jetbrains.kotlin.config.CommonConfigurationKeys import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.utils.PathUtil +import sparklin.kshell.org.jline.reader.LineReaderBuilder +import sparklin.kshell.org.jline.terminal.TerminalBuilder import sparklin.kshell.configuration.Configuration +import sparklin.kshell.org.jline.reader.LineReader +import sparklin.kshell.org.jline.reader.impl.history.DefaultHistory import sparklin.kshell.repl.* import sparklin.kshell.wrappers.ResultWrapper import java.io.Closeable @@ -23,7 +26,7 @@ open class KShell(val disposable: Disposable, val messageCollector: MessageCollector, val classpath: List, val moduleName: String, - val classLoader: ClassLoader) : Closeable { + val classLoader: ClassLoader) { private val compilerConfiguration = CompilerConfiguration().apply { addJvmClasspathRoots(PathUtil.getJdkClassesRoots(File(System.getProperty("java.home")))) @@ -32,20 +35,35 @@ open class KShell(val disposable: Disposable, put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector) } - val baseClassloader = URLClassLoader(compilerConfiguration.jvmClasspathRoots.map { it.toURI().toURL() } + private val baseClassloader = URLClassLoader(compilerConfiguration.jvmClasspathRoots.map { it.toURI().toURL() } .toTypedArray(), classLoader) - private lateinit var compiler: ReplCompiler + lateinit var compiler: ReplCompiler + private set + private lateinit var evaluator: ReplEvaluator + val state = ReplState(ReentrantReadWriteLock()) val incompleteLines = arrayListOf() - val reader = configuration.getConsoleReader() + + val term = TerminalBuilder.builder().build() + lateinit var readerBuilder: LineReaderBuilder + lateinit var reader: LineReader + val highlighter = ContextHighlighter({ s -> !isCommandMode(s)}, { s -> commands.firstOrNull { it.weakMatch(s) } }) + val commands = mutableListOf(FakeQuit()) val eventManager = EventManager() var invokeWrapper: InvokeWrapper? = null + var prompt = { + if (incompleteLines.isEmpty()) + "kotlin> " + else + "... " + } + private class FakeQuit: sparklin.kshell.BaseCommand() { override val name: String = "quit" override val short: String = "q" @@ -53,61 +71,66 @@ open class KShell(val disposable: Disposable, override fun execute(line: String) {} } - open fun buildDefaultCompleter() = Completer.DEFAULT_COMPLETER - fun listCommands(): Iterable = commands.asIterable() fun addClasspathRoots(files: List) = compilerConfiguration.addJvmClasspathRoots(files) fun initEngine() { - reader.apply { - addCompleter(ContextDependentCompleter(commands, incompleteLines::isEmpty, buildDefaultCompleter())) - } + readerBuilder = LineReaderBuilder.builder().terminal(term).highlighter(highlighter) + reader = readerBuilder.build() configuration.load() configuration.plugins().forEach { it.init(this, configuration) } + reader.setVariable(LineReader.HISTORY_FILE, configuration.get(LineReader.HISTORY_FILE, + System.getProperty("user.home") + File.separator + ".kshell_history")) + reader.setVariable(LineReader.SECONDARY_PROMPT_PATTERN, "") + compiler = ReplCompiler(disposable, compilerConfiguration, messageCollector) evaluator = ReplEvaluator(classpath, baseClassloader) } + private fun isCommandMode(buffer: String): Boolean = incompleteLines.isEmpty() + && buffer.startsWith(":") + && !buffer.startsWith("::") + fun doRun() { initEngine() do { - printPrompt() - val line = reader.readLine() + val line = reader.readLine(prompt()) if (line == null || isQuitAction(line)) break - if (incompleteLines.isEmpty() && line.startsWith(":") && !line.startsWith("::")) { + if (isCommandMode(line)) { try { val action = commands.first { it.match(line) } action.execute(line) state.lineIndex.getAndIncrement() } catch (_: NoSuchElementException) { - reader.println("Unknown command $line") + println("Unknown command $line") } catch (e: Exception) { commandError(e) } } else { if (line.isBlank() && (incompleteLines.isNotEmpty() && incompleteLines.last().isBlank())) { incompleteLines.clear() - reader.println("You typed two blank lines. Starting a new command.") + println("You typed two blank lines. Starting a new command.") } else { val source = (incompleteLines + line).joinToString(separator = "\n") val result = eval(source).result when (result) { is Result.Error -> { - incompleteLines.clear() - handleError(result.error) + if (result.error.isIncomplete) { + incompleteLines.add(line) + } else { + incompleteLines.clear() + handleError(result.error) + } } is Result.Success -> { incompleteLines.clear() - handleResult(result.data) - } - is Result.Incomplete -> { - incompleteLines.add(line) + handleSuccess(result.data) } } } @@ -127,15 +150,14 @@ open class KShell(val disposable: Disposable, private fun nextLine(code: String) = CodeLine(state.lineIndex.getAndIncrement(), code) - fun compile(code: String) = compiler.compile(state, nextLine(code)) + private fun compile(code: String) = compiler.compile(state, nextLine(code)) + + fun compile(code: SourceCode) = compiler.compile(state, code) fun eval(source: String): ResultWrapper = state.lock.write { val compileResult = compile(source) ResultWrapper(when (compileResult) { - is Result.Incomplete -> { - Result.Incomplete() - } is Result.Error -> { Result.Error(compileResult.error) } @@ -146,33 +168,27 @@ open class KShell(val disposable: Disposable, }) } - fun handleError(error: EvalError) { - reader.println("Message: ${error.message}") - } + fun handleError(error: EvalError) = when (error) { + is EvalError.RuntimeError -> { + if (error.cause != null) error.cause?.printStackTrace() else println("Runtime Error: ${error.message}") + } + is EvalError.CompileError -> println(error.message) + } - fun handleResult(result: EvalResult) { + fun handleSuccess(result: EvalResult) { if (result is EvalResult.ValueResult) - reader.println(result.toString()) - } - - fun printPrompt() { - if (incompleteLines.isEmpty()) - reader.setPrompt("kotlin [${state.lineIndex.get()}]> ") - else - reader.setPrompt("... ") + println(result.toString()) } - open fun commandError(e: Exception) { + private fun commandError(e: Exception) { e.printStackTrace() } - override fun close() { - disposable.dispose() + fun cleanUp() { + reader.history.save() } - open fun cleanUp() { - reader.cleanUp() - } + fun checker() = compiler.checker } class OnCompile(private val data: CompilationData) : Event { diff --git a/kshell/src/main/kotlin/sparklin/kshell/Util.kt b/kshell/src/main/kotlin/sparklin/kshell/Util.kt new file mode 100644 index 0000000..246fa11 --- /dev/null +++ b/kshell/src/main/kotlin/sparklin/kshell/Util.kt @@ -0,0 +1,9 @@ +package sparklin.kshell + +fun calcHumanReadableSize(bytes: Long, si: Boolean = false): String { + val unit = if (si) 1000 else 1024 + if (bytes < unit) return "$bytes B" + val exp = (Math.log(bytes.toDouble()) / Math.log(unit.toDouble())).toInt() + val pre = (if (si) "kMGTPE" else "KMGTPE")[exp - 1] + if (si) "" else "i" + return String.format("%.1f %sB", bytes / Math.pow(unit.toDouble(), exp.toDouble()), pre) +} \ No newline at end of file diff --git a/kshell/src/main/kotlin/sparklin/kshell/configuration/Configuration.kt b/kshell/src/main/kotlin/sparklin/kshell/configuration/Configuration.kt index fee5084..f196ff6 100644 --- a/kshell/src/main/kotlin/sparklin/kshell/configuration/Configuration.kt +++ b/kshell/src/main/kotlin/sparklin/kshell/configuration/Configuration.kt @@ -1,7 +1,6 @@ package sparklin.kshell.configuration import sparklin.kshell.Plugin -import sparklin.kshell.console.ConsoleReader import kotlin.reflect.KProperty abstract class Configuration { @@ -21,8 +20,6 @@ abstract class Configuration { abstract fun plugins(): Iterator - abstract fun getConsoleReader(): ConsoleReader - inner class DelegateProvider(private val converter: Converter, val default: () -> T) { operator fun getValue(thisRef: R, property: KProperty<*>): T { val p = "${thisRef.javaClass.kotlin.qualifiedName}.${property.name}" @@ -39,6 +36,8 @@ abstract class Configuration { fun get(converter: Converter, default: () -> T): DelegateProvider = DelegateProvider(converter, default) + fun get(converter: Converter, default: T): DelegateProvider = DelegateProvider(converter, { default }) + fun get(default: () -> String) = get(IdentityConverter, default) fun get(default: String) = get({ default }) diff --git a/kshell/src/main/kotlin/sparklin/kshell/configuration/ConfigurationImpl.kt b/kshell/src/main/kotlin/sparklin/kshell/configuration/ConfigurationImpl.kt index c1577cc..dc92ef1 100644 --- a/kshell/src/main/kotlin/sparklin/kshell/configuration/ConfigurationImpl.kt +++ b/kshell/src/main/kotlin/sparklin/kshell/configuration/ConfigurationImpl.kt @@ -1,9 +1,6 @@ package sparklin.kshell.configuration -import sparklin.kshell.plugins.HelpPlugin -import sparklin.kshell.plugins.LoadFilePlugin -import sparklin.kshell.plugins.PastePlugin -import sparklin.kshell.plugins.RuntimePlugin +import sparklin.kshell.plugins.* import java.io.BufferedReader import java.io.File import java.io.FileReader @@ -11,9 +8,11 @@ import java.util.* class ConfigurationImpl : PropertyBasedConfiguration(Properties(), listOf(LoadFilePlugin::class.qualifiedName!!, - RuntimePlugin::class.qualifiedName!!, - HelpPlugin::class.qualifiedName!!, - PastePlugin::class.qualifiedName!!)) { + RuntimePlugin::class.qualifiedName!!, + HelpPlugin::class.qualifiedName!!, + PastePlugin::class.qualifiedName!!, + SyntaxPlugin::class.qualifiedName!!, + PromptPlugin::class.qualifiedName!!)) { override fun load() { val path = configPath() @@ -24,6 +23,5 @@ class ConfigurationImpl : PropertyBasedConfiguration(Properties(), super.load() } - private fun configPath() = System.getProperty("config.path") ?: - (System.getProperty("user.home") ?: "") + File.separator + ".kshell" + private fun configPath() = System.getProperty("config.path") ?: (System.getProperty("user.home") ?: "") + File.separator+".kshell" } \ No newline at end of file diff --git a/kshell/src/main/kotlin/sparklin/kshell/configuration/PropertyBasedConfiguration.kt b/kshell/src/main/kotlin/sparklin/kshell/configuration/PropertyBasedConfiguration.kt index 31aefd6..895fbea 100644 --- a/kshell/src/main/kotlin/sparklin/kshell/configuration/PropertyBasedConfiguration.kt +++ b/kshell/src/main/kotlin/sparklin/kshell/configuration/PropertyBasedConfiguration.kt @@ -1,17 +1,10 @@ package sparklin.kshell.configuration import sparklin.kshell.Plugin -import sparklin.kshell.console.ConsoleReader -import sparklin.kshell.plugins.HelpPlugin -import sparklin.kshell.plugins.LoadFilePlugin -import sparklin.kshell.plugins.PastePlugin -import sparklin.kshell.plugins.RuntimePlugin -import java.io.* import java.util.* open class PropertyBasedConfiguration(protected val props: Properties, protected val defaultPlugins: List) : Configuration() { private val plugins = linkedMapOf>() - private val consoleReader = CachedInstance() override fun get(key: String, converter: Converter): T? { val strValue = props.getProperty(key) @@ -31,11 +24,4 @@ open class PropertyBasedConfiguration(protected val props: Properties, protected override fun getPlugin(klassName: String): Plugin? = plugins[klassName]?.get() override fun plugins(): Iterator = plugins.values.map { it.get()!! }.iterator() - - override fun getConsoleReader(): ConsoleReader { - val klassName = get("console.class","sparklin.kshell.console.jline2.ConsoleReaderImpl") - val reader = consoleReader.load(klassName, ConsoleReader::class) - reader.init(this) - return reader - } } \ No newline at end of file diff --git a/kshell/src/main/kotlin/sparklin/kshell/console/Completer.kt b/kshell/src/main/kotlin/sparklin/kshell/console/Completer.kt deleted file mode 100644 index a127b7c..0000000 --- a/kshell/src/main/kotlin/sparklin/kshell/console/Completer.kt +++ /dev/null @@ -1,11 +0,0 @@ -package sparklin.kshell.console - -interface Completer { - fun complete(buffer: String, cursor: Int, candidates: MutableList): Int - - companion object { - val DEFAULT_COMPLETER = object: Completer { - override fun complete(buffer: String, cursor: Int, candidates: MutableList): Int = -1 - } - } -} \ No newline at end of file diff --git a/kshell/src/main/kotlin/sparklin/kshell/console/ConsoleReader.kt b/kshell/src/main/kotlin/sparklin/kshell/console/ConsoleReader.kt deleted file mode 100644 index 3dae93e..0000000 --- a/kshell/src/main/kotlin/sparklin/kshell/console/ConsoleReader.kt +++ /dev/null @@ -1,21 +0,0 @@ -package sparklin.kshell.console - -import sparklin.kshell.configuration.Configuration - -interface ConsoleReader { - fun init(config: Configuration) - - fun addCompleter(completer: Completer) - - fun setPrompt(prompt: String) - - fun dropHistory(n: Int) - - fun addHistoryItem(item: String) - - fun readLine(): String? - - fun println(s: String) - - fun cleanUp() -} \ No newline at end of file diff --git a/kshell/src/main/kotlin/sparklin/kshell/plugins/BaseHighlighter.kt b/kshell/src/main/kotlin/sparklin/kshell/plugins/BaseHighlighter.kt new file mode 100644 index 0000000..897bed9 --- /dev/null +++ b/kshell/src/main/kotlin/sparklin/kshell/plugins/BaseHighlighter.kt @@ -0,0 +1,13 @@ +package sparklin.kshell.plugins + +import sparklin.kshell.org.jline.reader.Highlighter +import sparklin.kshell.org.jline.reader.LineReader +import sparklin.kshell.org.jline.utils.AttributedString + +interface BaseHighlighter: Highlighter { + fun highlight(buffer: String, offset: Int = 0): AttributedString + + override fun highlight(reader: LineReader, buffer: String): AttributedString { + return highlight(buffer) + } +} \ No newline at end of file diff --git a/kshell/src/main/kotlin/sparklin/kshell/plugins/FileCompleter.kt b/kshell/src/main/kotlin/sparklin/kshell/plugins/FileCompleter.kt deleted file mode 100644 index 6917aae..0000000 --- a/kshell/src/main/kotlin/sparklin/kshell/plugins/FileCompleter.kt +++ /dev/null @@ -1,95 +0,0 @@ -package sparklin.kshell.plugins - -import java.io.File -import sparklin.kshell.console.Completer - - -open class FileCompleter : Completer { - - override fun complete(buffer: String, cursor: Int, candidates: MutableList): Int { - var buffer = buffer - - checkNotNull>(candidates) - - if (OS_IS_WINDOWS) { - buffer = buffer.replace('/', '\\') - } - - // remove command (:load and so on) from the data to be completed - val p = buffer.indexOf(' ') - var translated: String = buffer.substring(p + 1) - - val homeDir = userHome - - // Special character: ~ maps to the user's home directory - if (translated.startsWith("~" + separator())) { - translated = homeDir.path + translated.substring(1) - } else if (translated.startsWith("~")) { - translated = homeDir.parentFile.absolutePath - } else if (!File(translated).isAbsolute) { - val cwd = userDir.absolutePath - translated = cwd + separator() + translated - } - - val file = File(translated) - val dir: File? - - if (translated.endsWith(separator())) { - dir = file - } else { - dir = file.parentFile - } - - val entries = if (dir == null) arrayOfNulls(0) else dir.listFiles() - - return matchFiles(buffer, translated, entries, candidates) - } - - protected fun separator(): String { - return File.separator - } - - private val userHome: File - get() = File(System.getProperty("user.home"))//Configuration.getUserHome() - - private val userDir: File - get() = File(".") - - private fun matchFiles(buffer: String, translated: String, files: Array, candidates: MutableList): Int { - var matches = 0 - - // first pass: just count the matches - for (file in files) { - if (file!!.absolutePath.startsWith(translated)) { - matches++ - } - } - for (file in files) { - if (file!!.absolutePath.startsWith(translated)) { - val name = file!!.name + if (matches == 1 && file.isDirectory) separator() else " " - candidates.add(render(file!!, name).toString()) - } - } - - var index = buffer.lastIndexOf(separator()) - - if (index < 0) index = buffer.lastIndexOf(' ') - - return index + separator().length - } - - protected fun render(file: File, name: CharSequence): CharSequence { - return name - } - - companion object { - // TODO: Handle files with spaces in them - - private val OS_IS_WINDOWS: Boolean - - init { - val os = "mac" // FIXME: Configuration.getOsName() - OS_IS_WINDOWS = os.contains("windows") - } - } -} \ No newline at end of file diff --git a/kshell/src/main/kotlin/sparklin/kshell/plugins/HelpPlugin.kt b/kshell/src/main/kotlin/sparklin/kshell/plugins/HelpPlugin.kt index 3c7b130..940455c 100644 --- a/kshell/src/main/kotlin/sparklin/kshell/plugins/HelpPlugin.kt +++ b/kshell/src/main/kotlin/sparklin/kshell/plugins/HelpPlugin.kt @@ -1,6 +1,5 @@ package sparklin.kshell.plugins -import sparklin.kshell.console.ConsoleReader import sparklin.kshell.BaseCommand import sparklin.kshell.Plugin import sparklin.kshell.KShell @@ -22,14 +21,14 @@ class HelpPlugin: Plugin { repl.apply { if (args.size == 1) { val help = commands.joinToString(separator = "\n") { it.desc() } - console.println(help) + println(help) } else { val command = args[1] try { val res = commands.first { it.match(":$command") } - console.println(res.help()) + println(res.help()) } catch (_: NoSuchElementException) { - console.println("$command: no such command. Type :help for help.") + println("$command: no such command. Type :help for help.") } } } @@ -37,11 +36,9 @@ class HelpPlugin: Plugin { } lateinit var repl: KShell - lateinit var console: ConsoleReader override fun init(repl: KShell, config: Configuration) { this.repl = repl - this.console = config.getConsoleReader() repl.registerCommand(Help(config)) } diff --git a/kshell/src/main/kotlin/sparklin/kshell/plugins/KotlinHighlighter.kt b/kshell/src/main/kotlin/sparklin/kshell/plugins/KotlinHighlighter.kt new file mode 100644 index 0000000..c97ab59 --- /dev/null +++ b/kshell/src/main/kotlin/sparklin/kshell/plugins/KotlinHighlighter.kt @@ -0,0 +1,71 @@ +package sparklin.kshell.plugins + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.lexer.KtKeywordToken +import org.jetbrains.kotlin.psi.KtFunction +import org.jetbrains.kotlin.psi.KtUserType +import sparklin.kshell.org.jline.utils.AttributedString +import sparklin.kshell.org.jline.utils.AttributedStringBuilder +import sparklin.kshell.org.jline.utils.AttributedStyle +import sparklin.kshell.repl.ReplChecker +import sparklin.kshell.repl.ReplState +import sparklin.kshell.repl.Result +import sparklin.kshell.repl.SourceCode + +class KotlinHighlighter(private val state: ReplState, private val checker: () -> ReplChecker, + private val styles: SyntaxPlugin.HighlightStyles): BaseHighlighter { + + + override fun highlight(buffer: String, offset: Int): AttributedString { + require(offset >= 0) + val code = buffer.substring(offset) + val fragment = CodeFragment(code) + val lineResult = checker().check(state, fragment, true) + + val psi = when (lineResult) { + is Result.Error -> lineResult.error.psiFile + is Result.Success -> lineResult.data.psiFile + } + + val sb = AttributedStringBuilder() + if (offset != 0) sb.append(buffer.substring(0, offset)) + for (i in code.indices) { + psi.findElementAt(i)?.let { element -> + val st = when { + element.isKeyword() -> styles.keyword + element.isFunction() -> styles.function + element.isNumber() -> styles.number + element.isString() -> styles.string + element.isStringTemplate() -> styles.stringTemplate + element.isType() -> styles.type + else -> null + } ?: AttributedStyle.DEFAULT + sb.style(st) + sb.append(code[i]) + } + } + return sb.toAttributedString() + } + + private fun PsiElement.isKeyword() = node?.elementType is KtKeywordToken + private fun PsiElement.isType() = isIdentifier() && parent?.parent is KtUserType + private fun PsiElement.isFunction() = isIdentifier() && parent is KtFunction //|| parent?.parent is KtCallExpression) + private fun PsiElement.isNumber() = checkElementType("INTEGER_LITERAL", "FLOAT_CONSTANT") + private fun PsiElement.isString() = checkElementType("REGULAR_STRING_PART", "OPEN_QUOTE", "CLOSING_QUOTE") + private fun PsiElement.isStringTemplate() = node?.elementType.toString().contains("TEMPLATE_ENTRY") + private fun PsiElement.isIdentifier() = checkElementType("IDENTIFIER") + + private fun PsiElement.checkElementType(s: String, vararg ss: String): Boolean { + val e = node?.elementType.toString() + if (e == s) return true + return ss.any { it == e } + } + + private class CodeFragment(override val code: String): SourceCode { + override val no: Int = 0 + override val part: Int = 0 + override fun mkFileName(): String = "Fragment" + override fun nextPart(codePart: String): SourceCode = throw UnsupportedOperationException("Should never happen") + override fun replace(code: String): SourceCode = throw UnsupportedOperationException("Should never happen") + } +} \ No newline at end of file diff --git a/kshell/src/main/kotlin/sparklin/kshell/plugins/LoadFilePlugin.kt b/kshell/src/main/kotlin/sparklin/kshell/plugins/LoadFilePlugin.kt index 8fdb9ca..de5b76a 100644 --- a/kshell/src/main/kotlin/sparklin/kshell/plugins/LoadFilePlugin.kt +++ b/kshell/src/main/kotlin/sparklin/kshell/plugins/LoadFilePlugin.kt @@ -4,7 +4,6 @@ import sparklin.kshell.BaseCommand import sparklin.kshell.KShell import sparklin.kshell.configuration.Configuration import sparklin.kshell.Plugin -import sparklin.kshell.console.Completer import java.io.File class LoadFilePlugin: Plugin { @@ -22,7 +21,6 @@ class LoadFilePlugin: Plugin { repl.eval(content) } - override fun completer(): Completer = FileCompleter() } lateinit var repl: KShell diff --git a/kshell/src/main/kotlin/sparklin/kshell/plugins/PastePlugin.kt b/kshell/src/main/kotlin/sparklin/kshell/plugins/PastePlugin.kt index 5e636ce..05dcd0f 100644 --- a/kshell/src/main/kotlin/sparklin/kshell/plugins/PastePlugin.kt +++ b/kshell/src/main/kotlin/sparklin/kshell/plugins/PastePlugin.kt @@ -1,11 +1,12 @@ package sparklin.kshell.plugins +import sparklin.kshell.org.jline.reader.EndOfFileException +import sparklin.kshell.org.jline.reader.LineReader import sparklin.kshell.BaseCommand import sparklin.kshell.KShell import sparklin.kshell.Plugin -import sparklin.kshell.configuration.CachedInstance import sparklin.kshell.configuration.Configuration -import sparklin.kshell.console.ConsoleReader +import sparklin.kshell.repl.Result class PastePlugin : Plugin { inner class Paste(conf: Configuration): BaseCommand() { @@ -17,42 +18,37 @@ class PastePlugin : Plugin { println("// Entering paste mode (ctrl-D to finish)") val buf = StringBuilder() var lineCount = 0 - while(true) { - val s = pasteConsole.readLine() - if (s == null) { - break - } else { + try { + while (true) { + val s = pasteConsole.readLine("") buf.append(s) buf.append('\n') - lineCount ++ + lineCount++ } - } + } catch (e: EndOfFileException) { } val code = buf.toString() - console.addHistoryItem(code) println("// Exiting paste mode, now interpreting.") - repl.eval(code) + val result = repl.eval(code).result + when (result) { + is Result.Error -> repl.handleError(result.error) + is Result.Success -> repl.handleSuccess(result.data) + } } } lateinit var repl: KShell - lateinit var console: ConsoleReader - lateinit var pasteConsole: ConsoleReader + lateinit var console: LineReader + lateinit var pasteConsole: LineReader override fun init(repl: KShell, config: Configuration) { this.repl = repl - this.console = config.getConsoleReader() - this.pasteConsole = getPasteConsoleReader(config) + this.console = repl.reader + this.pasteConsole = repl.readerBuilder.highlighter(console.highlighter).build() + pasteConsole.option(LineReader.Option.DISABLE_HIGHLIGHTER, console.isSet(LineReader.Option.DISABLE_HIGHLIGHTER)) + pasteConsole.setVariable(LineReader.SECONDARY_PROMPT_PATTERN, "") repl.registerCommand(Paste(config)) } override fun cleanUp() { } - - private fun getPasteConsoleReader(config: Configuration): ConsoleReader { - val klassName = config.get("console.class","sparklin.kshell.console.jline2.ConsoleReaderImpl") - val reader = CachedInstance(). - load(klassName, ConsoleReader::class) - reader.init(config) - return reader - } } \ No newline at end of file diff --git a/kshell/src/main/kotlin/sparklin/kshell/plugins/PromptPlugin.kt b/kshell/src/main/kotlin/sparklin/kshell/plugins/PromptPlugin.kt new file mode 100644 index 0000000..c199848 --- /dev/null +++ b/kshell/src/main/kotlin/sparklin/kshell/plugins/PromptPlugin.kt @@ -0,0 +1,112 @@ +package sparklin.kshell.plugins + +import sparklin.kshell.BaseCommand +import sparklin.kshell.KShell +import sparklin.kshell.Plugin +import sparklin.kshell.calcHumanReadableSize +import sparklin.kshell.configuration.Configuration +import java.time.LocalTime + +class PromptPlugin: Plugin { + inner class Prompt(conf: Configuration): BaseCommand() { + override val name: String by conf.get(default = "prompt") + override val short: String? by conf.getNullable() + override val description: String = "customize prompt" + override val params: String = "[pattern]" + + private val pattern: String by conf.get(default = "[%l]") + private val incomplete: String by conf.get(default = "...") + + init { + this@PromptPlugin.pattern = pattern + this@PromptPlugin.incomplete = incomplete + } + + override fun execute(line: String) { + val p = line.indexOf(' ') + if (p > 0) { + this@PromptPlugin.pattern = line.substring(p + 1).trim() + } else { + help() + } + } + + override fun help(): String { + return "no available" + } + + } + + lateinit var repl: KShell + lateinit var conf: Configuration + + lateinit var pattern: String + lateinit var incomplete: String + + private val types = mutableMapOf( + "l" to { "${repl.state.lineIndex.get()}" }, + "u" to { System.getProperty("user.name") }, + "d" to ::formattedTime, + "t" to ::totalMemory, + "m" to ::maxMemory) + + override fun init(repl: KShell, config: Configuration) { + this.repl = repl + this.conf = config + + repl.prompt = ::promptFunc + repl.registerCommand(Prompt(config)) + } + + private fun formattedTime(): String = LocalTime.now().toString() + + private fun totalMemory() = calcHumanReadableSize(Runtime.getRuntime().totalMemory()) + + private fun maxMemory() = calcHumanReadableSize(Runtime.getRuntime().maxMemory()) + + private fun promptFunc(): String = if (repl.incompleteLines.isNotEmpty()) incomplete else format("$pattern ", types) + + fun registerCustomType(type: String, display: () -> String) { + // TODO: check conflicts + types[type] = display + } + + override fun cleanUp() { } + + companion object { + internal fun format(p: String, types: Map String>): String { + var state = 0 + val sb = StringBuilder() + val tb = StringBuilder() + + for (ch in p) { + when (ch) { + '%' -> state = 1 + '{' -> when (state) { + 1 -> state = 2 + else -> sb.append(ch) + } + '}' -> when (state) { + 2 -> { + val t = tb.toString() + sb.append(types.getOrDefault(t, { "%{$t}" })()) + tb.setLength(0) + state = 0 + } + else -> sb.append(ch) + } + else -> when (state) { + 1 -> { + sb.append(types.getOrDefault(ch.toString(), { "%$ch" })()) + state = 0 + } + 2 -> tb.append(ch) + else -> sb.append(ch) + } + + } + } + return sb.toString() + } + } +} \ No newline at end of file diff --git a/kshell/src/main/kotlin/sparklin/kshell/plugins/RuntimePlugin.kt b/kshell/src/main/kotlin/sparklin/kshell/plugins/RuntimePlugin.kt index 1b4f43a..970b635 100644 --- a/kshell/src/main/kotlin/sparklin/kshell/plugins/RuntimePlugin.kt +++ b/kshell/src/main/kotlin/sparklin/kshell/plugins/RuntimePlugin.kt @@ -2,42 +2,67 @@ package sparklin.kshell.plugins import sparklin.kshell.* import sparklin.kshell.configuration.Configuration -import sparklin.kshell.console.ConsoleReader +import sparklin.kshell.org.jline.reader.Highlighter +import sparklin.kshell.org.jline.reader.LineReader +import sparklin.kshell.org.jline.utils.AttributedString import sparklin.kshell.repl.* import sparklin.kshell.repl.ReplCompiler.Companion.RESULT_FIELD_NAME import sparklin.kshell.repl.ReplCompiler.Companion.RUN_FIELD_NAME +import java.util.concurrent.atomic.AtomicInteger import kotlin.reflect.KClass import kotlin.reflect.KFunction import kotlin.reflect.KProperty1 import kotlin.reflect.KVariance import kotlin.reflect.full.declaredMemberProperties +import kotlin.reflect.full.primaryConstructor import kotlin.reflect.full.valueParameters class RuntimePlugin : Plugin { + inner class Imports(conf: Configuration): BaseCommand() { + override val name: String by conf.get(default = "imports ") + override val short: String by conf.get(default = "i") + + override val description: String = "show imports" + + override fun execute(line: String) { + repl.state.history + .filterIsInstance() + .forEach { println(it.psi.text) } + } + } + inner class InferType(conf: Configuration): BaseCommand() { override val name: String by conf.get(default = "type") override val short: String by conf.get(default = "t") override val description: String = "display the type of an expression without evaluating it" override val params = "" - override fun execute(line: String) { val p = line.indexOf(' ') val expr = line.substring(p + 1).trim() - val compileResult = repl.compile(expr) + val compileResult = repl.compile(CodeExpr(counter.getAndIncrement(), expr)) when (compileResult) { - is Result.Incomplete -> - console.println("Incomplete line") - is Result.Error -> - repl.handleError(EvalError.CompileError(compileResult.error.message)) - is Result.Success -> { - compileResult.data.classes.type?.let { - console.println(it) - } - } + is Result.Error -> repl.handleError(compileResult.error) + is Result.Success -> compileResult.data.classes.type?.let { println(it) } } } + + override fun highlighter(): Highlighter = customHighlighter + } + + private class CustomHighlighter(val baseHighlighter: () -> BaseHighlighter): Highlighter { + override fun highlight(reader: LineReader, buffer: String): AttributedString { + val p = buffer.indexOf(' ') + return baseHighlighter().highlight(buffer, p + 1) + } + } + + private data class CodeExpr(override val no: Int, override val code: String): SourceCode { + override val part: Int = 0 + override fun mkFileName(): String = "TypeInference_$no" + override fun nextPart(codePart: String): SourceCode = throw UnsupportedOperationException("Should never happen") + override fun replace(code: String): CodeExpr = CodeExpr(no, code) } inner class ListSymbols(conf: Configuration) : sparklin.kshell.BaseCommand() { @@ -53,13 +78,13 @@ class RuntimePlugin : Plugin { } private lateinit var repl: KShell - private lateinit var console: ConsoleReader private var lastCompiledClasses: CompiledClasses? = null private lateinit var table: SymbolsTable + private lateinit var customHighlighter: CustomHighlighter + private val counter = AtomicInteger(0) override fun init(repl: KShell, config: Configuration) { this.repl = repl - this.console = config.getConsoleReader() this.table = SymbolsTable(repl.state.history) @@ -70,9 +95,11 @@ class RuntimePlugin : Plugin { }) repl.invokeWrapper = ExtractSymbols(repl.invokeWrapper, table) + customHighlighter = CustomHighlighter({ repl.highlighter.syntaxHighlighter }) repl.registerCommand(InferType(config)) repl.registerCommand(ListSymbols(config)) + repl.registerCommand(Imports(config)) } override fun cleanUp() { } @@ -90,7 +117,9 @@ sealed class Symbol(val namespace: String, val name: String, val kind: SymbolKin class ClassSymbol(namespace: String, name: String, private val clazz: KClass<*>): Symbol(namespace, name, SymbolKind.CLASS) { override fun show(): String { - return "class $name" + val constructor = clazz.primaryConstructor?.let { FunctionSymbol.show(it, true) } ?: "" + val data = if (clazz.isData) "data " else "" + return "${data}class $name$constructor" } } @@ -103,17 +132,21 @@ class InstanceSymbol(namespace: String, name: String, private val obj: Any?, pri } class FunctionSymbol(namespace: String, name: String, private val func: KFunction<*>): Symbol(namespace, name, SymbolKind.FUNCTION) { - override fun show(): String { - val tp = if (func.typeParameters.isNotEmpty()) "<" + func.typeParameters.joinToString(separator = ",") { - (if (it.variance != KVariance.INVARIANT) "${it.variance} ${it.name}" else it.name) + - (if (it.upperBounds.isNotEmpty()) ": ${it.upperBounds.joinToString(separator = ",")}" else "") + override fun show(): String = Companion.show(func) + + companion object { + fun show(func: KFunction<*>, isConstructor: Boolean = false): String { + val tp = if (func.typeParameters.isNotEmpty()) "<" + func.typeParameters.joinToString(separator = ",") { + (if (it.variance != KVariance.INVARIANT) "${it.variance} ${it.name}" else it.name) + + (if (it.upperBounds.isNotEmpty()) ": ${it.upperBounds.joinToString(separator = ",")}" else "") } + "> " else "" - val vp = func.valueParameters.joinToString(separator = ",") { - it.name + ": " + it.type - } + val vp = func.valueParameters.joinToString(separator = ", ") { + it.name + ": " + it.type + } - return "fun $tp$name($vp): ${func.returnType}" + return if (isConstructor) "${tp.trim()}($vp)" else "fun $tp${func.name}($vp): ${func.returnType}" + } } } @@ -131,15 +164,16 @@ class SymbolsTable(val history: List) { fun list(pattern: String? = null, kinds: List = listOf(SymbolKind.INSTANCE, SymbolKind.FUNCTION, SymbolKind.CLASS)): List { val regex = pattern?.let { Regex(it) } - return symbols.filter { kinds.contains(it.kind) && !isShadowed(it) + return symbols.filter { kinds.contains(it.kind) && !isShadowed(it) && (regex == null || it.name.matches(regex)) }.map { it.show() } } - fun isShadowed(symbol: Symbol) = - history.filterIsInstance().findLast { it.klass == symbol.namespace && it.name == symbol.name } != null - + fun isShadowed(symbol: Symbol): Boolean = history + .filterIsInstance() + .findLast { it.klass == symbol.namespace && it.name == symbol.name } + ?.shadowed ?: false } class ExtractSymbols(private val wrapper: InvokeWrapper?, private val table: SymbolsTable): InvokeWrapper { diff --git a/kshell/src/main/kotlin/sparklin/kshell/plugins/SyntaxPlugin.kt b/kshell/src/main/kotlin/sparklin/kshell/plugins/SyntaxPlugin.kt new file mode 100644 index 0000000..ba4c29e --- /dev/null +++ b/kshell/src/main/kotlin/sparklin/kshell/plugins/SyntaxPlugin.kt @@ -0,0 +1,118 @@ +package sparklin.kshell.plugins + +import sparklin.kshell.BaseCommand +import sparklin.kshell.KShell +import sparklin.kshell.Plugin +import sparklin.kshell.configuration.BooleanConverter +import sparklin.kshell.configuration.Configuration +import sparklin.kshell.configuration.Converter +import sparklin.kshell.org.jline.reader.LineReader +import sparklin.kshell.org.jline.utils.AttributedStyle +import sparklin.kshell.org.jline.utils.AttributedStyle.* + +class SyntaxPlugin: Plugin { + inner class Syntax(conf: Configuration): BaseCommand() { + override val name: String by conf.get(default = "syntax") + override val short: String? by conf.getNullable() + override val description: String = "syntax highlighting" + private val on: Boolean by conf.get(BooleanConverter, default = true) + + override val params: String = "{on | off}" + + init { + repl.highlighter.apply { + syntaxHighlighter = kotlinHighlighter + reader.option(LineReader.Option.DISABLE_HIGHLIGHTER, !on) + } + } + + override fun execute(line: String) { + val args = line.replace('\t', ' ').split(' ') + + if (args.size != 2) { + println(help()) + return + } + + when (args[1]) { + "on" -> reader.option(LineReader.Option.DISABLE_HIGHLIGHTER, false) + "off" -> reader.option(LineReader.Option.DISABLE_HIGHLIGHTER, true) + else -> { + println("Unknown option ${args[1]}") + println(help()) + } + } + } + + override fun help(): String = """ + Enables syntax highlighting + :$name on - enable highlighting + :$name off - disable highlighting""".trimIndent() + } + + lateinit var repl: KShell + lateinit var kotlinHighlighter: KotlinHighlighter + lateinit var reader: LineReader + + override fun init(repl: KShell, config: Configuration) { + this.repl = repl + this.reader = repl.reader + kotlinHighlighter = KotlinHighlighter(repl.state, repl::checker, HighlightStylesFromConfiguration(config)) + + repl.registerCommand(Syntax(config)) + } + + override fun cleanUp() { } + + interface HighlightStyles { + val keyword: AttributedStyle? + val function: AttributedStyle? + val type: AttributedStyle? + val string: AttributedStyle? + val stringTemplate: AttributedStyle? + val number: AttributedStyle? + } + + class HighlightStylesFromConfiguration(conf: Configuration): HighlightStyles { + override val keyword: AttributedStyle by conf.get(StyleConverter, BOLD.foreground(RED)) + override val function: AttributedStyle by conf.get(StyleConverter, DEFAULT.foreground(YELLOW)) + override val type: AttributedStyle by conf.get(StyleConverter, DEFAULT.foreground(MAGENTA)) + override val string: AttributedStyle by conf.get(StyleConverter, DEFAULT.foreground(GREEN)) + override val stringTemplate: AttributedStyle by conf.get(StyleConverter, BOLD.foreground(YELLOW)) + override val number: AttributedStyle by conf.get(StyleConverter, DEFAULT.foreground(CYAN)) + } + + object StyleConverter: Converter { + // style: bold; color: yellow; background-color: black; + override fun convert(s: String): AttributedStyle { + val parsed = s.split(';').map { + val (k, v) = it.split(':') + k.trim() to v.trim() + }.toMap() + val attributedStyle = style(parsed.getOrDefault("style", "default")) + parsed["color"]?.let { attributedStyle.foreground(color(it)) } + parsed["background-color"]?.let { attributedStyle.background(color(it)) } + return attributedStyle + } + + private fun style(s: String) = when (s) { + "default" -> DEFAULT + "bold" -> BOLD + "inverse" -> INVERSE + else -> throw IllegalArgumentException("Unsupported style: $s") + } + + private fun color(s: String) = when (s) { + "black" -> BLACK + "red" -> RED + "green" -> GREEN + "yellow" -> YELLOW + "blue" -> BLUE + "magenta" -> MAGENTA + "cyan" -> CYAN + "white" -> WHITE + "bright" -> BRIGHT + else -> throw IllegalArgumentException("Unsupported color: $s") + } + } +} \ No newline at end of file diff --git a/kshell/src/main/kotlin/sparklin/kshell/wrappers/ResultWrapper.kt b/kshell/src/main/kotlin/sparklin/kshell/wrappers/ResultWrapper.kt index 4220219..f341d69 100644 --- a/kshell/src/main/kotlin/sparklin/kshell/wrappers/ResultWrapper.kt +++ b/kshell/src/main/kotlin/sparklin/kshell/wrappers/ResultWrapper.kt @@ -12,15 +12,13 @@ class ResultWrapper(val result: Result) { fun getStatus(): Status = when (result) { - is Result.Error -> Status.ERROR - is Result.Incomplete -> Status.INCOMPLETE + is Result.Error -> if (result.error.isIncomplete) Status.INCOMPLETE else Status.ERROR is Result.Success -> Status.SUCCESS } fun getMessage(): String? = when (result) { is Result.Error -> result.error.message - is Result.Incomplete -> null is Result.Success -> if (result.data is EvalResult.ValueResult) result.data.toString() else null } diff --git a/kshell/src/test/kotlin/sparklin/kshell/EventManagerTest.kt b/kshell/src/test/kotlin/sparklin/kshell/EventManagerTest.kt index bc1a64c..613d1ec 100644 --- a/kshell/src/test/kotlin/sparklin/kshell/EventManagerTest.kt +++ b/kshell/src/test/kotlin/sparklin/kshell/EventManagerTest.kt @@ -16,21 +16,22 @@ class EventManagerTest { @Test fun consistencyTest() { val arr = arrayOf("", "") + val em = EventManager() - EventManager.registerEventHandler(MyEvent0::class, object : EventHandler { + em.registerEventHandler(MyEvent0::class, object : EventHandler { override fun handle(event: MyEvent0) { arr[0] = event.data() } }) - EventManager.registerEventHandler(MyEvent1::class, object : EventHandler { + em.registerEventHandler(MyEvent1::class, object : EventHandler { override fun handle(event: MyEvent1) { arr[1] = event.data() } }) - EventManager.emitEvent(MyEvent0("hello")) - EventManager.emitEvent(MyEvent1("world")) + em.emitEvent(MyEvent0("hello")) + em.emitEvent(MyEvent1("world")) assertEquals("hello", arr[0]) assertEquals("world", arr[1]) diff --git a/kshell/src/test/kotlin/sparklin/kshell/SingleLineOutputStream.kt b/kshell/src/test/kotlin/sparklin/kshell/SingleLineOutputStream.kt deleted file mode 100644 index 089dcb3..0000000 --- a/kshell/src/test/kotlin/sparklin/kshell/SingleLineOutputStream.kt +++ /dev/null @@ -1,28 +0,0 @@ -package sparklin.kshell - -import java.io.OutputStream - -class SingleLineOutputStream : OutputStream() { - private val lineBuilder = StringBuilder() - private val buf = StringBuilder() - private var line: String = "" - - override fun write(b: Int) { - if (b == '\n'.toInt()) { - line = lineBuilder.toString() - buf.append(line) - buf.append('\n') - lineBuilder.setLength(0) - } else { - lineBuilder.append(b.toChar()) - } - } - - fun last(): String = line - - fun clear() { - buf.setLength(0) - } - - override fun toString(): String = buf.toString() -} \ No newline at end of file diff --git a/kshell/src/test/kotlin/sparklin/kshell/SingleLineOutputStreamTest.kt b/kshell/src/test/kotlin/sparklin/kshell/SingleLineOutputStreamTest.kt deleted file mode 100644 index 3d9ff53..0000000 --- a/kshell/src/test/kotlin/sparklin/kshell/SingleLineOutputStreamTest.kt +++ /dev/null @@ -1,17 +0,0 @@ -package sparklin.kshell - -import org.junit.Assert.assertEquals -import org.junit.Test -import java.io.PrintWriter - -class SingleLineOutputStreamTest { - @Test - fun testPrintln() { - val stream = SingleLineOutputStream() - val out = PrintWriter(stream) - assertEquals("", stream.last()) - out.println("hello") - out.flush() - assertEquals("hello", stream.last()) - } -} \ No newline at end of file diff --git a/kshell/src/test/kotlin/sparklin/kshell/plugins/KotlinHighlighterTest.kt b/kshell/src/test/kotlin/sparklin/kshell/plugins/KotlinHighlighterTest.kt new file mode 100644 index 0000000..a796ae2 --- /dev/null +++ b/kshell/src/test/kotlin/sparklin/kshell/plugins/KotlinHighlighterTest.kt @@ -0,0 +1,96 @@ +package sparklin.kshell.plugins + +import org.junit.Assert.assertEquals +import org.junit.Test +import sparklin.kshell.repl.ReplTestBase +import sparklin.kshell.org.jline.utils.AttributedString +import sparklin.kshell.org.jline.utils.AttributedStyle +import sparklin.kshell.org.jline.utils.AttributedStyle.* + +class KotlinHighlighterTest : ReplTestBase() { + private val keyword = "keyword" + private val function = "function" + private val type = "type" + private val string = "string" + private val stringTemplate = "stringTemplate" + private val number = "number" + + private val styles: Map = mapOf( + keyword to BOLD.foreground(RED), + function to DEFAULT.foreground(YELLOW), + type to DEFAULT.foreground(MAGENTA), + string to DEFAULT.foreground(GREEN), + stringTemplate to BOLD.foreground(YELLOW), + number to DEFAULT.foreground(CYAN)) + + private val mnemonics = listOf( + 'k' to keyword, + 'f' to function, + 't' to type, + 's' to string, + '$' to stringTemplate, + 'n' to number + ) + + class HighlightStylesFromMap(styles: Map): SyntaxPlugin.HighlightStyles { + override val keyword: AttributedStyle? by styles + override val function: AttributedStyle? by styles + override val type: AttributedStyle? by styles + override val string: AttributedStyle? by styles + override val stringTemplate: AttributedStyle? by styles + override val number: AttributedStyle? by styles + } + + private val stylesToMnemonics = mnemonics.map { + (m, e) -> + styles[e]!! to m + }.toMap() + + @Test + fun testFunction() { + val ht = KotlinHighlighter(repl.state, { repl.compiler.checker }, styles.filter(listOf(keyword, function))) + assertEquals("kkk f(x: Int) = x", ht.highlight("fun g(x: Int) = x").mnemonics()) + } + + @Test + fun testType() { + val ht = KotlinHighlighter(repl.state, { repl.compiler.checker }, + styles.filter(listOf(type))) + assertEquals("fun g(x: ttt): ttt = x", ht.highlight("fun g(x: Int): Int = x").mnemonics()) + assertEquals("val x: tttttt = 1.0", ht.highlight("val x: Double = 1.0").mnemonics()) + } + + @Test + fun testString() { + val ht = KotlinHighlighter(repl.state, { repl.compiler.checker }, + styles.filter(listOf(string, stringTemplate))) + assertEquals("sssssss\$\$world\$s\$xs", ht.highlight("\"hello \${world} \$x\"").mnemonics()) + } + + @Test + fun testNumber() { + val ht = KotlinHighlighter(repl.state, { repl.compiler.checker }, + styles.filter(listOf(number))) + assertEquals("val x: Double = nnn", ht.highlight("val x: Double = 1.0").mnemonics()) + assertEquals("val x: Double = nnnn", ht.highlight("val x: Double = 1.0f").mnemonics()) + assertEquals("println(nnnn)", ht.highlight("println(1000)").mnemonics()) + } + + @Test + fun testOffset() { + val ht = KotlinHighlighter(repl.state, { repl.compiler.checker }, + styles.filter(listOf(number))) + assertEquals(":t n + n", ht.highlight(":t 1 + 1", 2).mnemonics()) + } + + private fun Map.filter(list: List): HighlightStylesFromMap = + HighlightStylesFromMap(map { if (list.contains(it.key)) Pair(it.key, it.value) else Pair(it.key, null) }.toMap()) + + private fun AttributedString.mnemonics(): String { + val sb = StringBuilder() + for (i in indices) { + sb.append(stylesToMnemonics.getOrDefault(styleAt(i), this[i])) + } + return sb.toString() + } +} \ No newline at end of file diff --git a/kshell/src/test/kotlin/sparklin/kshell/plugins/PromptPluginTest.kt b/kshell/src/test/kotlin/sparklin/kshell/plugins/PromptPluginTest.kt new file mode 100644 index 0000000..75a93e3 --- /dev/null +++ b/kshell/src/test/kotlin/sparklin/kshell/plugins/PromptPluginTest.kt @@ -0,0 +1,12 @@ +package sparklin.kshell.plugins + +import org.junit.Assert.* +import org.junit.Test + +class PromptPluginTest { + @Test + fun testFormat() { + val types = mapOf("s" to { "S" }, "ss" to {"Z"}) + assertEquals("hello SZ{} %d", PromptPlugin.format("hello %s%{ss}{} %d", types)) + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 870028c..7d19184 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ 1.2.30 1.8 - 0.1.3 + 0.2-SNAPSHOT sparklin @@ -18,7 +18,6 @@ kshell-repl-api kshell - kshell-console-jline2 sparklin-spark-1.x sparklin-spark-2.x hdfs-browser diff --git a/zeppelin-interpreter/pom.xml b/zeppelin-interpreter/pom.xml index fee4e00..06fe87c 100644 --- a/zeppelin-interpreter/pom.xml +++ b/zeppelin-interpreter/pom.xml @@ -197,34 +197,6 @@ - - org.apache.maven.plugins - maven-jar-plugin - 2.6 - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/zeppelin-interpreter/src/main/java/sparklin/zeppelin/InterpreterConfiguration.java b/zeppelin-interpreter/src/main/java/sparklin/zeppelin/InterpreterConfiguration.java index 62245c5..5bfb94b 100644 --- a/zeppelin-interpreter/src/main/java/sparklin/zeppelin/InterpreterConfiguration.java +++ b/zeppelin-interpreter/src/main/java/sparklin/zeppelin/InterpreterConfiguration.java @@ -1,61 +1,13 @@ package sparklin.zeppelin; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import sparklin.kshell.configuration.Configuration; import sparklin.kshell.configuration.PropertyBasedConfiguration; -import sparklin.kshell.console.Completer; -import sparklin.kshell.console.ConsoleReader; import java.util.Collections; import java.util.Properties; class InterpreterConfiguration extends PropertyBasedConfiguration { - InterpreterConfiguration(@NotNull Properties props) { super(props, Collections.emptyList()); } - - @NotNull - @Override - public ConsoleReader getConsoleReader() { - return CONSOLE; - } - - private static final ConsoleReader CONSOLE = new DummyConsoleReader(); - - static class DummyConsoleReader implements ConsoleReader { - public void init(@NotNull Configuration config) { - - } - - public void addCompleter(@NotNull Completer completer) { - - } - - public void setPrompt(@NotNull String prompt) { - - } - - public void dropHistory(int n) { - - } - - public void addHistoryItem(@NotNull String item) { - - } - - @Nullable - public String readLine() { - return null; - } - - public void println(@NotNull String s) { - System.out.println(s); - } - - public void cleanUp() { - - } - } }