summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOmari Stephens <xsdg@android.com>2011-11-09 16:20:51 -0800
committerOmari Stephens <xsdg@android.com>2011-11-09 17:02:51 -0800
commit3aa4d1f0c4f24e2d2788ec9886de8a5ca3cd806b (patch)
tree4aeab040b46c1d854ce3afef3c3410153a9b792b
parentda0155bcd8fac5fc2f3cb2b5451b3c56948aa3ac (diff)
downloadjline-3aa4d1f0c4f24e2d2788ec9886de8a5ca3cd806b.tar.gz
Initial import
Downloaded jline-1.0.zip from and unpacked it: http://sourceforge.net/projects/jline/files/jline/1.0/ Bug: 3457774 Change-Id: Ic7e456baeff00fa2be059d3762bb2dbf822c7c56
-rw-r--r--LICENSE.txt33
-rw-r--r--jline-1.0.jarbin0 -> 91183 bytes
-rw-r--r--src/pom.xml230
-rw-r--r--src/src/assembly/assembly.xml55
-rw-r--r--src/src/main/java/jline/ANSIBuffer.java405
-rw-r--r--src/src/main/java/jline/ArgumentCompletor.java439
-rw-r--r--src/src/main/java/jline/CandidateCycleCompletionHandler.java28
-rw-r--r--src/src/main/java/jline/CandidateListCompletionHandler.java189
-rw-r--r--src/src/main/java/jline/ClassNameCompletor.java148
-rw-r--r--src/src/main/java/jline/CompletionHandler.java20
-rw-r--r--src/src/main/java/jline/Completor.java32
-rw-r--r--src/src/main/java/jline/ConsoleOperations.java282
-rw-r--r--src/src/main/java/jline/ConsoleReader.java1823
-rw-r--r--src/src/main/java/jline/ConsoleReaderInputStream.java108
-rw-r--r--src/src/main/java/jline/ConsoleRunner.java90
-rw-r--r--src/src/main/java/jline/CursorBuffer.java104
-rw-r--r--src/src/main/java/jline/FileNameCompletor.java133
-rw-r--r--src/src/main/java/jline/History.java299
-rw-r--r--src/src/main/java/jline/MultiCompletor.java80
-rw-r--r--src/src/main/java/jline/NullCompletor.java27
-rw-r--r--src/src/main/java/jline/SimpleCompletor.java194
-rw-r--r--src/src/main/java/jline/Terminal.java184
-rw-r--r--src/src/main/java/jline/UnixTerminal.java433
-rw-r--r--src/src/main/java/jline/UnsupportedTerminal.java98
-rw-r--r--src/src/main/java/jline/WindowsTerminal.java520
-rw-r--r--src/src/main/java/jline/package.html9
-rw-r--r--src/src/main/native/Makefile8
-rw-r--r--src/src/main/native/jline_WindowsTerminal.c57
-rw-r--r--src/src/main/native/jline_WindowsTerminal.h68
-rw-r--r--src/src/main/resources/jline/CandidateListCompletionHandler.properties5
-rw-r--r--src/src/main/resources/jline/jline32.dllbin0 -> 16069 bytes
-rw-r--r--src/src/main/resources/jline/jline64.dllbin0 -> 48128 bytes
-rw-r--r--src/src/main/resources/jline/keybindings-mac.properties62
-rw-r--r--src/src/main/resources/jline/keybindings.properties68
-rw-r--r--src/src/main/resources/jline/windowsbindings.properties68
-rw-r--r--src/src/site/apt/building.apt39
-rw-r--r--src/src/site/apt/downloads.apt39
-rw-r--r--src/src/site/docbook/index.xml492
-rw-r--r--src/src/site/fml/faq.fml26
-rwxr-xr-xsrc/src/site/resources/css/site.css311
-rwxr-xr-xsrc/src/site/resources/images/collapsed.pngbin0 -> 222 bytes
-rwxr-xr-xsrc/src/site/resources/images/dotted.pngbin0 -> 190 bytes
-rwxr-xr-xsrc/src/site/resources/images/expanded.pngbin0 -> 198 bytes
-rwxr-xr-xsrc/src/site/resources/images/external.pngbin0 -> 223 bytes
-rw-r--r--src/src/site/resources/images/ico_file_pdf.pngbin0 -> 280 bytes
-rw-r--r--src/src/site/resources/images/logo.jpgbin0 -> 4121 bytes
-rwxr-xr-xsrc/src/site/resources/images/newwindow.pngbin0 -> 224 bytes
-rw-r--r--src/src/site/site.xml40
-rw-r--r--src/src/test/java/jline/ConsoleReaderTest.java162
-rw-r--r--src/src/test/java/jline/JLineTestCase.java140
-rw-r--r--src/src/test/java/jline/TestCompletion.java71
-rw-r--r--src/src/test/java/jline/TestEditLine.java160
-rw-r--r--src/src/test/java/jline/TestHistory.java77
-rw-r--r--src/src/test/java/jline/example/Example.java97
-rw-r--r--src/src/test/java/jline/example/PasswordReader.java32
-rw-r--r--src/src/test/resources/jline/example/english.gzbin0 -> 130975 bytes
56 files changed, 7985 insertions, 0 deletions
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..1cdc44c
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,33 @@
+Copyright (c) 2002-2006, Marc Prud'hommeaux <mwp1@cornell.edu>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the following
+conditions are met:
+
+Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with
+the distribution.
+
+Neither the name of JLine nor the names of its contributors
+may be used to endorse or promote products derived from this
+software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/jline-1.0.jar b/jline-1.0.jar
new file mode 100644
index 0000000..6c949b2
--- /dev/null
+++ b/jline-1.0.jar
Binary files differ
diff --git a/src/pom.xml b/src/pom.xml
new file mode 100644
index 0000000..11ebd31
--- /dev/null
+++ b/src/pom.xml
@@ -0,0 +1,230 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+To build, you need to have Maven 2 installed.
+
+To compile, run:
+
+ mvn compile
+
+To run tests, run:
+
+ mvn test
+
+To run one particular test, e.g. TestSomeTest, run:
+
+ mvn test -Dtest=TestSomeTest
+
+To build the jars, run:
+
+ mvn package
+
+To create and upload a release, run:
+
+ mvn deploy
+
+To build the site and upload it, run:
+
+ mvn site:deploy
+
+To perform a complete release, run:
+
+ mvn clean compile package site assembly:assembly deploy site:deploy
+
+To actually upload the artifact to sourceforge, it must be manually ftp'd:
+
+ lftp ftp://upload.sourceforge.net/incoming/ -e "put `ls target/jline-*.zip`"
+
+To make a bundle and request that ibilio upload it, do:
+
+ mvn source:jar javadoc:jar repository:bundle-create
+
+ scp target/jline-*-bundle.jar shell.sourceforge.net:/home/groups/j/jl/jline/htdocs
+
+ Make a request like at http://jira.codehaus.org/browse/MAVENUPLOAD-1003
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
+ http://maven.apache.org/maven-v4_0_0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>jline</groupId>
+ <artifactId>jline</artifactId>
+ <packaging>jar</packaging>
+ <name>JLine</name>
+ <version>1.0</version>
+ <description>JLine is a java library for reading and editing user input in console applications. It features tab-completion, command history, password masking, customizable keybindings, and pass-through handlers to use to chain to other console applications.</description>
+ <url>http://jline.sourceforge.net</url>
+ <issueManagement>
+ <system>sourceforge</system>
+ <url>http://sourceforge.net/tracker/?group_id=64033&amp;atid=506056</url>
+ </issueManagement>
+ <inceptionYear>2002</inceptionYear>
+ <mailingLists>
+ <mailingList>
+ <name>JLine users</name>
+ <subscribe>https://lists.sourceforge.net/lists/listinfo/jline-users</subscribe>
+ <post>jline-users@lists.sourceforge.net</post>
+ <archive>http://sourceforge.net/mailarchive/forum.php?forum=jline-users</archive>
+ </mailingList>
+ </mailingLists>
+
+ <developers>
+ <developer>
+ <id>mprudhom</id>
+ <name>Marc Prud'hommeaux</name>
+ <email>mwp1@cornell.edu</email>
+ </developer>
+ <developer>
+ <id>headius</id>
+ <name>Charles Oliver Nutter</name>
+ <email>headius@headius.com</email>
+ </developer>
+ </developers>
+ <licenses>
+ <license>
+ <name>BSD</name>
+ <url>LICENSE.txt</url>
+ </license>
+ </licenses>
+ <scm>
+ <connection>scm:git://github.com/jline/jline.git</connection>
+ <developerConnection>scm:git://github.com/jline/jline.git</developerConnection>
+ <url>https://github.com/jline/jline</url>
+ </scm>
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>3.8.1</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <useFile>false</useFile>
+ <trimStackTrace>false</trimStackTrace>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>1.3</source>
+ <target>1.3</target>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-site-plugin</artifactId>
+ <configuration>
+ <stagingDirectory>../site-staging</stagingDirectory>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <configuration>
+ <descriptors>
+ <descriptor>src/assembly/assembly.xml</descriptor>
+ </descriptors>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>2.0.0</version>
+ <executions>
+ <execution>
+ <id>bundle-manifest</id>
+ <phase>process-classes</phase>
+ <goals>
+ <goal>manifest</goal>
+ </goals>
+ <configuration>
+ <instructions>
+ <Import-Package>!jline*,javax.swing;resolution:=optional,*</Import-Package>
+ <DynamicImport-Package>*</DynamicImport-Package>
+ </instructions>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <artifactId>maven-jar-plugin</artifactId>
+ <configuration>
+ <archive>
+ <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+ </archive>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <reporting>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>jxr-maven-plugin</artifactId>
+ <configuration>
+ <aggregate>true</aggregate>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <configuration>
+ <aggregate>true</aggregate>
+ <linksource>true</linksource>
+ <links>
+ <link>http://java.sun.com/j2se/1.5.0/docs/api</link>
+ </links>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-pmd-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-project-info-reports-plugin</artifactId>
+ <reportSets>
+ <reportSet>
+ <reports>
+ <report>project-team</report>
+ <report>mailing-list</report>
+ <report>issue-tracking</report>
+ <report>license</report>
+ <report>scm</report>
+ </reports>
+ </reportSet>
+ </reportSets>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>surefire-report-maven-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </reporting>
+ <distributionManagement>
+ <repository>
+ <id>jline</id>
+ <url>scp://shell.sourceforge.net/home/groups/j/jl/jline/htdocs/m2repo</url>
+ </repository>
+ <snapshotRepository>
+ <id>jline</id>
+ <url>scp://shell.sourceforge.net/home/groups/j/jl/jline/htdocs/m2snapshot</url>
+ </snapshotRepository>
+ <site>
+ <id>jline</id>
+ <name>jline</name>
+ <url>scpexe://shell.sourceforge.net/home/groups/j/jl/jline/htdocs/</url>
+ </site>
+ </distributionManagement>
+</project>
diff --git a/src/src/assembly/assembly.xml b/src/src/assembly/assembly.xml
new file mode 100644
index 0000000..216c697
--- /dev/null
+++ b/src/src/assembly/assembly.xml
@@ -0,0 +1,55 @@
+<assembly>
+ <id></id>
+ <formats>
+ <format>zip</format>
+ </formats>
+ <includeBaseDirectory>true</includeBaseDirectory>
+ <fileSets>
+ <fileSet>
+ <includes>
+ <include>README*</include>
+ <include>LICENSE*</include>
+ <include>NOTICE*</include>
+ </includes>
+ </fileSet>
+ <fileSet>
+ <directory>target</directory>
+ <outputDirectory></outputDirectory>
+ <includes>
+ <include>*.jar</include>
+ </includes>
+ </fileSet>
+ <fileSet>
+ <directory>licenses</directory>
+ <outputDirectory>/lib</outputDirectory>
+ <includes>
+ <include>*</include>
+ </includes>
+ </fileSet>
+ <fileSet>
+ <directory>src/test/java/jline/example</directory>
+ <outputDirectory>/examples/jline/example</outputDirectory>
+ </fileSet>
+ <fileSet>
+ <directory>src/test/resources/jline/example</directory>
+ <outputDirectory>/examples/jline/example</outputDirectory>
+ </fileSet>
+ <fileSet>
+ <directory>target/site/apidocs</directory>
+ <outputDirectory>/apidocs</outputDirectory>
+ </fileSet>
+
+ <!-- also include sources -->
+ <fileSet>
+ <directory>src</directory>
+ <outputDirectory>/src/src</outputDirectory>
+ </fileSet>
+ <fileSet>
+ <directory></directory>
+ <outputDirectory>/src</outputDirectory>
+ <includes>
+ <include>pom.xml</include>
+ </includes>
+ </fileSet>
+ </fileSets>
+</assembly>
diff --git a/src/src/main/java/jline/ANSIBuffer.java b/src/src/main/java/jline/ANSIBuffer.java
new file mode 100644
index 0000000..c2e3318
--- /dev/null
+++ b/src/src/main/java/jline/ANSIBuffer.java
@@ -0,0 +1,405 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.io.*;
+
+/**
+ * A buffer that can contain ANSI text.
+ *
+ * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public class ANSIBuffer {
+ private boolean ansiEnabled = true;
+ private final StringBuffer ansiBuffer = new StringBuffer();
+ private final StringBuffer plainBuffer = new StringBuffer();
+
+ public ANSIBuffer() {
+ }
+
+ public ANSIBuffer(final String str) {
+ append(str);
+ }
+
+ public void setAnsiEnabled(final boolean ansi) {
+ this.ansiEnabled = ansi;
+ }
+
+ public boolean getAnsiEnabled() {
+ return this.ansiEnabled;
+ }
+
+ public String getAnsiBuffer() {
+ return ansiBuffer.toString();
+ }
+
+ public String getPlainBuffer() {
+ return plainBuffer.toString();
+ }
+
+ public String toString(final boolean ansi) {
+ return ansi ? getAnsiBuffer() : getPlainBuffer();
+ }
+
+ public String toString() {
+ return toString(ansiEnabled);
+ }
+
+ public ANSIBuffer append(final String str) {
+ ansiBuffer.append(str);
+ plainBuffer.append(str);
+
+ return this;
+ }
+
+ public ANSIBuffer attrib(final String str, final int code) {
+ ansiBuffer.append(ANSICodes.attrib(code)).append(str)
+ .append(ANSICodes.attrib(ANSICodes.OFF));
+ plainBuffer.append(str);
+
+ return this;
+ }
+
+ public ANSIBuffer red(final String str) {
+ return attrib(str, ANSICodes.FG_RED);
+ }
+
+ public ANSIBuffer blue(final String str) {
+ return attrib(str, ANSICodes.FG_BLUE);
+ }
+
+ public ANSIBuffer green(final String str) {
+ return attrib(str, ANSICodes.FG_GREEN);
+ }
+
+ public ANSIBuffer black(final String str) {
+ return attrib(str, ANSICodes.FG_BLACK);
+ }
+
+ public ANSIBuffer yellow(final String str) {
+ return attrib(str, ANSICodes.FG_YELLOW);
+ }
+
+ public ANSIBuffer magenta(final String str) {
+ return attrib(str, ANSICodes.FG_MAGENTA);
+ }
+
+ public ANSIBuffer cyan(final String str) {
+ return attrib(str, ANSICodes.FG_CYAN);
+ }
+
+ public ANSIBuffer bold(final String str) {
+ return attrib(str, ANSICodes.BOLD);
+ }
+
+ public ANSIBuffer underscore(final String str) {
+ return attrib(str, ANSICodes.UNDERSCORE);
+ }
+
+ public ANSIBuffer blink(final String str) {
+ return attrib(str, ANSICodes.BLINK);
+ }
+
+ public ANSIBuffer reverse(final String str) {
+ return attrib(str, ANSICodes.REVERSE);
+ }
+
+ public static class ANSICodes {
+ static final int OFF = 0;
+ static final int BOLD = 1;
+ static final int UNDERSCORE = 4;
+ static final int BLINK = 5;
+ static final int REVERSE = 7;
+ static final int CONCEALED = 8;
+ static final int FG_BLACK = 30;
+ static final int FG_RED = 31;
+ static final int FG_GREEN = 32;
+ static final int FG_YELLOW = 33;
+ static final int FG_BLUE = 34;
+ static final int FG_MAGENTA = 35;
+ static final int FG_CYAN = 36;
+ static final int FG_WHITE = 37;
+ static final char ESC = 27;
+
+ /**
+ * Constructor is private since this is a utility class.
+ */
+ private ANSICodes() {
+ }
+
+ /**
+ * Sets the screen mode. The mode will be one of the following values:
+ * <pre>
+ * mode description
+ * ----------------------------------------
+ * 0 40 x 148 x 25 monochrome (text)
+ * 1 40 x 148 x 25 color (text)
+ * 2 80 x 148 x 25 monochrome (text)
+ * 3 80 x 148 x 25 color (text)
+ * 4 320 x 148 x 200 4-color (graphics)
+ * 5 320 x 148 x 200 monochrome (graphics)
+ * 6 640 x 148 x 200 monochrome (graphics)
+ * 7 Enables line wrapping
+ * 13 320 x 148 x 200 color (graphics)
+ * 14 640 x 148 x 200 color (16-color graphics)
+ * 15 640 x 148 x 350 monochrome (2-color graphics)
+ * 16 640 x 148 x 350 color (16-color graphics)
+ * 17 640 x 148 x 480 monochrome (2-color graphics)
+ * 18 640 x 148 x 480 color (16-color graphics)
+ * 19 320 x 148 x 200 color (256-color graphics)
+ * </pre>
+ */
+ public static String setmode(final int mode) {
+ return ESC + "[=" + mode + "h";
+ }
+
+ /**
+ * Same as setmode () except for mode = 7, which disables line
+ * wrapping (useful for writing the right-most column without
+ * scrolling to the next line).
+ */
+ public static String resetmode(final int mode) {
+ return ESC + "[=" + mode + "l";
+ }
+
+ /**
+ * Clears the screen and moves the cursor to the home postition.
+ */
+ public static String clrscr() {
+ return ESC + "[2J";
+ }
+
+ /**
+ * Removes all characters from the current cursor position until
+ * the end of the line.
+ */
+ public static String clreol() {
+ return ESC + "[K";
+ }
+
+ /**
+ * Moves the cursor n positions to the left. If n is greater or
+ * equal to the current cursor column, the cursor is moved to the
+ * first column.
+ */
+ public static String left(final int n) {
+ return ESC + "[" + n + "D";
+ }
+
+ /**
+ * Moves the cursor n positions to the right. If n plus the current
+ * cursor column is greater than the rightmost column, the cursor
+ * is moved to the rightmost column.
+ */
+ public static String right(final int n) {
+ return ESC + "[" + n + "C";
+ }
+
+ /**
+ * Moves the cursor n rows up without changing the current column.
+ * If n is greater than or equal to the current row, the cursor is
+ * placed in the first row.
+ */
+ public static String up(final int n) {
+ return ESC + "[" + n + "A";
+ }
+
+ /**
+ * Moves the cursor n rows down. If n plus the current row is greater
+ * than the bottom row, the cursor is moved to the bottom row.
+ */
+ public static String down(final int n) {
+ return ESC + "[" + n + "B";
+ }
+
+ /*
+ * Moves the cursor to the given row and column. (1,1) represents
+ * the upper left corner. The lower right corner of a usual DOS
+ * screen is (25, 80).
+ */
+ public static String gotoxy(final int row, final int column) {
+ return ESC + "[" + row + ";" + column + "H";
+ }
+
+ /**
+ * Saves the current cursor position.
+ */
+ public static String save() {
+ return ESC + "[s";
+ }
+
+ /**
+ * Restores the saved cursor position.
+ */
+ public static String restore() {
+ return ESC + "[u";
+ }
+
+ /**
+ * Sets the character attribute. It will be
+ * one of the following character attributes:
+ *
+ * <pre>
+ * Text attributes
+ * 0 All attributes off
+ * 1 Bold on
+ * 4 Underscore (on monochrome display adapter only)
+ * 5 Blink on
+ * 7 Reverse video on
+ * 8 Concealed on
+ *
+ * Foreground colors
+ * 30 Black
+ * 31 Red
+ * 32 Green
+ * 33 Yellow
+ * 34 Blue
+ * 35 Magenta
+ * 36 Cyan
+ * 37 White
+ *
+ * Background colors
+ * 40 Black
+ * 41 Red
+ * 42 Green
+ * 43 Yellow
+ * 44 Blue
+ * 45 Magenta
+ * 46 Cyan
+ * 47 White
+ * </pre>
+ *
+ * The attributes remain in effect until the next attribute command
+ * is sent.
+ */
+ public static String attrib(final int attr) {
+ return ESC + "[" + attr + "m";
+ }
+
+ /**
+ * Sets the key with the given code to the given value. code must be
+ * derived from the following table, value must
+ * be any semicolon-separated
+ * combination of String (enclosed in double quotes) and numeric values.
+ * For example, to set F1 to the String "Hello F1", followed by a CRLF
+ * sequence, one can use: ANSI.setkey ("0;59", "\"Hello F1\";13;10").
+ * Heres's the table of key values:
+ * <pre>
+ * Key Code SHIFT+code CTRL+code ALT+code
+ * ---------------------------------------------------------------
+ * F1 0;59 0;84 0;94 0;104
+ * F2 0;60 0;85 0;95 0;105
+ * F3 0;61 0;86 0;96 0;106
+ * F4 0;62 0;87 0;97 0;107
+ * F5 0;63 0;88 0;98 0;108
+ * F6 0;64 0;89 0;99 0;109
+ * F7 0;65 0;90 0;100 0;110
+ * F8 0;66 0;91 0;101 0;111
+ * F9 0;67 0;92 0;102 0;112
+ * F10 0;68 0;93 0;103 0;113
+ * F11 0;133 0;135 0;137 0;139
+ * F12 0;134 0;136 0;138 0;140
+ * HOME (num keypad) 0;71 55 0;119 --
+ * UP ARROW (num keypad) 0;72 56 (0;141) --
+ * PAGE UP (num keypad) 0;73 57 0;132 --
+ * LEFT ARROW (num keypad) 0;75 52 0;115 --
+ * RIGHT ARROW (num keypad) 0;77 54 0;116 --
+ * END (num keypad) 0;79 49 0;117 --
+ * DOWN ARROW (num keypad) 0;80 50 (0;145) --
+ * PAGE DOWN (num keypad) 0;81 51 0;118 --
+ * INSERT (num keypad) 0;82 48 (0;146) --
+ * DELETE (num keypad) 0;83 46 (0;147) --
+ * HOME (224;71) (224;71) (224;119) (224;151)
+ * UP ARROW (224;72) (224;72) (224;141) (224;152)
+ * PAGE UP (224;73) (224;73) (224;132) (224;153)
+ * LEFT ARROW (224;75) (224;75) (224;115) (224;155)
+ * RIGHT ARROW (224;77) (224;77) (224;116) (224;157)
+ * END (224;79) (224;79) (224;117) (224;159)
+ * DOWN ARROW (224;80) (224;80) (224;145) (224;154)
+ * PAGE DOWN (224;81) (224;81) (224;118) (224;161)
+ * INSERT (224;82) (224;82) (224;146) (224;162)
+ * DELETE (224;83) (224;83) (224;147) (224;163)
+ * PRINT SCREEN -- -- 0;114 --
+ * PAUSE/BREAK -- -- 0;0 --
+ * BACKSPACE 8 8 127 (0)
+ * ENTER 13 -- 10 (0
+ * TAB 9 0;15 (0;148) (0;165)
+ * NULL 0;3 -- -- --
+ * A 97 65 1 0;30
+ * B 98 66 2 0;48
+ * C 99 66 3 0;46
+ * D 100 68 4 0;32
+ * E 101 69 5 0;18
+ * F 102 70 6 0;33
+ * G 103 71 7 0;34
+ * H 104 72 8 0;35
+ * I 105 73 9 0;23
+ * J 106 74 10 0;36
+ * K 107 75 11 0;37
+ * L 108 76 12 0;38
+ * M 109 77 13 0;50
+ * N 110 78 14 0;49
+ * O 111 79 15 0;24
+ * P 112 80 16 0;25
+ * Q 113 81 17 0;16
+ * R 114 82 18 0;19
+ * S 115 83 19 0;31
+ * T 116 84 20 0;20
+ * U 117 85 21 0;22
+ * V 118 86 22 0;47
+ * W 119 87 23 0;17
+ * X 120 88 24 0;45
+ * Y 121 89 25 0;21
+ * Z 122 90 26 0;44
+ * 1 49 33 -- 0;120
+ * 2 50 64 0 0;121
+ * 3 51 35 -- 0;122
+ * 4 52 36 -- 0;123
+ * 5 53 37 -- 0;124
+ * 6 54 94 30 0;125
+ * 7 55 38 -- 0;126
+ * 8 56 42 -- 0;126
+ * 9 57 40 -- 0;127
+ * 0 48 41 -- 0;129
+ * - 45 95 31 0;130
+ * = 61 43 --- 0;131
+ * [ 91 123 27 0;26
+ * ] 93 125 29 0;27
+ * 92 124 28 0;43
+ * ; 59 58 -- 0;39
+ * ' 39 34 -- 0;40
+ * , 44 60 -- 0;51
+ * . 46 62 -- 0;52
+ * / 47 63 -- 0;53
+ * ` 96 126 -- (0;41)
+ * ENTER (keypad) 13 -- 10 (0;166)
+ * / (keypad) 47 47 (0;142) (0;74)
+ * * (keypad) 42 (0;144) (0;78) --
+ * - (keypad) 45 45 (0;149) (0;164)
+ * + (keypad) 43 43 (0;150) (0;55)
+ * 5 (keypad) (0;76) 53 (0;143) --
+ */
+ public static String setkey(final String code, final String value) {
+ return ESC + "[" + code + ";" + value + "p";
+ }
+ }
+
+ public static void main(final String[] args) throws Exception {
+ // sequence, one can use: ANSI.setkey ("0;59", "\"Hello F1\";13;10").
+ BufferedReader reader =
+ new BufferedReader(new InputStreamReader(System.in));
+ System.out.print(ANSICodes.setkey("97", "97;98;99;13")
+ + ANSICodes.attrib(ANSICodes.OFF));
+ System.out.flush();
+
+ String line;
+
+ while ((line = reader.readLine()) != null) {
+ System.out.println("GOT: " + line);
+ }
+ }
+}
diff --git a/src/src/main/java/jline/ArgumentCompletor.java b/src/src/main/java/jline/ArgumentCompletor.java
new file mode 100644
index 0000000..5493ad8
--- /dev/null
+++ b/src/src/main/java/jline/ArgumentCompletor.java
@@ -0,0 +1,439 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.util.*;
+
+/**
+ * A {@link Completor} implementation that invokes a child completor
+ * using the appropriate <i>separator</i> argument. This
+ * can be used instead of the individual completors having to
+ * know about argument parsing semantics.
+ * <p>
+ * <strong>Example 1</strong>: Any argument of the command line can
+ * use file completion.
+ * <p>
+ * <pre>
+ * consoleReader.addCompletor (new ArgumentCompletor (
+ * new {@link FileNameCompletor} ()))
+ * </pre>
+ * <p>
+ * <strong>Example 2</strong>: The first argument of the command line
+ * can be completed with any of "foo", "bar", or "baz", and remaining
+ * arguments can be completed with a file name.
+ * <p>
+ * <pre>
+ * consoleReader.addCompletor (new ArgumentCompletor (
+ * new {@link SimpleCompletor} (new String [] { "foo", "bar", "baz"})));
+ * consoleReader.addCompletor (new ArgumentCompletor (
+ * new {@link FileNameCompletor} ()));
+ * </pre>
+ *
+ * <p>
+ * When the argument index is past the last embedded completors, the last
+ * completors is always used. To disable this behavior, have the last
+ * completor be a {@link NullCompletor}. For example:
+ * </p>
+ *
+ * <pre>
+ * consoleReader.addCompletor (new ArgumentCompletor (
+ * new {@link SimpleCompletor} (new String [] { "foo", "bar", "baz"}),
+ * new {@link SimpleCompletor} (new String [] { "xxx", "yyy", "xxx"}),
+ * new {@link NullCompletor}
+ * ));
+ * </pre>
+ * <p>
+ * TODO: handle argument quoting and escape characters
+ * </p>
+ *
+ * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public class ArgumentCompletor implements Completor {
+ final Completor[] completors;
+ final ArgumentDelimiter delim;
+ boolean strict = true;
+
+ /**
+ * Constuctor: create a new completor with the default
+ * argument separator of " ".
+ *
+ * @param completor the embedded completor
+ */
+ public ArgumentCompletor(final Completor completor) {
+ this(new Completor[] {
+ completor
+ });
+ }
+
+ /**
+ * Constuctor: create a new completor with the default
+ * argument separator of " ".
+ *
+ * @param completors the List of completors to use
+ */
+ public ArgumentCompletor(final List completors) {
+ this((Completor[]) completors.toArray(new Completor[completors.size()]));
+ }
+
+ /**
+ * Constuctor: create a new completor with the default
+ * argument separator of " ".
+ *
+ * @param completors the embedded argument completors
+ */
+ public ArgumentCompletor(final Completor[] completors) {
+ this(completors, new WhitespaceArgumentDelimiter());
+ }
+
+ /**
+ * Constuctor: create a new completor with the specified
+ * argument delimiter.
+ *
+ * @param completor the embedded completor
+ * @param delim the delimiter for parsing arguments
+ */
+ public ArgumentCompletor(final Completor completor,
+ final ArgumentDelimiter delim) {
+ this(new Completor[] {
+ completor
+ }, delim);
+ }
+
+ /**
+ * Constuctor: create a new completor with the specified
+ * argument delimiter.
+ *
+ * @param completors the embedded completors
+ * @param delim the delimiter for parsing arguments
+ */
+ public ArgumentCompletor(final Completor[] completors,
+ final ArgumentDelimiter delim) {
+ this.completors = completors;
+ this.delim = delim;
+ }
+
+ /**
+ * 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 succees
+ * if all the completions from arguments 0-(N-1) also succeed.
+ */
+ public boolean getStrict() {
+ return this.strict;
+ }
+
+ public int complete(final String buffer, final int cursor,
+ final List candidates) {
+ ArgumentList list = delim.delimit(buffer, cursor);
+ int argpos = list.getArgumentPosition();
+ int argIndex = list.getCursorArgumentIndex();
+
+ if (argIndex < 0) {
+ return -1;
+ }
+
+ final Completor comp;
+
+ // if we are beyond the end of the completors, just use the last one
+ if (argIndex >= completors.length) {
+ comp = completors[completors.length - 1];
+ } else {
+ comp = completors[argIndex];
+ }
+
+ // ensure that all the previous completors are successful before
+ // allowing this completor to pass (only if strict is true).
+ for (int i = 0; getStrict() && (i < argIndex); i++) {
+ Completor sub =
+ completors[(i >= completors.length) ? (completors.length - 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.size() == 0) {
+ return -1;
+ }
+ }
+
+ int ret = comp.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++) {
+ String val = candidates.get(i).toString();
+
+ while ((val.length() > 0)
+ && delim.isDelimiter(val, val.length() - 1)) {
+ val = val.substring(0, val.length() - 1);
+ }
+
+ candidates.set(i, val);
+ }
+ }
+
+ ConsoleReader.debug("Completing " + buffer + "(pos=" + cursor + ") "
+ + "with: " + candidates + ": offset=" + pos);
+
+ return pos;
+ }
+
+ /**
+ * The {@link ArgumentCompletor.ArgumentDelimiter} allows custom
+ * breaking up of a {@link String} into individual arguments in
+ * order to dispatch the arguments to the nested {@link Completor}.
+ *
+ * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+ public static interface ArgumentDelimiter {
+ /**
+ * Break the specified buffer into individual tokens
+ * that can be completed on their own.
+ *
+ * @param buffer the buffer to split
+ * @param argumentPosition the current position of the
+ * cursor in the buffer
+ * @return the tokens
+ */
+ ArgumentList delimit(String buffer, int argumentPosition);
+
+ /**
+ * 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(String buffer, int pos);
+ }
+
+ /**
+ * Abstract implementation of a delimiter that uses the
+ * {@link #isDelimiter} method to determine if a particular
+ * character should be used as a delimiter.
+ *
+ * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+ public abstract static class AbstractArgumentDelimiter
+ implements ArgumentDelimiter {
+ private char[] quoteChars = new char[] { '\'', '"' };
+ private char[] escapeChars = new char[] { '\\' };
+
+ public void setQuoteChars(final char[] quoteChars) {
+ this.quoteChars = quoteChars;
+ }
+
+ public char[] getQuoteChars() {
+ return this.quoteChars;
+ }
+
+ public void setEscapeChars(final char[] escapeChars) {
+ this.escapeChars = escapeChars;
+ }
+
+ public char[] getEscapeChars() {
+ return this.escapeChars;
+ }
+
+ public ArgumentList delimit(final String buffer, final int cursor) {
+ List args = new LinkedList();
+ StringBuffer arg = new StringBuffer();
+ int argpos = -1;
+ int bindex = -1;
+
+ for (int i = 0; (buffer != null) && (i <= buffer.length()); i++) {
+ // once we reach the cursor, set the
+ // position of the selected index
+ if (i == cursor) {
+ bindex = args.size();
+ // the position in the current argument is just the
+ // length of the current argument
+ argpos = arg.length();
+ }
+
+ if ((i == buffer.length()) || isDelimiter(buffer, i)) {
+ if (arg.length() > 0) {
+ args.add(arg.toString());
+ arg.setLength(0); // reset the arg
+ }
+ } else {
+ arg.append(buffer.charAt(i));
+ }
+ }
+
+ return new ArgumentList((String[]) 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 String buffer, final int pos) {
+ if (isQuoted(buffer, pos)) {
+ return false;
+ }
+
+ if (isEscaped(buffer, pos)) {
+ return false;
+ }
+
+ return isDelimiterChar(buffer, pos);
+ }
+
+ public boolean isQuoted(final String buffer, final int pos) {
+ return false;
+ }
+
+ public boolean isEscaped(final String buffer, final int pos) {
+ if (pos <= 0) {
+ return false;
+ }
+
+ for (int i = 0; (escapeChars != null) && (i < escapeChars.length);
+ i++) {
+ if (buffer.charAt(pos) == escapeChars[i]) {
+ return !isEscaped(buffer, pos - 1); // escape escape
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns true if the character at the specified position
+ * if a delimiter. This method will only be called if the
+ * character is not enclosed in any of the
+ * {@link #getQuoteChars}, and is not escaped by ant of the
+ * {@link #getEscapeChars}. To perform escaping manually,
+ * override {@link #isDelimiter} instead.
+ */
+ public abstract boolean isDelimiterChar(String buffer, int pos);
+ }
+
+ /**
+ * {@link ArgumentCompletor.ArgumentDelimiter}
+ * implementation that counts all
+ * whitespace (as reported by {@link Character#isWhitespace})
+ * as being a delimiter.
+ *
+ * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+ public static class WhitespaceArgumentDelimiter
+ extends AbstractArgumentDelimiter {
+ /**
+ * The character is a delimiter if it is whitespace, and the
+ * preceeding character is not an escape character.
+ */
+ public boolean isDelimiterChar(String buffer, int pos) {
+ return Character.isWhitespace(buffer.charAt(pos));
+ }
+ }
+
+ /**
+ * The result of a delimited buffer.
+ *
+ * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+ public static class ArgumentList {
+ private String[] arguments;
+ private int cursorArgumentIndex;
+ private int argumentPosition;
+ private int bufferPosition;
+
+ /**
+ * @param arguments the array of tokens
+ * @param cursorArgumentIndex the token index of the cursor
+ * @param argumentPosition the position of the cursor in the
+ * current token
+ * @param bufferPosition the position of the cursor in
+ * the whole buffer
+ */
+ public ArgumentList(String[] arguments, int cursorArgumentIndex,
+ int argumentPosition, int bufferPosition) {
+ this.arguments = arguments;
+ this.cursorArgumentIndex = cursorArgumentIndex;
+ this.argumentPosition = argumentPosition;
+ this.bufferPosition = bufferPosition;
+ }
+
+ public void setCursorArgumentIndex(int cursorArgumentIndex) {
+ this.cursorArgumentIndex = cursorArgumentIndex;
+ }
+
+ public int getCursorArgumentIndex() {
+ return this.cursorArgumentIndex;
+ }
+
+ public String getCursorArgument() {
+ if ((cursorArgumentIndex < 0)
+ || (cursorArgumentIndex >= arguments.length)) {
+ return null;
+ }
+
+ return arguments[cursorArgumentIndex];
+ }
+
+ public void setArgumentPosition(int argumentPosition) {
+ this.argumentPosition = argumentPosition;
+ }
+
+ public int getArgumentPosition() {
+ return this.argumentPosition;
+ }
+
+ public void setArguments(String[] arguments) {
+ this.arguments = arguments;
+ }
+
+ public String[] getArguments() {
+ return this.arguments;
+ }
+
+ public void setBufferPosition(int bufferPosition) {
+ this.bufferPosition = bufferPosition;
+ }
+
+ public int getBufferPosition() {
+ return this.bufferPosition;
+ }
+ }
+}
diff --git a/src/src/main/java/jline/CandidateCycleCompletionHandler.java b/src/src/main/java/jline/CandidateCycleCompletionHandler.java
new file mode 100644
index 0000000..a0bf208
--- /dev/null
+++ b/src/src/main/java/jline/CandidateCycleCompletionHandler.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * <p>
+ * A {@link CompletionHandler} that deals with multiple distinct completions
+ * by cycling through each one every time tab is pressed. This
+ * mimics the behavior of the
+ * <a href="http://packages.qa.debian.org/e/editline.html">editline</a>
+ * library.
+ * </p>
+ * <p><strong>This class is currently a stub; it does nothing</strong></p>
+ * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public class CandidateCycleCompletionHandler implements CompletionHandler {
+ public boolean complete(final ConsoleReader reader, final List candidates,
+ final int position) throws IOException {
+ throw new IllegalStateException("CandidateCycleCompletionHandler unimplemented");
+ }
+}
diff --git a/src/src/main/java/jline/CandidateListCompletionHandler.java b/src/src/main/java/jline/CandidateListCompletionHandler.java
new file mode 100644
index 0000000..d9d43c2
--- /dev/null
+++ b/src/src/main/java/jline/CandidateListCompletionHandler.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.io.*;
+import java.text.MessageFormat;
+import java.util.*;
+
+/**
+ * <p>
+ * A {@link CompletionHandler} that deals with multiple distinct completions
+ * by outputting the complete list of possibilities to the console. This
+ * mimics the behavior of the
+ * <a href="http://www.gnu.org/directory/readline.html">readline</a>
+ * library.
+ * </p>
+ *
+ * <strong>TODO:</strong>
+ * <ul>
+ * <li>handle quotes and escaped quotes</li>
+ * <li>enable automatic escaping of whitespace</li>
+ * </ul>
+ *
+ * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public class CandidateListCompletionHandler implements CompletionHandler {
+ private static ResourceBundle loc = ResourceBundle.
+ getBundle(CandidateListCompletionHandler.class.getName());
+
+ private boolean eagerNewlines = true;
+
+ public void setAlwaysIncludeNewline(boolean eagerNewlines) {
+ this.eagerNewlines = eagerNewlines;
+ }
+
+ public boolean complete(final ConsoleReader reader, final List candidates,
+ final int pos) throws IOException {
+ CursorBuffer buf = reader.getCursorBuffer();
+
+ // if there is only one completion, then fill in the buffer
+ if (candidates.size() == 1) {
+ String value = candidates.get(0).toString();
+
+ // 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);
+ String bufString = buf.toString();
+ setBuffer(reader, value, pos);
+ }
+
+ if (eagerNewlines)
+ reader.printNewline();
+ printCandidates(reader, candidates, eagerNewlines);
+
+ // redraw the current console buffer
+ reader.drawLine();
+
+ return true;
+ }
+
+ public static void setBuffer(ConsoleReader reader, String value, int offset)
+ throws IOException {
+ while ((reader.getCursorBuffer().cursor > offset)
+ && reader.backspace()) {
+ ;
+ }
+
+ reader.putString(value);
+ reader.setCursorPosition(offset + value.length());
+ }
+
+ /**
+ * Print out the candidates. If the size of the candidates
+ * is greated than the {@link getAutoprintThreshhold},
+ * they prompt with aq warning.
+ *
+ * @param candidates the list of candidates to print
+ */
+ public static final void printCandidates(ConsoleReader reader,
+ Collection candidates, boolean eagerNewlines)
+ throws IOException {
+ Set distinct = new HashSet(candidates);
+
+ if (distinct.size() > reader.getAutoprintThreshhold()) {
+ if (!eagerNewlines)
+ reader.printNewline();
+ reader.printString(MessageFormat.format
+ (loc.getString("display-candidates"), new Object[] {
+ new Integer(candidates .size())
+ }) + " ");
+
+ reader.flushConsole();
+
+ int c;
+
+ String noOpt = loc.getString("display-candidates-no");
+ String yesOpt = loc.getString("display-candidates-yes");
+
+ while ((c = reader.readCharacter(new char[] {
+ yesOpt.charAt(0), noOpt.charAt(0) })) != -1) {
+ if (noOpt.startsWith
+ (new String(new char[] { (char) c }))) {
+ reader.printNewline();
+ return;
+ } else if (yesOpt.startsWith
+ (new String(new char[] { (char) c }))) {
+ 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 (Iterator i = candidates.iterator(); i.hasNext();) {
+ Object next = i.next();
+
+ if (!(copy.contains(next))) {
+ copy.add(next);
+ }
+ }
+
+ candidates = copy;
+ }
+
+ reader.printNewline();
+ reader.printColumns(candidates);
+ }
+
+ /**
+ * Returns a root that matches all the {@link String} elements
+ * of the specified {@link List}, or null if there are
+ * no commalities. For example, if the list contains
+ * <i>foobar</i>, <i>foobaz</i>, <i>foobuz</i>, the
+ * method will return <i>foob</i>.
+ */
+ private final String getUnambiguousCompletions(final List candidates) {
+ if ((candidates == null) || (candidates.size() == 0)) {
+ return null;
+ }
+
+ // convert to an array for speed
+ String[] strings =
+ (String[]) candidates.toArray(new String[candidates.size()]);
+
+ String first = strings[0];
+ StringBuffer candidate = new StringBuffer();
+
+ for (int i = 0; i < first.length(); i++) {
+ if (startsWith(first.substring(0, i + 1), strings)) {
+ candidate.append(first.charAt(i));
+ } else {
+ break;
+ }
+ }
+
+ return candidate.toString();
+ }
+
+ /**
+ * @return true is all the elements of <i>candidates</i>
+ * start with <i>starts</i>
+ */
+ private final boolean startsWith(final String starts,
+ final String[] candidates) {
+ for (int i = 0; i < candidates.length; i++) {
+ if (!candidates[i].startsWith(starts)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/src/src/main/java/jline/ClassNameCompletor.java b/src/src/main/java/jline/ClassNameCompletor.java
new file mode 100644
index 0000000..3ef5802
--- /dev/null
+++ b/src/src/main/java/jline/ClassNameCompletor.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+/**
+ * A Completor implementation that completes java class names. By default,
+ * it scans the java class path to locate all the classes.
+ *
+ * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public class ClassNameCompletor extends SimpleCompletor {
+
+ /**
+ * Complete candidates using all the classes available in the
+ * java <em>CLASSPATH</em>.
+ */
+ public ClassNameCompletor() throws IOException {
+ this(null);
+ }
+
+ public ClassNameCompletor(final SimpleCompletorFilter filter)
+ throws IOException {
+ super(getClassNames(), filter);
+ setDelimiter(".");
+ }
+
+ public static String[] getClassNames() throws IOException {
+ Set urls = new HashSet();
+
+ for (ClassLoader loader = ClassNameCompletor.class
+ .getClassLoader(); loader != null;
+ loader = loader.getParent()) {
+ if (!(loader instanceof URLClassLoader)) {
+ continue;
+ }
+
+ urls.addAll(Arrays.asList(((URLClassLoader) loader).getURLs()));
+ }
+
+ // Now add the URL that holds java.lang.String. This is because
+ // some JVMs do not report the core classes jar in the list of
+ // class loaders.
+ Class[] systemClasses = new Class[] {
+ String.class, javax.swing.JFrame.class
+ };
+
+ for (int i = 0; i < systemClasses.length; i++) {
+ URL classURL = systemClasses[i].getResource("/"
+ + systemClasses[i].getName() .replace('.', '/') + ".class");
+
+ if (classURL != null) {
+ URLConnection uc = (URLConnection) classURL.openConnection();
+
+ if (uc instanceof JarURLConnection) {
+ urls.add(((JarURLConnection) uc).getJarFileURL());
+ }
+ }
+ }
+
+ Set classes = new HashSet();
+
+ for (Iterator i = urls.iterator(); i.hasNext();) {
+ URL url = (URL) i.next();
+ File file = new File(url.getFile());
+
+ if (file.isDirectory()) {
+ Set files = getClassFiles(file.getAbsolutePath(),
+ new HashSet(), file, new int[] { 200 });
+ classes.addAll(files);
+
+ continue;
+ }
+
+ if ((file == null) || !file.isFile()) // TODO: handle directories
+ {
+ continue;
+ }
+ if (!file.toString().endsWith (".jar"))
+ continue;
+
+ JarFile jf = new JarFile(file);
+
+ for (Enumeration e = jf.entries(); e.hasMoreElements();) {
+ JarEntry entry = (JarEntry) e.nextElement();
+
+ if (entry == null) {
+ continue;
+ }
+
+ String name = entry.getName();
+
+ if (!name.endsWith(".class")) // only use class files
+ {
+ continue;
+ }
+
+ classes.add(name);
+ }
+ }
+
+ // now filter classes by changing "/" to "." and trimming the
+ // trailing ".class"
+ Set classNames = new TreeSet();
+
+ for (Iterator i = classes.iterator(); i.hasNext();) {
+ String name = (String) i.next();
+ classNames.add(name.replace('/', '.').
+ substring(0, name.length() - 6));
+ }
+
+ return (String[]) classNames.toArray(new String[classNames.size()]);
+ }
+
+ private static Set getClassFiles(String root, Set holder, File directory,
+ int[] maxDirectories) {
+ // we have passed the maximum number of directories to scan
+ if (maxDirectories[0]-- < 0) {
+ return holder;
+ }
+
+ File[] files = directory.listFiles();
+
+ for (int i = 0; (files != null) && (i < files.length); i++) {
+ String name = files[i].getAbsolutePath();
+
+ if (!(name.startsWith(root))) {
+ continue;
+ } else if (files[i].isDirectory()) {
+ getClassFiles(root, holder, files[i], maxDirectories);
+ } else if (files[i].getName().endsWith(".class")) {
+ holder.add(files[i].getAbsolutePath().
+ substring(root.length() + 1));
+ }
+ }
+
+ return holder;
+ }
+}
diff --git a/src/src/main/java/jline/CompletionHandler.java b/src/src/main/java/jline/CompletionHandler.java
new file mode 100644
index 0000000..6f524da
--- /dev/null
+++ b/src/src/main/java/jline/CompletionHandler.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * Handler for dealing with candidates for tab-completion.
+ *
+ * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public interface CompletionHandler {
+ boolean complete(ConsoleReader reader, List candidates, int position)
+ throws IOException;
+}
diff --git a/src/src/main/java/jline/Completor.java b/src/src/main/java/jline/Completor.java
new file mode 100644
index 0000000..ad29cf9
--- /dev/null
+++ b/src/src/main/java/jline/Completor.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.util.*;
+
+/**
+ * A Completor is the mechanism by which tab-completion candidates
+ * will be resolved.
+ *
+ * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public interface Completor {
+ /**
+ * Populates <i>candidates</i> with a list of possible
+ * completions for the <i>buffer</i>. The <i>candidates</i>
+ * list will not be sorted before being displayed to the
+ * user: thus, the complete method should sort the
+ * {@link List} before returning.
+ *
+ *
+ * @param buffer the buffer
+ * @param candidates the {@link List} of candidates to populate
+ * @return the index of the <i>buffer</i> for which
+ * the completion will be relative
+ */
+ int complete(String buffer, int cursor, List candidates);
+}
diff --git a/src/src/main/java/jline/ConsoleOperations.java b/src/src/main/java/jline/ConsoleOperations.java
new file mode 100644
index 0000000..16aa0e7
--- /dev/null
+++ b/src/src/main/java/jline/ConsoleOperations.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.awt.event.KeyEvent;
+
+/**
+ * Symbolic constants for Console operations and virtual key bindings.
+ * @see KeyEvent
+ *
+ * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public interface ConsoleOperations {
+ final String CR = System.getProperty("line.separator");
+ final char BACKSPACE = '\b';
+ final char RESET_LINE = '\r';
+ final char KEYBOARD_BELL = '\07';
+ final char CTRL_A = 1;
+ final char CTRL_B = 2;
+ final char CTRL_C = 3;
+ final char CTRL_D = 4;
+ final char CTRL_E = 5;
+ final char CTRL_F = 6;
+ final char CTRL_G = 7;
+ final static char CTRL_K = 11;
+ final static char CTRL_L = 12;
+ final char CTRL_N = 14;
+ final char CTRL_P = 16;
+ final static char CTRL_OB = 27;
+ final static char DELETE = 127;
+ final static char CTRL_QM = 127;
+
+
+ /**
+ * Logical constants for key operations.
+ */
+
+ /**
+ * Unknown operation.
+ */
+ final short UNKNOWN = -99;
+
+ /**
+ * Operation that moves to the beginning of the buffer.
+ */
+ final short MOVE_TO_BEG = -1;
+
+ /**
+ * Operation that moves to the end of the buffer.
+ */
+ final short MOVE_TO_END = -3;
+
+ /**
+ * Operation that moved to the previous character in the buffer.
+ */
+ final short PREV_CHAR = -4;
+
+ /**
+ * Operation that issues a newline.
+ */
+ final short NEWLINE = -6;
+
+ /**
+ * Operation that deletes the buffer from the current character to the end.
+ */
+ final short KILL_LINE = -7;
+
+ /**
+ * Operation that clears the screen.
+ */
+ final short CLEAR_SCREEN = -8;
+
+ /**
+ * Operation that sets the buffer to the next history item.
+ */
+ final short NEXT_HISTORY = -9;
+
+ /**
+ * Operation that sets the buffer to the previous history item.
+ */
+ final short PREV_HISTORY = -11;
+
+ /**
+ * Operation that redisplays the current buffer.
+ */
+ final short REDISPLAY = -13;
+
+ /**
+ * Operation that deletes the buffer from the cursor to the beginning.
+ */
+ final short KILL_LINE_PREV = -15;
+
+ /**
+ * Operation that deletes the previous word in the buffer.
+ */
+ final short DELETE_PREV_WORD = -16;
+
+ /**
+ * Operation that moves to the next character in the buffer.
+ */
+ final short NEXT_CHAR = -19;
+
+ /**
+ * Operation that moves to the previous character in the buffer.
+ */
+ final short REPEAT_PREV_CHAR = -20;
+
+ /**
+ * Operation that searches backwards in the command history.
+ */
+ final short SEARCH_PREV = -21;
+
+ /**
+ * Operation that repeats the character.
+ */
+ final short REPEAT_NEXT_CHAR = -24;
+
+ /**
+ * Operation that searches forward in the command history.
+ */
+ final short SEARCH_NEXT = -25;
+
+ /**
+ * Operation that moved to the previous whitespace.
+ */
+ final short PREV_SPACE_WORD = -27;
+
+ /**
+ * Operation that moved to the end of the current word.
+ */
+ final short TO_END_WORD = -29;
+
+ /**
+ * Operation that
+ */
+ final short REPEAT_SEARCH_PREV = -34;
+
+ /**
+ * Operation that
+ */
+ final short PASTE_PREV = -36;
+
+ /**
+ * Operation that
+ */
+ final short REPLACE_MODE = -37;
+
+ /**
+ * Operation that
+ */
+ final short SUBSTITUTE_LINE = -38;
+
+ /**
+ * Operation that
+ */
+ final short TO_PREV_CHAR = -39;
+
+ /**
+ * Operation that
+ */
+ final short NEXT_SPACE_WORD = -40;
+
+ /**
+ * Operation that
+ */
+ final short DELETE_PREV_CHAR = -41;
+
+ /**
+ * Operation that
+ */
+ final short ADD = -42;
+
+ /**
+ * Operation that
+ */
+ final short PREV_WORD = -43;
+
+ /**
+ * Operation that
+ */
+ final short CHANGE_META = -44;
+
+ /**
+ * Operation that
+ */
+ final short DELETE_META = -45;
+
+ /**
+ * Operation that
+ */
+ final short END_WORD = -46;
+
+ /**
+ * Operation that toggles insert/overtype
+ */
+ final short INSERT = -48;
+
+ /**
+ * Operation that
+ */
+ final short REPEAT_SEARCH_NEXT = -49;
+
+ /**
+ * Operation that
+ */
+ final short PASTE_NEXT = -50;
+
+ /**
+ * Operation that
+ */
+ final short REPLACE_CHAR = -51;
+
+ /**
+ * Operation that
+ */
+ final short SUBSTITUTE_CHAR = -52;
+
+ /**
+ * Operation that
+ */
+ final short TO_NEXT_CHAR = -53;
+
+ /**
+ * Operation that undoes the previous operation.
+ */
+ final short UNDO = -54;
+
+ /**
+ * Operation that moved to the next word.
+ */
+ final short NEXT_WORD = -55;
+
+ /**
+ * Operation that deletes the previous character.
+ */
+ final short DELETE_NEXT_CHAR = -56;
+
+ /**
+ * Operation that toggles between uppercase and lowercase.
+ */
+ final short CHANGE_CASE = -57;
+
+ /**
+ * Operation that performs completion operation on the current word.
+ */
+ final short COMPLETE = -58;
+
+ /**
+ * Operation that exits the command prompt.
+ */
+ final short EXIT = -59;
+
+ /**
+ * Operation that pastes the contents of the clipboard into the line
+ */
+ final short PASTE = -60;
+
+ /**
+ * Operation that moves the current History to the beginning.
+ */
+ final static short START_OF_HISTORY = -61;
+
+ /**
+ * Operation that moves the current History to the end.
+ */
+ final static short END_OF_HISTORY = -62;
+
+ /**
+ * Operation that clears whatever text is on the current line.
+ */
+ final static short CLEAR_LINE = -63;
+
+ /**
+ * Operation that aborts the current command (like searching)
+ */
+ final static short ABORT = -64;
+
+}
diff --git a/src/src/main/java/jline/ConsoleReader.java b/src/src/main/java/jline/ConsoleReader.java
new file mode 100644
index 0000000..18339d4
--- /dev/null
+++ b/src/src/main/java/jline/ConsoleReader.java
@@ -0,0 +1,1823 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.awt.*;
+import java.awt.datatransfer.*;
+import java.awt.event.ActionListener;
+
+import java.io.*;
+import java.util.*;
+import java.util.List;
+
+/**
+ * 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 Terminal#initializeTerminal} for convenience
+ * methods for issuing platform-specific setup commands.
+ *
+ * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public class ConsoleReader implements ConsoleOperations {
+
+ final static int TAB_WIDTH = 4;
+ String prompt;
+ private boolean useHistory = true;
+ private boolean usePagination = false;
+ public static final String CR = System.getProperty("line.separator");
+ private static ResourceBundle loc = ResourceBundle.getBundle(CandidateListCompletionHandler.class.getName());
+ /**
+ * Map that contains the operation name to keymay operation mapping.
+ */
+ public static SortedMap KEYMAP_NAMES;
+
+
+ static {
+ Map names = new TreeMap();
+
+ names.put("MOVE_TO_BEG", new Short(MOVE_TO_BEG));
+ names.put("MOVE_TO_END", new Short(MOVE_TO_END));
+ names.put("PREV_CHAR", new Short(PREV_CHAR));
+ names.put("NEWLINE", new Short(NEWLINE));
+ names.put("KILL_LINE", new Short(KILL_LINE));
+ names.put("PASTE", new Short(PASTE));
+ names.put("CLEAR_SCREEN", new Short(CLEAR_SCREEN));
+ names.put("NEXT_HISTORY", new Short(NEXT_HISTORY));
+ names.put("PREV_HISTORY", new Short(PREV_HISTORY));
+ names.put("START_OF_HISTORY", new Short(START_OF_HISTORY));
+ names.put("END_OF_HISTORY", new Short(END_OF_HISTORY));
+ names.put("REDISPLAY", new Short(REDISPLAY));
+ names.put("KILL_LINE_PREV", new Short(KILL_LINE_PREV));
+ names.put("DELETE_PREV_WORD", new Short(DELETE_PREV_WORD));
+ names.put("NEXT_CHAR", new Short(NEXT_CHAR));
+ names.put("REPEAT_PREV_CHAR", new Short(REPEAT_PREV_CHAR));
+ names.put("SEARCH_PREV", new Short(SEARCH_PREV));
+ names.put("REPEAT_NEXT_CHAR", new Short(REPEAT_NEXT_CHAR));
+ names.put("SEARCH_NEXT", new Short(SEARCH_NEXT));
+ names.put("PREV_SPACE_WORD", new Short(PREV_SPACE_WORD));
+ names.put("TO_END_WORD", new Short(TO_END_WORD));
+ names.put("REPEAT_SEARCH_PREV", new Short(REPEAT_SEARCH_PREV));
+ names.put("PASTE_PREV", new Short(PASTE_PREV));
+ names.put("REPLACE_MODE", new Short(REPLACE_MODE));
+ names.put("SUBSTITUTE_LINE", new Short(SUBSTITUTE_LINE));
+ names.put("TO_PREV_CHAR", new Short(TO_PREV_CHAR));
+ names.put("NEXT_SPACE_WORD", new Short(NEXT_SPACE_WORD));
+ names.put("DELETE_PREV_CHAR", new Short(DELETE_PREV_CHAR));
+ names.put("ADD", new Short(ADD));
+ names.put("PREV_WORD", new Short(PREV_WORD));
+ names.put("CHANGE_META", new Short(CHANGE_META));
+ names.put("DELETE_META", new Short(DELETE_META));
+ names.put("END_WORD", new Short(END_WORD));
+ names.put("NEXT_CHAR", new Short(NEXT_CHAR));
+ names.put("INSERT", new Short(INSERT));
+ names.put("REPEAT_SEARCH_NEXT", new Short(REPEAT_SEARCH_NEXT));
+ names.put("PASTE_NEXT", new Short(PASTE_NEXT));
+ names.put("REPLACE_CHAR", new Short(REPLACE_CHAR));
+ names.put("SUBSTITUTE_CHAR", new Short(SUBSTITUTE_CHAR));
+ names.put("TO_NEXT_CHAR", new Short(TO_NEXT_CHAR));
+ names.put("UNDO", new Short(UNDO));
+ names.put("NEXT_WORD", new Short(NEXT_WORD));
+ names.put("DELETE_NEXT_CHAR", new Short(DELETE_NEXT_CHAR));
+ names.put("CHANGE_CASE", new Short(CHANGE_CASE));
+ names.put("COMPLETE", new Short(COMPLETE));
+ names.put("EXIT", new Short(EXIT));
+ names.put("CLEAR_LINE", new Short(CLEAR_LINE));
+ names.put("ABORT", new Short(ABORT));
+
+ KEYMAP_NAMES = new TreeMap(Collections.unmodifiableMap(names));
+ }
+ /**
+ * The map for logical operations.
+ */
+ private final short[] keybindings;
+ /**
+ * If true, issue an audible keyboard bell when appropriate.
+ */
+ private boolean bellEnabled = true;
+ /**
+ * The current character mask.
+ */
+ private Character mask = null;
+ /**
+ * The null mask.
+ */
+ private static final Character NULL_MASK = new Character((char) 0);
+ /**
+ * The number of tab-completion candidates above which a warning will be
+ * prompted before showing all the candidates.
+ */
+ private int autoprintThreshhold = Integer.getInteger(
+ "jline.completion.threshold", 100).intValue(); // same default as
+
+ // bash
+ /**
+ * The Terminal to use.
+ */
+ private final Terminal terminal;
+ private CompletionHandler completionHandler = new CandidateListCompletionHandler();
+ InputStream in;
+ final Writer out;
+ final CursorBuffer buf = new CursorBuffer();
+ static PrintWriter debugger;
+ History history = new History();
+ final List completors = new LinkedList();
+ private Character echoCharacter = null;
+ private Map triggeredActions = new HashMap();
+
+ private StringBuffer searchTerm = null;
+ private String previousSearchTerm = "";
+ private int searchIndex = -1;
+
+ /**
+ * Adding a triggered Action allows to give another course of action
+ * if a character passed the preprocessing.
+ *
+ * Say you want to close the application if the user enter q.
+ * addTriggerAction('q', new ActionListener(){ System.exit(0); });
+ * would do the trick.
+ *
+ * @param c
+ * @param listener
+ */
+ public void addTriggeredAction(char c, ActionListener listener) {
+ triggeredActions.put(new Character(c), listener);
+ }
+
+ /**
+ * Create a new reader using {@link FileDescriptor#in} for input and
+ * {@link System#out} for output. {@link FileDescriptor#in} is used because
+ * it has a better chance of being unbuffered.
+ */
+ public ConsoleReader() throws IOException {
+ this(new FileInputStream(FileDescriptor.in),
+ new PrintWriter(
+ new OutputStreamWriter(System.out,
+ System.getProperty("jline.WindowsTerminal.output.encoding", System.getProperty("file.encoding")))));
+ }
+
+ /**
+ * Create a new reader using the specified {@link InputStream} for input and
+ * the specific writer for output, using the default keybindings resource.
+ */
+ public ConsoleReader(final InputStream in, final Writer out)
+ throws IOException {
+ this(in, out, null);
+ }
+
+ public ConsoleReader(final InputStream in, final Writer out,
+ final InputStream bindings) throws IOException {
+ this(in, out, bindings, Terminal.getTerminal());
+ }
+
+ /**
+ * Create a new reader.
+ *
+ * @param in
+ * the input
+ * @param out
+ * the output
+ * @param bindings
+ * the key bindings to use
+ * @param term
+ * the terminal to use
+ */
+ public ConsoleReader(InputStream in, Writer out, InputStream bindings,
+ Terminal term) throws IOException {
+ this.terminal = term;
+ setInput(in);
+ this.out = out;
+ if (bindings == null) {
+ try {
+ String bindingFile = System.getProperty("jline.keybindings",
+ new File(System.getProperty("user.home"),
+ ".jlinebindings.properties").getAbsolutePath());
+
+ if (new File(bindingFile).isFile()) {
+ bindings = new FileInputStream(new File(bindingFile));
+ }
+ } catch (Exception e) {
+ // swallow exceptions with option debugging
+ if (debugger != null) {
+ e.printStackTrace(debugger);
+ }
+ }
+ }
+
+ if (bindings == null) {
+ bindings = terminal.getDefaultBindings();
+ }
+
+ this.keybindings = new short[Character.MAX_VALUE * 2];
+
+ Arrays.fill(this.keybindings, UNKNOWN);
+
+ /**
+ * Loads the key bindings. Bindings file is in the format:
+ *
+ * keycode: operation name
+ */
+ if (bindings != null) {
+ Properties p = new Properties();
+ p.load(bindings);
+ bindings.close();
+
+ for (Iterator i = p.keySet().iterator(); i.hasNext();) {
+ String val = (String) i.next();
+
+ try {
+ Short code = new Short(val);
+ String op = (String) p.getProperty(val);
+
+ Short opval = (Short) KEYMAP_NAMES.get(op);
+
+ if (opval != null) {
+ keybindings[code.shortValue()] = opval.shortValue();
+ }
+ } catch (NumberFormatException nfe) {
+ consumeException(nfe);
+ }
+ }
+
+ // hardwired arrow key bindings
+ // keybindings[VK_UP] = PREV_HISTORY;
+ // keybindings[VK_DOWN] = NEXT_HISTORY;
+ // keybindings[VK_LEFT] = PREV_CHAR;
+ // keybindings[VK_RIGHT] = NEXT_CHAR;
+ }
+ }
+
+ public Terminal getTerminal() {
+ return this.terminal;
+ }
+
+ /**
+ * Set the stream for debugging. Development use only.
+ */
+ public void setDebug(final PrintWriter debugger) {
+ ConsoleReader.debugger = debugger;
+ }
+
+ /**
+ * Set the stream to be used for console input.
+ */
+ public void setInput(final InputStream in) {
+ this.in = in;
+ }
+
+ /**
+ * Returns the stream used for console input.
+ */
+ public InputStream getInput() {
+ return this.in;
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * @param bellEnabled
+ * if true, enable audible keyboard bells if an alert is
+ * required.
+ */
+ public void setBellEnabled(final boolean bellEnabled) {
+ this.bellEnabled = bellEnabled;
+ }
+
+ /**
+ * @return true is audible keyboard bell is enabled.
+ */
+ public boolean getBellEnabled() {
+ return this.bellEnabled;
+ }
+
+ /**
+ * Query the terminal to find the current width;
+ *
+ * @see Terminal#getTerminalWidth
+ * @return the width of the current terminal.
+ */
+ public int getTermwidth() {
+ return getTerminal().getTerminalWidth();
+ }
+
+ /**
+ * Query the terminal to find the current width;
+ *
+ * @see Terminal#getTerminalHeight
+ *
+ * @return the height of the current terminal.
+ */
+ public int getTermheight() {
+ return getTerminal().getTerminalHeight();
+ }
+
+ /**
+ * @param autoprintThreshhold
+ * the number of candidates to print without issuing a warning.
+ */
+ public void setAutoprintThreshhold(final int autoprintThreshhold) {
+ this.autoprintThreshhold = autoprintThreshhold;
+ }
+
+ /**
+ * @return the number of candidates to print without issing a warning.
+ */
+ public int getAutoprintThreshhold() {
+ return this.autoprintThreshhold;
+ }
+
+ int getKeyForAction(short logicalAction) {
+ for (int i = 0; i < keybindings.length; i++) {
+ if (keybindings[i] == logicalAction) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * Clear the echoed characters for the specified character code.
+ */
+ int clearEcho(int c) throws IOException {
+ // if the terminal is not echoing, then just return...
+ if (!terminal.getEcho()) {
+ return 0;
+ }
+
+ // otherwise, clear
+ int num = countEchoCharacters((char) c);
+ back(num);
+ drawBuffer(num);
+
+ return num;
+ }
+
+ int countEchoCharacters(char c) {
+ // tabs as special: we need to determine the number of spaces
+ // to cancel based on what out current cursor position is
+ if (c == 9) {
+ int tabstop = 8; // will this ever be different?
+ int position = getCursorPosition();
+
+ return tabstop - (position % tabstop);
+ }
+
+ return getPrintableCharacters(c).length();
+ }
+
+ /**
+ * Return the number of characters that will be printed when the specified
+ * character is echoed to the screen. Adapted from cat by Torbjorn Granlund,
+ * as repeated in stty by David MacKenzie.
+ */
+ StringBuffer getPrintableCharacters(char ch) {
+ StringBuffer sbuff = new StringBuffer();
+
+ if (ch >= 32) {
+ if (ch < 127) {
+ sbuff.append(ch);
+ } else if (ch == 127) {
+ sbuff.append('^');
+ sbuff.append('?');
+ } else {
+ sbuff.append('M');
+ sbuff.append('-');
+
+ if (ch >= (128 + 32)) {
+ if (ch < (128 + 127)) {
+ sbuff.append((char) (ch - 128));
+ } else {
+ sbuff.append('^');
+ sbuff.append('?');
+ }
+ } else {
+ sbuff.append('^');
+ sbuff.append((char) (ch - 128 + 64));
+ }
+ }
+ } else {
+ sbuff.append('^');
+ sbuff.append((char) (ch + 64));
+ }
+
+ return sbuff;
+ }
+
+ int getCursorPosition() {
+ // FIXME: does not handle anything but a line with a prompt
+ // absolute position
+ return getStrippedAnsiLength(prompt) + buf.cursor;
+ }
+
+ /**
+ * Strips ANSI escape sequences starting with CSI and ending with char in range 64-126
+ * @param ansiString String possibly containing ANSI codes, may be null
+ * @return length after stripping ANSI codes
+ */
+ int getStrippedAnsiLength(String ansiString) {
+ if (ansiString == null) return 0;
+ boolean inAnsi = false;
+ int strippedLength = 0;
+ char[] chars = ansiString.toCharArray();
+ for (int i = 0; i < chars.length; i++) {
+ char c = chars[i];
+ if (!inAnsi && c == 27 && i < chars.length - 1 && chars[i+1] == '[') {
+ i++; // skip '['
+ inAnsi = true;
+ } else if (inAnsi) {
+ if (64 <= c && c <= 126) {
+ inAnsi = false;
+ }
+ } else {
+ strippedLength++;
+ }
+ }
+ return strippedLength;
+ }
+
+ public String readLine(final String prompt) throws IOException {
+ return readLine(prompt, null);
+ }
+
+ /**
+ * The default prompt that will be issued.
+ */
+ public void setDefaultPrompt(String prompt) {
+ this.prompt = prompt;
+ }
+
+ /**
+ * The default prompt that will be issued.
+ */
+ public String getDefaultPrompt() {
+ return prompt;
+ }
+
+ /**
+ * Read a line from the <i>in</i> {@link InputStream}, and return the line
+ * (without any trailing newlines).
+ *
+ * @param prompt
+ * the prompt to issue to the console, may be null.
+ * @return a line that is read from the terminal, or null if there was null
+ * input (e.g., <i>CTRL-D</i> was pressed).
+ */
+ public String readLine(final String prompt, final Character mask)
+ throws IOException {
+ this.mask = mask;
+ if (prompt != null) {
+ this.prompt = prompt;
+ }
+
+ try {
+ terminal.beforeReadLine(this, this.prompt, mask);
+
+ if ((this.prompt != null) && (this.prompt.length() > 0)) {
+ out.write(this.prompt);
+ out.flush();
+ }
+
+ // if the terminal is unsupported, just use plain-java reading
+ if (!terminal.isSupported()) {
+ return readLine(in);
+ }
+
+ final int NORMAL = 1;
+ final int SEARCH = 2;
+ int state = NORMAL;
+
+ boolean success = true;
+
+ while (true) {
+ // Read next key and look up the command binding.
+ int[] next = readBinding();
+
+ if (next == null) {
+ return null;
+ }
+
+ int c = next[0];
+ int code = next[1];
+
+ if (c == -1) {
+ return null;
+ }
+
+ // Search mode.
+ //
+ // Note that we have to do this first, because if there is a command
+ // not linked to a search command, we leave the search mode and fall
+ // through to the normal state.
+ if (state == SEARCH) {
+ switch (code) {
+ // This doesn't work right now, it seems CTRL-G is not passed
+ // down correctly. :(
+ case ABORT:
+ state = NORMAL;
+ break;
+
+ case SEARCH_PREV:
+ if (searchTerm.length() == 0) {
+ searchTerm.append(previousSearchTerm);
+ }
+
+ if (searchIndex == -1) {
+ searchIndex = history.searchBackwards(searchTerm.toString());
+ } else {
+ searchIndex = history.searchBackwards(searchTerm.toString(), searchIndex);
+ }
+ break;
+
+ case DELETE_PREV_CHAR:
+ if (searchTerm.length() > 0) {
+ searchTerm.deleteCharAt(searchTerm.length() - 1);
+ searchIndex = history.searchBackwards(searchTerm.toString());
+ }
+ break;
+
+ case UNKNOWN:
+ searchTerm.appendCodePoint(c);
+ searchIndex = history.searchBackwards(searchTerm.toString());
+ break;
+
+ default:
+ // Set buffer and cursor position to the found string.
+ if (searchIndex != -1) {
+ history.setCurrentIndex(searchIndex);
+ setBuffer(history.current());
+ buf.cursor = history.current().indexOf(searchTerm.toString());
+ }
+ state = NORMAL;
+ break;
+ }
+
+ // if we're still in search mode, print the search status
+ if (state == SEARCH) {
+ if (searchTerm.length() == 0) {
+ printSearchStatus("", "");
+ } else {
+ if (searchIndex == -1) {
+ beep();
+ } else {
+ printSearchStatus(searchTerm.toString(), history.getHistory(searchIndex));
+ }
+ }
+ }
+ // otherwise, restore the line
+ else {
+ restoreLine();
+ }
+ }
+
+ if (state == NORMAL) {
+ switch (code) {
+ case EXIT: // ctrl-d
+
+ if (buf.buffer.length() == 0) {
+ return null;
+ }
+ else {
+ success = deleteCurrentCharacter();
+ }
+ break;
+
+ case COMPLETE: // tab
+ success = complete();
+ break;
+
+ case MOVE_TO_BEG:
+ success = setCursorPosition(0);
+ break;
+
+ case KILL_LINE: // CTRL-K
+ success = killLine();
+ break;
+
+ case CLEAR_SCREEN: // CTRL-L
+ success = clearScreen();
+ break;
+
+ case KILL_LINE_PREV: // CTRL-U
+ success = resetLine();
+ break;
+
+ case NEWLINE: // enter
+ moveToEnd();
+ printNewline(); // output newline
+ return finishBuffer();
+
+ case DELETE_PREV_CHAR: // backspace
+ success = backspace();
+ break;
+
+ case DELETE_NEXT_CHAR: // delete
+ success = deleteCurrentCharacter();
+ break;
+
+ case MOVE_TO_END:
+ success = moveToEnd();
+ break;
+
+ case PREV_CHAR:
+ success = moveCursor(-1) != 0;
+ break;
+
+ case NEXT_CHAR:
+ success = moveCursor(1) != 0;
+ break;
+
+ case NEXT_HISTORY:
+ success = moveHistory(true);
+ break;
+
+ case PREV_HISTORY:
+ success = moveHistory(false);
+ break;
+
+ case ABORT:
+ case REDISPLAY:
+ break;
+
+ case PASTE:
+ success = paste();
+ break;
+
+ case DELETE_PREV_WORD:
+ success = deletePreviousWord();
+ break;
+
+ case PREV_WORD:
+ success = previousWord();
+ break;
+
+ case NEXT_WORD:
+ success = nextWord();
+ break;
+
+ case START_OF_HISTORY:
+ success = history.moveToFirstEntry();
+ if (success) {
+ setBuffer(history.current());
+ }
+ break;
+
+ case END_OF_HISTORY:
+ success = history.moveToLastEntry();
+ if (success) {
+ setBuffer(history.current());
+ }
+ break;
+
+ case CLEAR_LINE:
+ moveInternal(-(buf.buffer.length()));
+ killLine();
+ break;
+
+ case INSERT:
+ buf.setOvertyping(!buf.isOvertyping());
+ break;
+
+ case SEARCH_PREV: // CTRL-R
+ if (searchTerm != null) {
+ previousSearchTerm = searchTerm.toString();
+ }
+ searchTerm = new StringBuffer(buf.buffer);
+ state = SEARCH;
+ if (searchTerm.length() > 0) {
+ searchIndex = history.searchBackwards(searchTerm.toString());
+ if (searchIndex == -1) {
+ beep();
+ }
+ printSearchStatus(searchTerm.toString(),
+ searchIndex > -1 ? history.getHistory(searchIndex) : "");
+ } else {
+ searchIndex = -1;
+ printSearchStatus("", "");
+ }
+ break;
+
+ case UNKNOWN:
+ default:
+ if (c != 0) { // ignore null chars
+ ActionListener action = (ActionListener) triggeredActions.get(new Character((char) c));
+ if (action != null) {
+ action.actionPerformed(null);
+ } else {
+ putChar(c, true);
+ }
+ } else {
+ success = false;
+ }
+ }
+
+ if (!(success)) {
+ beep();
+ }
+
+ flushConsole();
+ }
+ }
+ } finally {
+ terminal.afterReadLine(this, this.prompt, mask);
+ }
+ }
+
+ private String readLine(InputStream in) throws IOException {
+ StringBuffer buf = new StringBuffer();
+
+ while (true) {
+ int i = in.read();
+
+ if ((i == -1) || (i == '\n') || (i == '\r')) {
+ return buf.toString();
+ }
+
+ buf.append((char) i);
+ }
+
+ // return new BufferedReader (new InputStreamReader (in)).readLine ();
+ }
+
+ /**
+ * Reads the console input and returns an array of the form [raw, key
+ * binding].
+ */
+ private int[] readBinding() throws IOException {
+ int c = readVirtualKey();
+
+ if (c == -1) {
+ return null;
+ }
+
+ // extract the appropriate key binding
+ short code = keybindings[c];
+
+ if (debugger != null) {
+ // debug(" translated: " + (int) c + ": " + code);
+ }
+
+ return new int[]{c, code};
+ }
+
+ /**
+ * Move up or down the history tree.
+ */
+ private final 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;
+ }
+
+ /**
+ * Paste the contents of the clipboard into the console buffer
+ *
+ * @return true if clipboard contents pasted
+ */
+ public boolean paste() throws IOException {
+ Clipboard clipboard;
+ try { // May throw ugly exception on system without X
+ clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
+ } catch (Exception e) {
+ return false;
+ }
+
+ if (clipboard == null) {
+ return false;
+ }
+
+ Transferable transferable = clipboard.getContents(null);
+
+ if (transferable == null) {
+ return false;
+ }
+
+ try {
+ Object content = transferable.getTransferData(DataFlavor.plainTextFlavor);
+
+ /*
+ * This fix was suggested in bug #1060649 at
+ * http://sourceforge.net/tracker/index.php?func=detail&aid=1060649&group_id=64033&atid=506056
+ * to get around the deprecated DataFlavor.plainTextFlavor, but it
+ * raises a UnsupportedFlavorException on Mac OS X
+ */
+ if (content == null) {
+ try {
+ content = new DataFlavor().getReaderForText(transferable);
+ } catch (Exception e) {
+ }
+ }
+
+ 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 = null;
+
+ for (BufferedReader read = new BufferedReader((Reader) content); (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 ufe) {
+ if (debugger != null) {
+ debug(ufe + "");
+ }
+
+ return false;
+ }
+ }
+
+ /**
+ * Kill the buffer ahead of the current cursor position.
+ *
+ * @return true if successful
+ */
+ public boolean killLine() throws IOException {
+ int cp = buf.cursor;
+ int len = buf.buffer.length();
+
+ if (cp >= len) {
+ return false;
+ }
+
+ int num = buf.buffer.length() - cp;
+ clearAhead(num);
+
+ for (int i = 0; i < num; i++) {
+ buf.buffer.deleteCharAt(len - i - 1);
+ }
+
+ return true;
+ }
+
+ /**
+ * Clear the screen by issuing the ANSI "clear screen" code.
+ */
+ public boolean clearScreen() throws IOException {
+ if (!terminal.isANSISupported()) {
+ return false;
+ }
+
+ // send the ANSI code to clear the screen
+ printANSISequence("2J");
+
+ // then send the ANSI code to go to position 1,1
+ printANSISequence("1;1H");
+
+ redrawLine();
+
+ return true;
+ }
+
+ /**
+ * Use the completors to modify the buffer with the appropriate completions.
+ *
+ * @return true if successful
+ */
+ private final boolean complete() throws IOException {
+ // debug ("tab for (" + buf + ")");
+ if (completors.size() == 0) {
+ return false;
+ }
+
+ List candidates = new LinkedList();
+ String bufstr = buf.buffer.toString();
+ int cursor = buf.cursor;
+
+ int position = -1;
+
+ for (Iterator i = completors.iterator(); i.hasNext();) {
+ Completor comp = (Completor) i.next();
+
+ if ((position = comp.complete(bufstr, cursor, candidates)) != -1) {
+ break;
+ }
+ }
+
+ // no candidates? Fail.
+ if (candidates.size() == 0) {
+ return false;
+ }
+
+ return completionHandler.complete(this, candidates, position);
+ }
+
+ public CursorBuffer getCursorBuffer() {
+ return buf;
+ }
+
+ /**
+ * Output the specified {@link Collection} in proper columns.
+ *
+ * @param stuff
+ * the stuff to print
+ */
+ public void printColumns(final Collection stuff) throws IOException {
+ if ((stuff == null) || (stuff.size() == 0)) {
+ return;
+ }
+
+ int width = getTermwidth();
+ int maxwidth = 0;
+
+ for (Iterator i = stuff.iterator(); i.hasNext(); maxwidth = Math.max(
+ maxwidth, i.next().toString().length())) {
+ ;
+ }
+
+ StringBuffer line = new StringBuffer();
+
+ int showLines;
+
+ if (usePagination) {
+ showLines = getTermheight() - 1; // page limit
+ } else {
+ showLines = Integer.MAX_VALUE;
+ }
+
+ for (Iterator i = stuff.iterator(); i.hasNext();) {
+ String cur = (String) i.next();
+
+ if ((line.length() + maxwidth) > width) {
+ printString(line.toString().trim());
+ printNewline();
+ line.setLength(0);
+ if (--showLines == 0) { // Overflow
+ printString(loc.getString("display-more"));
+ flushConsole();
+ int c = readVirtualKey();
+ if (c == '\r' || c == '\n') {
+ showLines = 1; // one step forward
+ } else if (c != 'q') {
+ showLines = getTermheight() - 1; // page forward
+ }
+ back(loc.getString("display-more").length());
+ if (c == 'q') {
+ break; // cancel
+ }
+ }
+ }
+
+ pad(cur, maxwidth + 3, line);
+ }
+
+ if (line.length() > 0) {
+ printString(line.toString().trim());
+ printNewline();
+ line.setLength(0);
+ }
+ }
+
+ /**
+ * Append <i>toPad</i> to the specified <i>appendTo</i>, as well as (<i>toPad.length () -
+ * len</i>) spaces.
+ *
+ * @param toPad
+ * the {@link String} to pad
+ * @param len
+ * the target length
+ * @param appendTo
+ * the {@link StringBuffer} to which to append the padded
+ * {@link String}.
+ */
+ private final void pad(final String toPad, final int len,
+ final StringBuffer appendTo) {
+ appendTo.append(toPad);
+
+ for (int i = 0; i < (len - toPad.length()); i++, appendTo.append(' ')) {
+ ;
+ }
+ }
+
+ /**
+ * Add the specified {@link Completor} to the list of handlers for
+ * tab-completion.
+ *
+ * @param completor
+ * the {@link Completor} to add
+ * @return true if it was successfully added
+ */
+ public boolean addCompletor(final Completor completor) {
+ return completors.add(completor);
+ }
+
+ /**
+ * Remove the specified {@link Completor} from the list of handlers for
+ * tab-completion.
+ *
+ * @param completor
+ * the {@link Completor} to remove
+ * @return true if it was successfully removed
+ */
+ public boolean removeCompletor(final Completor completor) {
+ return completors.remove(completor);
+ }
+
+ /**
+ * Returns an unmodifiable list of all the completors.
+ */
+ public Collection getCompletors() {
+ return Collections.unmodifiableList(completors);
+ }
+
+ /**
+ * Erase the current line.
+ *
+ * @return false if we failed (e.g., the buffer was empty)
+ */
+ final boolean resetLine() throws IOException {
+ if (buf.cursor == 0) {
+ return false;
+ }
+
+ backspaceAll();
+
+ return true;
+ }
+
+ /**
+ * Move the cursor position to the specified absolute index.
+ */
+ public final boolean setCursorPosition(final int position)
+ throws IOException {
+ return moveCursor(position - buf.cursor) != 0;
+ }
+
+ /**
+ * Set the current buffer's content to the specified {@link String}. The
+ * visual console will be modified to show the current buffer.
+ *
+ * @param buffer
+ * the new contents of the buffer.
+ */
+ private final void setBuffer(final String buffer) throws IOException {
+ // don't bother modifying it if it is unchanged
+ if (buffer.equals(buf.buffer.toString())) {
+ return;
+ }
+
+ // obtain the difference between the current buffer and the new one
+ int sameIndex = 0;
+
+ for (int i = 0, l1 = buffer.length(), l2 = buf.buffer.length(); (i < l1) && (i < l2); i++) {
+ if (buffer.charAt(i) == buf.buffer.charAt(i)) {
+ sameIndex++;
+ } else {
+ break;
+ }
+ }
+
+ int diff = buf.cursor - sameIndex;
+ if (diff < 0) { // we can't backspace here so try from the end of the buffer
+ moveToEnd();
+ diff = buf.buffer.length() - sameIndex;
+ }
+
+ backspace(diff); // go back for the differences
+ killLine(); // clear to the end of the line
+ buf.buffer.setLength(sameIndex); // the new length
+ putString(buffer.substring(sameIndex)); // append the differences
+ }
+
+ /**
+ * Clear the line and redraw it.
+ */
+ public final void redrawLine() throws IOException {
+ printCharacter(RESET_LINE);
+ flushConsole();
+ drawLine();
+ }
+
+ /**
+ * Output put the prompt + the current buffer
+ */
+ public final void drawLine() throws IOException {
+ if (prompt != null) {
+ printString(prompt);
+ }
+
+ printString(buf.buffer.toString());
+
+ if (buf.length() != buf.cursor) // not at end of line
+ {
+ back(buf.length() - buf.cursor - 1); // sync
+ }
+ }
+
+ /**
+ * Output a platform-dependant newline.
+ */
+ public final void printNewline() throws IOException {
+ printString(CR);
+ flushConsole();
+ }
+
+ /**
+ * Clear the buffer and add its contents to the history.
+ *
+ * @return the former contents of the buffer.
+ */
+ final String finishBuffer() {
+ String str = buf.buffer.toString();
+
+ // we only add it to the history if the buffer is not empty
+ // and if mask is null, since having a mask typically means
+ // the string was a password. We clear the mask after this call
+ if (str.length() > 0) {
+ if (mask == null && useHistory) {
+ history.addToHistory(str);
+ } else {
+ mask = null;
+ }
+ }
+
+ history.moveToEnd();
+
+ buf.buffer.setLength(0);
+ buf.cursor = 0;
+
+ return str;
+ }
+
+ /**
+ * Write out the specified string to the buffer and the output stream.
+ */
+ public final void putString(final String str) throws IOException {
+ buf.write(str);
+ printString(str);
+ drawBuffer();
+ }
+
+ /**
+ * Output the specified string to the output stream (but not the buffer).
+ */
+ public final void printString(final String str) throws IOException {
+ printCharacters(str.toCharArray());
+ }
+
+ /**
+ * Output the specified character, both to the buffer and the output stream.
+ */
+ private final void putChar(final int c, final boolean print)
+ throws IOException {
+ buf.write((char) c);
+
+ if (print) {
+ // no masking...
+ if (mask == null) {
+ printCharacter(c);
+ } // null mask: don't print anything...
+ else if (mask.charValue() == 0) {
+ ;
+ } // otherwise print the mask...
+ else {
+ printCharacter(mask.charValue());
+ }
+
+ drawBuffer();
+ }
+ }
+
+ /**
+ * Redraw the rest of the buffer from the cursor onwards. This is necessary
+ * for inserting text into the buffer.
+ *
+ * @param clear
+ * the number of characters to clear after the end of the buffer
+ */
+ private final void drawBuffer(final int clear) throws IOException {
+ // debug ("drawBuffer: " + clear);
+ if (buf.cursor == buf.length() && clear == 0) {
+ return;
+ }
+ char[] chars = buf.buffer.substring(buf.cursor).toCharArray();
+ if (mask != null) {
+ Arrays.fill(chars, mask.charValue());
+ }
+
+ printCharacters(chars);
+ clearAhead(clear);
+ if (terminal.isANSISupported()) {
+ if (chars.length > 0) {
+ // don't ask, it seems to work
+ back(Math.max(chars.length - 1, 1));
+ }
+ } else {
+ back(chars.length);
+ }
+ flushConsole();
+ }
+
+ /**
+ * Redraw the rest of the buffer from the cursor onwards. This is necessary
+ * for inserting text into the buffer.
+ */
+ private final void drawBuffer() throws IOException {
+ drawBuffer(0);
+ }
+
+ /**
+ * Clear ahead the specified number of characters without moving the cursor.
+ */
+ private final void clearAhead(final int num) throws IOException {
+ if (num == 0) {
+ return;
+ }
+
+ if (terminal.isANSISupported()) {
+ printANSISequence("J");
+ return;
+ }
+
+ // debug ("clearAhead: " + num);
+
+ // print blank extra characters
+ printCharacters(' ', num);
+
+ // we need to flush here so a "clever" console
+ // doesn't just ignore the redundancy of a space followed by
+ // a backspace.
+ flushConsole();
+
+ // reset the visual cursor
+ back(num);
+
+ flushConsole();
+ }
+
+ /**
+ * Move the visual cursor backwards without modifying the buffer cursor.
+ */
+ private final void back(final int num) throws IOException {
+ if (num == 0) return;
+ if (terminal.isANSISupported()) {
+ int width = getTermwidth();
+ int cursor = getCursorPosition();
+ // debug("back: " + cursor + " + " + num + " on " + width);
+ int currRow = (cursor + num) / width;
+ int newRow = cursor / width;
+ int newCol = cursor % width + 1;
+ // debug(" old row: " + currRow + " new row: " + newRow);
+ if (newRow < currRow) {
+ printANSISequence((currRow - newRow) + "A");
+ }
+ printANSISequence(newCol + "G");
+ flushConsole();
+ return;
+ }
+ printCharacters(BACKSPACE, num);
+ flushConsole();
+ }
+
+ /**
+ * Issue an audible keyboard bell, if {@link #getBellEnabled} return true.
+ */
+ public final void beep() throws IOException {
+ if (!(getBellEnabled())) {
+ return;
+ }
+
+ printCharacter(KEYBOARD_BELL);
+ // need to flush so the console actually beeps
+ flushConsole();
+ }
+
+ /**
+ * Output the specified character to the output stream without manipulating
+ * the current buffer.
+ */
+ private final void printCharacter(final int c) throws IOException {
+ if (c == '\t') {
+ char cbuf[] = new char[TAB_WIDTH];
+ Arrays.fill(cbuf, ' ');
+ out.write(cbuf);
+ return;
+ }
+
+ out.write(c);
+ }
+
+ /**
+ * Output the specified characters to the output stream without manipulating
+ * the current buffer.
+ */
+ private final void printCharacters(final char[] c) throws IOException {
+ int len = 0;
+ for (int i = 0; i < c.length; i++) {
+ if (c[i] == '\t') {
+ len += TAB_WIDTH;
+ } else {
+ len++;
+ }
+ }
+
+ char cbuf[];
+ if (len == c.length) {
+ cbuf = c;
+ } else {
+ cbuf = new char[len];
+ int pos = 0;
+ for (int i = 0; i < c.length; i++) {
+ if (c[i] == '\t') {
+ Arrays.fill(cbuf, pos, pos + TAB_WIDTH, ' ');
+ pos += TAB_WIDTH;
+ } else {
+ cbuf[pos] = c[i];
+ pos++;
+ }
+ }
+ }
+
+ out.write(cbuf);
+ }
+
+ private final void printCharacters(final char c, final int num)
+ throws IOException {
+ if (num == 1) {
+ printCharacter(c);
+ } else {
+ char[] chars = new char[num];
+ Arrays.fill(chars, c);
+ printCharacters(chars);
+ }
+ }
+
+ /**
+ * 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 immedately.
+ */
+ public final void flushConsole() throws IOException {
+ out.flush();
+ }
+
+ private final int backspaceAll() throws IOException {
+ return backspace(Integer.MAX_VALUE);
+ }
+
+ /**
+ * Issue <em>num</em> backspaces.
+ *
+ * @return the number of characters backed up
+ */
+ private final int backspace(final int num) throws IOException {
+ if (buf.cursor == 0) {
+ return 0;
+ }
+
+ int count = 0;
+ int termwidth = getTermwidth();
+ int lines = getCursorPosition() / termwidth;
+ count = moveCursor(-1 * num) * -1;
+ // debug ("Deleting from " + buf.cursor + " for " + count);
+ buf.buffer.delete(buf.cursor, buf.cursor + count);
+ if (getCursorPosition() / termwidth != lines) {
+ if (terminal.isANSISupported()) {
+ // debug("doing backspace redraw: " + getCursorPosition() + " on " + termwidth + ": " + lines);
+ printANSISequence("J");
+ flushConsole();
+ }
+ }
+ drawBuffer(count);
+
+ return count;
+ }
+
+ /**
+ * Issue a backspace.
+ *
+ * @return true if successful
+ */
+ public final boolean backspace() throws IOException {
+ return backspace(1) == 1;
+ }
+
+ private final boolean moveToEnd() throws IOException {
+ return moveCursor(buf.length() - buf.cursor) > 0;
+ }
+
+ /**
+ * Delete the character at the current position and redraw the remainder of
+ * the buffer.
+ */
+ private final boolean deleteCurrentCharacter() throws IOException {
+ if (buf.length() == 0 || buf.cursor == buf.length()) {
+ return false;
+ }
+
+ buf.buffer.deleteCharAt(buf.cursor);
+ drawBuffer(1);
+ return true;
+ }
+
+ private final boolean previousWord() throws IOException {
+ while (isDelimiter(buf.current()) && (moveCursor(-1) != 0)) {
+ ;
+ }
+
+ while (!isDelimiter(buf.current()) && (moveCursor(-1) != 0)) {
+ ;
+ }
+
+ return true;
+ }
+
+ private final boolean nextWord() throws IOException {
+ while (isDelimiter(buf.current()) && (moveCursor(1) != 0)) {
+ ;
+ }
+
+ while (!isDelimiter(buf.current()) && (moveCursor(1) != 0)) {
+ ;
+ }
+
+ return true;
+ }
+
+ private final boolean deletePreviousWord() throws IOException {
+ while (isDelimiter(buf.current()) && backspace()) {
+ ;
+ }
+
+ while (!isDelimiter(buf.current()) && backspace()) {
+ ;
+ }
+
+ return true;
+ }
+
+ /**
+ * Move the cursor <i>where</i> characters.
+ *
+ * @param num
+ * if less than 0, move abs(<i>num</i>) to the left,
+ * otherwise move <i>num</i> to the right.
+ *
+ * @return the number of spaces we moved
+ */
+ public final 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;
+ }
+
+ /**
+ * debug.
+ *
+ * @param str
+ * the message to issue.
+ */
+ public static void debug(final String str) {
+ if (debugger != null) {
+ debugger.println(str);
+ debugger.flush();
+ }
+ }
+
+ /**
+ * Move the cursor <i>where</i> characters, withough checking the current
+ * buffer.
+ *
+ * @param where
+ * the number of characters to move to the right or left.
+ */
+ private final void moveInternal(final int where) throws IOException {
+ // debug ("move cursor " + where + " ("
+ // + buf.cursor + " => " + (buf.cursor + where) + ")");
+ buf.cursor += where;
+
+ if (terminal.isANSISupported()) {
+ if (where < 0) {
+ back(Math.abs(where));
+ } else {
+ int width = getTermwidth();
+ int cursor = getCursorPosition();
+ int oldLine = (cursor - where) / width;
+ int newLine = cursor / width;
+ if (newLine > oldLine) {
+ printANSISequence((newLine - oldLine) + "B");
+ }
+ printANSISequence(1 +(cursor % width) + "G");
+ }
+ flushConsole();
+ return;
+ }
+
+ char c;
+
+ if (where < 0) {
+ int len = 0;
+ for (int i = buf.cursor; i < buf.cursor - where; i++) {
+ if (buf.getBuffer().charAt(i) == '\t') {
+ len += TAB_WIDTH;
+ } else {
+ len++;
+ }
+ }
+
+ char cbuf[] = new char[len];
+ Arrays.fill(cbuf, BACKSPACE);
+ out.write(cbuf);
+
+ return;
+ } else if (buf.cursor == 0) {
+ return;
+ } else if (mask != null) {
+ c = mask.charValue();
+ } else {
+ printCharacters(buf.buffer.substring(buf.cursor - where, buf.cursor).toCharArray());
+ return;
+ }
+
+ // null character mask: don't output anything
+ if (NULL_MASK.equals(mask)) {
+ return;
+ }
+
+ printCharacters(c, Math.abs(where));
+ }
+
+ /**
+ * Read a character from the console.
+ *
+ * @return the character, or -1 if an EOF is received.
+ */
+ public final int readVirtualKey() throws IOException {
+ int c = terminal.readVirtualKey(in);
+
+ if (debugger != null) {
+ // debug("keystroke: " + c + "");
+ }
+
+ // clear any echo characters
+ clearEcho(c);
+
+ return c;
+ }
+
+ public final int readCharacter(final char[] allowed) throws IOException {
+ // if we restrict to a limited set and the current character
+ // is not in the set, then try again.
+ char c;
+
+ Arrays.sort(allowed); // always need to sort before binarySearch
+
+ while (Arrays.binarySearch(allowed, c = (char) readVirtualKey()) < 0);
+
+ return c;
+ }
+
+ /**
+ * Issue <em>num</em> deletes.
+ *
+ * @return the number of characters backed up
+ */
+ private final int delete(final int num)
+ throws IOException {
+ /* Commented out beacuse of DWA-2949:
+ if (buf.cursor == 0)
+ return 0;*/
+
+ buf.buffer.delete(buf.cursor, buf.cursor + 1);
+ drawBuffer(1);
+
+ return 1;
+ }
+
+ public final boolean replace(int num, String replacement) {
+ buf.buffer.replace(buf.cursor - num, buf.cursor, replacement);
+ try {
+ moveCursor(-num);
+ drawBuffer(Math.max(0, num - replacement.length()));
+ moveCursor(replacement.length());
+ } catch (IOException e) {
+ e.printStackTrace();
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Issue a delete.
+ *
+ * @return true if successful
+ */
+ public final boolean delete()
+ throws IOException {
+ return delete(1) == 1;
+ }
+
+ public void setHistory(final History history) {
+ this.history = history;
+ }
+
+ public History getHistory() {
+ return this.history;
+ }
+
+ public void setCompletionHandler(final CompletionHandler completionHandler) {
+ this.completionHandler = completionHandler;
+ }
+
+ public CompletionHandler getCompletionHandler() {
+ return this.completionHandler;
+ }
+
+ /**
+ * <p>
+ * Set the echo character. For example, to have "*" entered when a password
+ * is typed:
+ * </p>
+ *
+ * <pre>
+ * myConsoleReader.setEchoCharacter(new Character('*'));
+ * </pre>
+ *
+ * <p>
+ * Setting the character to
+ *
+ * <pre>
+ * null
+ * </pre>
+ *
+ * will restore normal character echoing. Setting the character to
+ *
+ * <pre>
+ * new Character(0)
+ * </pre>
+ *
+ * will cause nothing to be echoed.
+ * </p>
+ *
+ * @param echoCharacter
+ * the character to echo to the console in place of the typed
+ * character.
+ */
+ public void setEchoCharacter(final Character echoCharacter) {
+ this.echoCharacter = echoCharacter;
+ }
+
+ /**
+ * Returns the echo character.
+ */
+ public Character getEchoCharacter() {
+ return this.echoCharacter;
+ }
+
+ /**
+ * No-op for exceptions we want to silently consume.
+ */
+ private void consumeException(final Throwable e) {
+ }
+
+ /**
+ * Checks to see if the specified character is a delimiter. We consider a
+ * character a delimiter if it is anything but a letter or digit.
+ *
+ * @param c
+ * the character to test
+ * @return true if it is a delimiter
+ */
+ private boolean isDelimiter(char c) {
+ return !Character.isLetterOrDigit(c);
+ }
+
+ private void printANSISequence(String sequence) throws IOException {
+ printCharacter(27);
+ printCharacter('[');
+ printString(sequence);
+ flushConsole();
+ }
+
+ /*
+ private int currentCol, currentRow;
+
+ private void getCurrentPosition() {
+ // check for ByteArrayInputStream to disable for unit tests
+ if (terminal.isANSISupported() && !(in instanceof ByteArrayInputStream)) {
+ try {
+ printANSISequence("[6n");
+ flushConsole();
+ StringBuffer b = new StringBuffer(8);
+ // position is sent as <ESC>[{ROW};{COLUMN}R
+ int r;
+ while((r = in.read()) > -1 && r != 'R') {
+ if (r != 27 && r != '[') {
+ b.append((char) r);
+ }
+ }
+ String[] pos = b.toString().split(";");
+ currentRow = Integer.parseInt(pos[0]);
+ currentCol = Integer.parseInt(pos[1]);
+ } catch (Exception x) {
+ // no luck
+ currentRow = currentCol = -1;
+ }
+ }
+ }
+ */
+
+ /**
+ * Whether or not to add new commands to the history buffer.
+ */
+ public void setUseHistory(boolean useHistory) {
+ this.useHistory = useHistory;
+ }
+
+ /**
+ * Whether or not to add new commands to the history buffer.
+ */
+ public boolean getUseHistory() {
+ return useHistory;
+ }
+
+ /**
+ * Whether to use pagination when the number of rows of candidates exceeds
+ * the height of the temrinal.
+ */
+ public void setUsePagination(boolean usePagination) {
+ this.usePagination = usePagination;
+ }
+
+ /**
+ * Whether to use pagination when the number of rows of candidates exceeds
+ * the height of the temrinal.
+ */
+ public boolean getUsePagination() {
+ return this.usePagination;
+ }
+
+ public void printSearchStatus(String searchTerm, String match) throws IOException {
+ int i = match.indexOf(searchTerm);
+ printString("\r(reverse-i-search) `" + searchTerm + "': " + match + "\u001b[K");
+ // FIXME: our ANSI using back() does not work here
+ printCharacters(BACKSPACE, match.length() - i);
+ flushConsole();
+ }
+
+ public void restoreLine() throws IOException {
+ printString("\u001b[2K"); // ansi/vt100 for clear whole line
+ redrawLine();
+ flushConsole();
+ }
+}
diff --git a/src/src/main/java/jline/ConsoleReaderInputStream.java b/src/src/main/java/jline/ConsoleReaderInputStream.java
new file mode 100644
index 0000000..22a7b6d
--- /dev/null
+++ b/src/src/main/java/jline/ConsoleReaderInputStream.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * An {@link InputStream} implementation that wraps a {@link ConsoleReader}.
+ * It is useful for setting up the {@link System#in} for a generic
+ * console.
+ * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public class ConsoleReaderInputStream extends SequenceInputStream {
+ private static InputStream systemIn = System.in;
+
+ public static void setIn() throws IOException {
+ setIn(new ConsoleReader());
+ }
+
+ public static void setIn(final 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 ConsoleReader reader) {
+ super(new ConsoleEnumeration(reader));
+ }
+
+ private static class ConsoleEnumeration implements Enumeration {
+ private final ConsoleReader reader;
+ private ConsoleLineInputStream next = null;
+ private ConsoleLineInputStream prev = null;
+
+ public ConsoleEnumeration(final ConsoleReader reader) {
+ this.reader = reader;
+ }
+
+ public Object 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 ConsoleReader reader;
+ private String line = null;
+ private int index = 0;
+ private boolean eol = false;
+ protected boolean wasNull = false;
+
+ public ConsoleLineInputStream(final 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++);
+ }
+ }
+}
diff --git a/src/src/main/java/jline/ConsoleRunner.java b/src/src/main/java/jline/ConsoleRunner.java
new file mode 100644
index 0000000..36356de
--- /dev/null
+++ b/src/src/main/java/jline/ConsoleRunner.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * <p>
+ * A pass-through application that sets the system input stream to a
+ * {@link ConsoleReader} and invokes the specified main method.
+ * </p>
+ * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public class ConsoleRunner {
+ private static ConsoleReader reader;
+
+ public static ConsoleReader getReader() { return reader; }
+
+ public static final String property = "jline.history";
+
+ public static void main(final String[] args) throws Exception {
+ String historyFileName = null;
+
+ List argList = new ArrayList(Arrays.asList(args));
+
+ if (argList.size() == 0) {
+ usage();
+
+ return;
+ }
+
+ historyFileName = System.getProperty(ConsoleRunner.property, null);
+
+ // invoke the main() method
+ String mainClass = (String) argList.remove(0);
+
+ // setup the inpout stream
+ reader = new ConsoleReader();
+
+ if (historyFileName != null) {
+ reader.setHistory(new History (new File
+ (System.getProperty("user.home"),
+ ".jline-" + mainClass
+ + "." + historyFileName + ".history")));
+ } else {
+ reader.setHistory(new History(new File
+ (System.getProperty("user.home"),
+ ".jline-" + mainClass + ".history")));
+ }
+
+ String completors = System.getProperty
+ (ConsoleRunner.class.getName() + ".completors", "");
+ List completorList = new ArrayList();
+
+ for (StringTokenizer tok = new StringTokenizer(completors, ",");
+ tok.hasMoreTokens();) {
+ completorList.add
+ ((Completor) Class.forName(tok.nextToken()).newInstance());
+ }
+
+ if (completorList.size() > 0) {
+ reader.addCompletor(new ArgumentCompletor(completorList));
+ }
+
+ ConsoleReaderInputStream.setIn(reader);
+
+ try {
+ Class.forName(mainClass).
+ getMethod("main", new Class[] { String[].class }).
+ invoke(null, new Object[] { argList.toArray(new String[0]) });
+ } finally {
+ // just in case this main method is called from another program
+ ConsoleReaderInputStream.restoreIn();
+ }
+ }
+
+ private static void usage() {
+ System.out.println("Usage: \n java " + "[-Djline.history='name'] "
+ + ConsoleRunner.class.getName()
+ + " <target class name> [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/src/src/main/java/jline/CursorBuffer.java b/src/src/main/java/jline/CursorBuffer.java
new file mode 100644
index 0000000..120d705
--- /dev/null
+++ b/src/src/main/java/jline/CursorBuffer.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+/**
+ * A CursorBuffer is a holder for a {@link StringBuffer} that also contains the
+ * current cursor position.
+ *
+ * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public class CursorBuffer {
+ public int cursor = 0;
+
+ StringBuffer buffer = new StringBuffer();
+
+ private boolean overtyping = false;
+
+ public int length() {
+ return buffer.length();
+ }
+
+ public char current() {
+ if (cursor <= 0) {
+ return 0;
+ }
+
+ return buffer.charAt(cursor - 1);
+ }
+
+ public boolean clearBuffer() {
+ if (buffer.length() == 0) {
+ return false;
+ }
+
+ buffer.delete(0, buffer.length());
+ cursor = 0;
+ return true;
+ }
+
+ /**
+ * 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 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 {@link String} into the buffer, setting the cursor
+ * to the end of the insertion point.
+ *
+ * @param str
+ * the String to insert. Must not be null.
+ */
+ public void write(final String 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 String toString() {
+ return buffer.toString();
+ }
+
+ public boolean isOvertyping() {
+ return overtyping;
+ }
+
+ public void setOvertyping(boolean b) {
+ overtyping = b;
+ }
+
+ public StringBuffer getBuffer() {
+ return buffer;
+ }
+
+ public void setBuffer(StringBuffer buffer) {
+ buffer.setLength(0);
+ buffer.append(this.buffer.toString());
+
+ this.buffer = buffer;
+ }
+
+
+}
diff --git a/src/src/main/java/jline/FileNameCompletor.java b/src/src/main/java/jline/FileNameCompletor.java
new file mode 100644
index 0000000..19c4525
--- /dev/null
+++ b/src/src/main/java/jline/FileNameCompletor.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * A file name completor takes the buffer and issues a list of
+ * potential completions.
+ *
+ * <p>
+ * This completor tries to behave as similar as possible to
+ * <i>bash</i>'s file name completion (using GNU readline)
+ * with the following exceptions:
+ *
+ * <ul>
+ * <li>Candidates that are directories will end with "/"</li>
+ * <li>Wildcard regular expressions are not evaluated or replaced</li>
+ * <li>The "~" character can be used to represent the user's home,
+ * but it cannot complete to other users' homes, since java does
+ * not provide any way of determining that easily</li>
+ * </ul>
+ *
+ * <p>TODO</p>
+ * <ul>
+ * <li>Handle files with spaces in them</li>
+ * <li>Have an option for file type color highlighting</li>
+ * </ul>
+ *
+ * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public class FileNameCompletor implements Completor {
+ public int complete(final String buf, final int cursor,
+ final List candidates) {
+ String buffer = (buf == null) ? "" : buf;
+
+ String translated = buffer;
+
+ // special character: ~ maps to the user's home directory
+ if (translated.startsWith("~" + File.separator)) {
+ translated = System.getProperty("user.home")
+ + translated.substring(1);
+ } else if (translated.startsWith("~")) {
+ translated = new File(System.getProperty("user.home")).getParentFile()
+ .getAbsolutePath();
+ } else if (!(translated.startsWith(File.separator))) {
+ translated = new File("").getAbsolutePath() + File.separator
+ + translated;
+ }
+
+ File f = new File(translated);
+
+ final File dir;
+
+ if (translated.endsWith(File.separator)) {
+ dir = f;
+ } else {
+ dir = f.getParentFile();
+ }
+
+ final File[] entries = (dir == null) ? new File[0] : dir.listFiles();
+
+ try {
+ return matchFiles(buffer, translated, entries, candidates);
+ } finally {
+ // we want to output a sorted list of files
+ sortFileNames(candidates);
+ }
+ }
+
+ protected void sortFileNames(final List fileNames) {
+ Collections.sort(fileNames);
+ }
+
+ /**
+ * Match the specified <i>buffer</i> to the array of <i>entries</i>
+ * and enter the matches into the list of <i>candidates</i>. This method
+ * can be overridden in a subclass that wants to do more
+ * sophisticated file name completion.
+ *
+ * @param buffer the untranslated buffer
+ * @param translated the buffer with common characters replaced
+ * @param entries the list of files to match
+ * @param candidates the list of candidates to populate
+ *
+ * @return the offset of the match
+ */
+ public int matchFiles(String buffer, String translated, File[] entries,
+ List candidates) {
+ if (entries == null) {
+ return -1;
+ }
+
+ int matches = 0;
+
+ // first pass: just count the matches
+ for (int i = 0; i < entries.length; i++) {
+ if (entries[i].getAbsolutePath().startsWith(translated)) {
+ matches++;
+ }
+ }
+
+ // green - executable
+ // blue - directory
+ // red - compressed
+ // cyan - symlink
+ for (int i = 0; i < entries.length; i++) {
+ if (entries[i].getAbsolutePath().startsWith(translated)) {
+ String name =
+ entries[i].getName()
+ + (((matches == 1) && entries[i].isDirectory())
+ ? File.separator : " ");
+
+ /*
+ if (entries [i].isDirectory ())
+ {
+ name = new ANSIBuffer ().blue (name).toString ();
+ }
+ */
+ candidates.add(name);
+ }
+ }
+
+ final int index = buffer.lastIndexOf(File.separator);
+
+ return index + File.separator.length();
+ }
+}
diff --git a/src/src/main/java/jline/History.java b/src/src/main/java/jline/History.java
new file mode 100644
index 0000000..aefadc3
--- /dev/null
+++ b/src/src/main/java/jline/History.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * A command history buffer.
+ *
+ * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public class History {
+
+ private List history = new ArrayList();
+ private PrintWriter output = null;
+ private int maxSize = 500;
+ private int currentIndex = 0;
+
+ /**
+ * Construstor: initialize a blank history.
+ */
+ public History() {
+ }
+
+ /**
+ * Construstor: initialize History object the the specified {@link File} for
+ * storage.
+ */
+ public History(final File historyFile) throws IOException {
+ setHistoryFile(historyFile);
+ }
+
+ public void setHistoryFile(final File historyFile) throws IOException {
+ if (historyFile.isFile()) {
+ load(new FileInputStream(historyFile));
+ }
+
+ setOutput(new PrintWriter(new FileWriter(historyFile), true));
+ flushBuffer();
+ }
+
+ /**
+ * Load the history buffer from the specified InputStream.
+ */
+ public void load(final InputStream in) throws IOException {
+ load(new InputStreamReader(in));
+ }
+
+ /**
+ * Load the history buffer from the specified Reader.
+ */
+ public void load(final Reader reader) throws IOException {
+ BufferedReader breader = new BufferedReader(reader);
+ List lines = new ArrayList();
+ String line;
+
+ while ((line = breader.readLine()) != null) {
+ lines.add(line);
+ }
+
+ for (Iterator i = lines.iterator(); i.hasNext();) {
+ addToHistory((String) i.next());
+ }
+ }
+
+ public int size() {
+ return history.size();
+ }
+
+ /**
+ * Clear the history buffer
+ */
+ public void clear() {
+ history.clear();
+ currentIndex = 0;
+ }
+
+ /**
+ * Add the specified buffer to the end of the history. The pointer is set to
+ * the end of the history buffer.
+ */
+ public void addToHistory(final String buffer) {
+ // don't append duplicates to the end of the buffer
+ if ((history.size() != 0) && buffer.equals(history.get(history.size() - 1))) {
+ return;
+ }
+
+ history.add(buffer);
+
+ while (history.size() > getMaxSize()) {
+ history.remove(0);
+ }
+
+ currentIndex = history.size();
+
+ if (getOutput() != null) {
+ getOutput().println(buffer);
+ getOutput().flush();
+ }
+ }
+
+ /**
+ * Flush the entire history buffer to the output PrintWriter.
+ */
+ public void flushBuffer() throws IOException {
+ if (getOutput() != null) {
+ for (Iterator i = history.iterator(); i.hasNext(); getOutput().println((String) i.next())) {
+ ;
+ }
+
+ getOutput().flush();
+ }
+ }
+
+ /**
+ * 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 moveToLastEntry() {
+ int lastEntry = history.size() - 1;
+ if (lastEntry >= 0 && lastEntry != currentIndex) {
+ currentIndex = history.size() - 1;
+ 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() {
+ currentIndex = history.size();
+ }
+
+ /**
+ * Set the maximum size that the history buffer will store.
+ */
+ public void setMaxSize(final int maxSize) {
+ this.maxSize = maxSize;
+ }
+
+ /**
+ * Get the maximum size that the history buffer will store.
+ */
+ public int getMaxSize() {
+ return this.maxSize;
+ }
+
+ /**
+ * The output to which all history elements will be written (or null of
+ * history is not saved to a buffer).
+ */
+ public void setOutput(final PrintWriter output) {
+ this.output = output;
+ }
+
+ /**
+ * Returns the PrintWriter that is used to store history elements.
+ */
+ public PrintWriter getOutput() {
+ return this.output;
+ }
+
+ /**
+ * Returns the current history index.
+ */
+ public int getCurrentIndex() {
+ return this.currentIndex;
+ }
+
+ /**
+ * Return the content of the current buffer.
+ */
+ public String current() {
+ if (currentIndex >= history.size()) {
+ return "";
+ }
+
+ return (String) history.get(currentIndex);
+ }
+
+ /**
+ * Move the pointer to the previous element in the buffer.
+ *
+ * @return true if we successfully went to the previous element
+ */
+ public boolean previous() {
+ if (currentIndex <= 0) {
+ return false;
+ }
+
+ currentIndex--;
+
+ 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 (currentIndex >= history.size()) {
+ return false;
+ }
+
+ currentIndex++;
+
+ return true;
+ }
+
+ /**
+ * Returns an immutable list of the history buffer.
+ */
+ public List getHistoryList() {
+ return Collections.unmodifiableList(history);
+ }
+
+ /**
+ * Returns the standard {@link AbstractCollection#toString} representation
+ * of the history list.
+ */
+ public String toString() {
+ return history.toString();
+ }
+
+ /**
+ * 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 moveToFirstEntry() {
+ if (history.size() > 0 && currentIndex != 0) {
+ currentIndex = 0;
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * 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) {
+ for (int i = startIndex - 1; i >= 0; i--) {
+ if (i >= size())
+ continue;
+ if (getHistory(i).indexOf(searchTerm) != -1) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * 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 s) {
+ return searchBackwards(s, getCurrentIndex());
+ }
+
+ /**
+ * Get the history string for the given index.
+ *
+ * @param index
+ * @return
+ */
+ public String getHistory(int index) {
+ return (String) history.get(index);
+ }
+
+ /**
+ * Set current index to given number.
+ *
+ * @param index
+ */
+ public void setCurrentIndex(int index) {
+ if (index >= 0 && index < history.size())
+ currentIndex = index;
+ }
+}
diff --git a/src/src/main/java/jline/MultiCompletor.java b/src/src/main/java/jline/MultiCompletor.java
new file mode 100644
index 0000000..e3cd4e3
--- /dev/null
+++ b/src/src/main/java/jline/MultiCompletor.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.util.*;
+
+/**
+ * <p>
+ * A completor that contains multiple embedded completors. This differs
+ * from the {@link ArgumentCompletor}, in that the nested completors
+ * are dispatched individually, rather than delimited by arguments.
+ * </p>
+ *
+ * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public class MultiCompletor implements Completor {
+ Completor[] completors = new Completor[0];
+
+ /**
+ * Construct a MultiCompletor with no embedded completors.
+ */
+ public MultiCompletor() {
+ this(new Completor[0]);
+ }
+
+ /**
+ * Construct a MultiCompletor with the specified list of
+ * {@link Completor} instances.
+ */
+ public MultiCompletor(final List completors) {
+ this((Completor[]) completors.toArray(new Completor[completors.size()]));
+ }
+
+ /**
+ * Construct a MultiCompletor with the specified
+ * {@link Completor} instances.
+ */
+ public MultiCompletor(final Completor[] completors) {
+ this.completors = completors;
+ }
+
+ public int complete(final String buffer, final int pos, final List cand) {
+ int[] positions = new int[completors.length];
+ List[] copies = new List[completors.length];
+
+ for (int i = 0; i < completors.length; i++) {
+ // clone and save the candidate list
+ copies[i] = new LinkedList(cand);
+ positions[i] = completors[i].complete(buffer, pos, copies[i]);
+ }
+
+ int maxposition = -1;
+
+ for (int i = 0; i < positions.length; i++) {
+ maxposition = Math.max(maxposition, positions[i]);
+ }
+
+ // now we have the max cursor value: build up all the
+ // candidate lists that have the same cursor value
+ for (int i = 0; i < copies.length; i++) {
+ if (positions[i] == maxposition) {
+ cand.addAll(copies[i]);
+ }
+ }
+
+ return maxposition;
+ }
+
+ public void setCompletors(final Completor[] completors) {
+ this.completors = completors;
+ }
+
+ public Completor[] getCompletors() {
+ return this.completors;
+ }
+}
diff --git a/src/src/main/java/jline/NullCompletor.java b/src/src/main/java/jline/NullCompletor.java
new file mode 100644
index 0000000..aa6cdf7
--- /dev/null
+++ b/src/src/main/java/jline/NullCompletor.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.util.*;
+
+/**
+ * <p>
+ * A completor that does nothing. Useful as the last item in an
+ * {@link ArgumentCompletor}.
+ * </p>
+ *
+ * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public class NullCompletor implements Completor {
+ /**
+ * Returns -1 always, indicating that the the buffer is never
+ * handled.
+ */
+ public int complete(final String buffer, int cursor, List candidates) {
+ return -1;
+ }
+}
diff --git a/src/src/main/java/jline/SimpleCompletor.java b/src/src/main/java/jline/SimpleCompletor.java
new file mode 100644
index 0000000..2d488b6
--- /dev/null
+++ b/src/src/main/java/jline/SimpleCompletor.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * <p>
+ * A simple {@link Completor} implementation that handles a pre-defined
+ * list of completion words.
+ * </p>
+ *
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * myConsoleReader.addCompletor (new SimpleCompletor (new String [] { "now", "yesterday", "tomorrow" }));
+ * </pre>
+ *
+ * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public class SimpleCompletor implements Completor, Cloneable {
+ /**
+ * The list of candidates that will be completed.
+ */
+ SortedSet candidates;
+
+ /**
+ * A delimiter to use to qualify completions.
+ */
+ String delimiter;
+ final SimpleCompletorFilter filter;
+
+ /**
+ * Create a new SimpleCompletor with a single possible completion
+ * values.
+ */
+ public SimpleCompletor(final String candidateString) {
+ this(new String[] {
+ candidateString
+ });
+ }
+
+ /**
+ * Create a new SimpleCompletor with a list of possible completion
+ * values.
+ */
+ public SimpleCompletor(final String[] candidateStrings) {
+ this(candidateStrings, null);
+ }
+
+ public SimpleCompletor(final String[] strings,
+ final SimpleCompletorFilter filter) {
+ this.filter = filter;
+ setCandidateStrings(strings);
+ }
+
+ /**
+ * Complete candidates using the contents of the specified Reader.
+ */
+ public SimpleCompletor(final Reader reader) throws IOException {
+ this(getStrings(reader));
+ }
+
+ /**
+ * Complete candidates using the whitespearated values in
+ * read from the specified Reader.
+ */
+ public SimpleCompletor(final InputStream in) throws IOException {
+ this(getStrings(new InputStreamReader(in)));
+ }
+
+ private static String[] getStrings(final Reader in)
+ throws IOException {
+ final Reader reader =
+ (in instanceof BufferedReader) ? in : new BufferedReader(in);
+
+ List words = new LinkedList();
+ String line;
+
+ while ((line = ((BufferedReader) reader).readLine()) != null) {
+ for (StringTokenizer tok = new StringTokenizer(line);
+ tok.hasMoreTokens(); words.add(tok.nextToken())) {
+ ;
+ }
+ }
+
+ return (String[]) words.toArray(new String[words.size()]);
+ }
+
+ public int complete(final String buffer, final int cursor, final List clist) {
+ String start = (buffer == null) ? "" : buffer;
+
+ SortedSet matches = candidates.tailSet(start);
+
+ for (Iterator i = matches.iterator(); i.hasNext();) {
+ String can = (String) i.next();
+
+ if (!(can.startsWith(start))) {
+ break;
+ }
+
+ if (delimiter != null) {
+ int index = can.indexOf(delimiter, cursor);
+
+ if (index != -1) {
+ can = can.substring(0, index + 1);
+ }
+ }
+
+ clist.add(can);
+ }
+
+ if (clist.size() == 1) {
+ clist.set(0, ((String) clist.get(0)) + " ");
+ }
+
+ // the index of the completion is always from the beginning of
+ // the buffer.
+ return (clist.size() == 0) ? (-1) : 0;
+ }
+
+ public void setDelimiter(final String delimiter) {
+ this.delimiter = delimiter;
+ }
+
+ public String getDelimiter() {
+ return this.delimiter;
+ }
+
+ public void setCandidates(final SortedSet candidates) {
+ if (filter != null) {
+ TreeSet filtered = new TreeSet();
+
+ for (Iterator i = candidates.iterator(); i.hasNext();) {
+ String element = (String) i.next();
+ element = filter.filter(element);
+
+ if (element != null) {
+ filtered.add(element);
+ }
+ }
+
+ this.candidates = filtered;
+ } else {
+ this.candidates = candidates;
+ }
+ }
+
+ public SortedSet getCandidates() {
+ return Collections.unmodifiableSortedSet(this.candidates);
+ }
+
+ public void setCandidateStrings(final String[] strings) {
+ setCandidates(new TreeSet(Arrays.asList(strings)));
+ }
+
+ public void addCandidateString(final String candidateString) {
+ final String string =
+ (filter == null) ? candidateString : filter.filter(candidateString);
+
+ if (string != null) {
+ candidates.add(string);
+ }
+ }
+
+ public Object clone() throws CloneNotSupportedException {
+ return super.clone();
+ }
+
+ /**
+ * Filter for elements in the completor.
+ *
+ * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+ public static interface SimpleCompletorFilter {
+ /**
+ * Filter the specified String. To not filter it, return the
+ * same String as the parameter. To exclude it, return null.
+ */
+ public String filter(String element);
+ }
+
+ public static class NoOpFilter implements SimpleCompletorFilter {
+ public String filter(final String element) {
+ return element;
+ }
+ }
+}
diff --git a/src/src/main/java/jline/Terminal.java b/src/src/main/java/jline/Terminal.java
new file mode 100644
index 0000000..6eecbe0
--- /dev/null
+++ b/src/src/main/java/jline/Terminal.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.io.*;
+
+/**
+ * Representation of the input terminal for a platform. Handles
+ * any initialization that the platform may need to perform
+ * in order to allow the {@link ConsoleReader} to correctly handle
+ * input.
+ *
+ * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public abstract class Terminal implements ConsoleOperations {
+ private static Terminal term;
+
+ /**
+ * @see #setupTerminal
+ */
+ public static Terminal getTerminal() {
+ return setupTerminal();
+ }
+
+ /**
+ * Reset the current terminal to null.
+ */
+ public static void resetTerminal() {
+ term = null;
+ }
+
+ /**
+ * <p>Configure and return the {@link Terminal} instance for the
+ * current platform. This will initialize any system settings
+ * that are required for the console to be able to handle
+ * input correctly, such as setting tabtop, buffered input, and
+ * character echo.</p>
+ *
+ * <p>This class will use the Terminal implementation specified in the
+ * <em>jline.terminal</em> system property, or, if it is unset, by
+ * detecting the operating system from the <em>os.name</em>
+ * system property and instantiating either the
+ * {@link WindowsTerminalTest} or {@link UnixTerminal}.
+ *
+ * @see #initializeTerminal
+ */
+ public static synchronized Terminal setupTerminal() {
+ if (term != null) {
+ return term;
+ }
+
+ final Terminal t;
+
+ String os = System.getProperty("os.name").toLowerCase();
+ String termProp = System.getProperty("jline.terminal");
+
+ if ((termProp != null) && (termProp.length() > 0)) {
+ try {
+ t = (Terminal) Class.forName(termProp).newInstance();
+ } catch (Exception e) {
+ throw (IllegalArgumentException) new IllegalArgumentException(e
+ .toString()).fillInStackTrace();
+ }
+ } else if (os.indexOf("windows") != -1) {
+ t = new WindowsTerminal();
+ } else {
+ t = new UnixTerminal();
+ }
+
+ try {
+ t.initializeTerminal();
+ } catch (Exception e) {
+ e.printStackTrace();
+
+ return term = new UnsupportedTerminal();
+ }
+
+ return term = t;
+ }
+
+ /**
+ * Returns true if the current console supports ANSI
+ * codes.
+ */
+ public boolean isANSISupported() {
+ return true;
+ }
+
+ /**
+ * Read a single character from the input stream. This might
+ * enable a terminal implementation to better handle nuances of
+ * the console.
+ */
+ public int readCharacter(final InputStream in) throws IOException {
+ return in.read();
+ }
+
+ /**
+ * Reads a virtual key from the console. Typically, this will
+ * just be the raw character that was entered, but in some cases,
+ * multiple input keys will need to be translated into a single
+ * virtual key.
+ *
+ * @param in the InputStream to read from
+ * @return the virtual key (e.g., {@link ConsoleOperations#VK_UP})
+ */
+ public int readVirtualKey(InputStream in) throws IOException {
+ return readCharacter(in);
+ }
+
+ /**
+ * Initialize any system settings
+ * that are required for the console to be able to handle
+ * input correctly, such as setting tabtop, buffered input, and
+ * character echo.
+ */
+ public abstract void initializeTerminal() throws Exception;
+
+ /**
+ * Returns the current width of the terminal (in characters)
+ */
+ public abstract int getTerminalWidth();
+
+ /**
+ * Returns the current height of the terminal (in lines)
+ */
+ public abstract int getTerminalHeight();
+
+ /**
+ * Returns true if this terminal is capable of initializing the
+ * terminal to use jline.
+ */
+ public abstract boolean isSupported();
+
+ /**
+ * Returns true if the terminal will echo all characters type.
+ */
+ public abstract boolean getEcho();
+
+ /**
+ * Invokes before the console reads a line with the prompt and mask.
+ */
+ public void beforeReadLine(ConsoleReader reader, String prompt,
+ Character mask) {
+ }
+
+ /**
+ * Invokes after the console reads a line with the prompt and mask.
+ */
+ public void afterReadLine(ConsoleReader reader, String prompt,
+ Character mask) {
+ }
+
+ /**
+ * Returns false if character echoing is disabled.
+ */
+ public abstract boolean isEchoEnabled();
+
+
+ /**
+ * Enable character echoing. This can be used to re-enable character
+ * if the ConsoleReader is no longer being used.
+ */
+ public abstract void enableEcho();
+
+
+ /**
+ * Disable character echoing. This can be used to manually re-enable
+ * character if the ConsoleReader has been disabled.
+ */
+ public abstract void disableEcho();
+
+ public InputStream getDefaultBindings() {
+ // Mac bindings are slightly different from Unix/Linux.
+ // For instance, the Delete key behavior is different between them.
+ return Terminal.class.getResourceAsStream(
+ System.getProperty("os.name").toLowerCase().startsWith("mac") ?
+ "keybindings-mac.properties" : "keybindings.properties");
+ }
+}
diff --git a/src/src/main/java/jline/UnixTerminal.java b/src/src/main/java/jline/UnixTerminal.java
new file mode 100644
index 0000000..83f5194
--- /dev/null
+++ b/src/src/main/java/jline/UnixTerminal.java
@@ -0,0 +1,433 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * <p>
+ * Terminal that is used for unix platforms. Terminal initialization
+ * is handled by issuing the <em>stty</em> command against the
+ * <em>/dev/tty</em> file to disable character echoing and enable
+ * character input. All known unix systems (including
+ * Linux and Macintosh OS X) support the <em>stty</em>), so this
+ * implementation should work for an reasonable POSIX system.
+ * </p>
+ *
+ * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ * @author Updates <a href="mailto:dwkemp@gmail.com">Dale Kemp</a> 2005-12-03
+ */
+public class UnixTerminal extends Terminal {
+ public static final short ARROW_START = 27;
+ public static final short ARROW_PREFIX = 91;
+ public static final short ARROW_LEFT = 68;
+ public static final short ARROW_RIGHT = 67;
+ public static final short ARROW_UP = 65;
+ public static final short ARROW_DOWN = 66;
+ public static final short O_PREFIX = 79;
+ public static final short HOME_CODE = 72;
+ public static final short END_CODE = 70;
+
+ public static final short DEL_THIRD = 51;
+ public static final short DEL_SECOND = 126;
+
+ private boolean echoEnabled;
+ private String ttyConfig;
+ private String ttyProps;
+ private long ttyPropsLastFetched;
+ private boolean backspaceDeleteSwitched = false;
+ private static String sttyCommand =
+ System.getProperty("jline.sttyCommand", "stty");
+
+
+ String encoding = System.getProperty("input.encoding", "UTF-8");
+ ReplayPrefixOneCharInputStream replayStream = new ReplayPrefixOneCharInputStream(encoding);
+ InputStreamReader replayReader;
+
+ public UnixTerminal() {
+ try {
+ replayReader = new InputStreamReader(replayStream, encoding);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ protected void checkBackspace(){
+ String[] ttyConfigSplit = ttyConfig.split(":|=");
+ backspaceDeleteSwitched = ttyConfigSplit.length >= 7 && "7f".equals(ttyConfigSplit[6]);
+ }
+
+ /**
+ * Remove line-buffered input by invoking "stty -icanon min 1"
+ * against the current terminal.
+ */
+ public void initializeTerminal() throws IOException, InterruptedException {
+ // save the initial tty configuration
+ ttyConfig = stty("-g");
+
+ // sanity check
+ if ((ttyConfig.length() == 0)
+ || ((ttyConfig.indexOf("=") == -1)
+ && (ttyConfig.indexOf(":") == -1))) {
+ throw new IOException("Unrecognized stty code: " + ttyConfig);
+ }
+
+ checkBackspace();
+
+ // set the console to be character-buffered instead of line-buffered
+ stty("-icanon min 1");
+
+ // disable character echoing
+ stty("-echo");
+ echoEnabled = false;
+
+ // at exit, restore the original tty configuration (for JDK 1.3+)
+ try {
+ Runtime.getRuntime().addShutdownHook(new Thread() {
+ public void start() {
+ try {
+ restoreTerminal();
+ } catch (Exception e) {
+ consumeException(e);
+ }
+ }
+ });
+ } catch (AbstractMethodError ame) {
+ // JDK 1.3+ only method. Bummer.
+ consumeException(ame);
+ }
+ }
+
+ /**
+ * Restore the original terminal configuration, which can be used when
+ * shutting down the console reader. The ConsoleReader cannot be
+ * used after calling this method.
+ */
+ public void restoreTerminal() throws Exception {
+ if (ttyConfig != null) {
+ stty(ttyConfig);
+ ttyConfig = null;
+ }
+ resetTerminal();
+ }
+
+
+
+ public int readVirtualKey(InputStream in) throws IOException {
+ int c = readCharacter(in);
+
+ if (backspaceDeleteSwitched)
+ if (c == DELETE)
+ c = BACKSPACE;
+ else if (c == BACKSPACE)
+ c = DELETE;
+
+ // in Unix terminals, arrow keys are represented by
+ // a sequence of 3 characters. E.g., the up arrow
+ // key yields 27, 91, 68
+ if (c == ARROW_START && in.available() > 0) {
+ // Escape key is also 27, so we use InputStream.available()
+ // to distinguish those. If 27 represents an arrow, there
+ // should be two more chars immediately available.
+ while (c == ARROW_START) {
+ c = readCharacter(in);
+ }
+ if (c == ARROW_PREFIX || c == O_PREFIX) {
+ c = readCharacter(in);
+ if (c == ARROW_UP) {
+ return CTRL_P;
+ } else if (c == ARROW_DOWN) {
+ return CTRL_N;
+ } else if (c == ARROW_LEFT) {
+ return CTRL_B;
+ } else if (c == ARROW_RIGHT) {
+ return CTRL_F;
+ } else if (c == HOME_CODE) {
+ return CTRL_A;
+ } else if (c == END_CODE) {
+ return CTRL_E;
+ } else if (c == DEL_THIRD) {
+ c = readCharacter(in); // read 4th
+ return DELETE;
+ }
+ }
+ }
+ // handle unicode characters, thanks for a patch from amyi@inf.ed.ac.uk
+ if (c > 128) {
+ // handle unicode characters longer than 2 bytes,
+ // thanks to Marc.Herbert@continuent.com
+ replayStream.setInput(c, in);
+// replayReader = new InputStreamReader(replayStream, encoding);
+ c = replayReader.read();
+
+ }
+
+ return c;
+ }
+
+ /**
+ * No-op for exceptions we want to silently consume.
+ */
+ private void consumeException(Throwable e) {
+ }
+
+ public boolean isSupported() {
+ return true;
+ }
+
+ public boolean getEcho() {
+ return false;
+ }
+
+ /**
+ * Returns the value of "stty size" width param.
+ *
+ * <strong>Note</strong>: this method caches the value from the
+ * first time it is called in order to increase speed, which means
+ * that changing to size of the terminal will not be reflected
+ * in the console.
+ */
+ public int getTerminalWidth() {
+ int val = -1;
+
+ try {
+ val = getTerminalProperty("columns");
+ } catch (Exception e) {
+ }
+
+ if (val == -1) {
+ val = 80;
+ }
+
+ return val;
+ }
+
+ /**
+ * Returns the value of "stty size" height param.
+ *
+ * <strong>Note</strong>: this method caches the value from the
+ * first time it is called in order to increase speed, which means
+ * that changing to size of the terminal will not be reflected
+ * in the console.
+ */
+ public int getTerminalHeight() {
+ int val = -1;
+
+ try {
+ val = getTerminalProperty("rows");
+ } catch (Exception e) {
+ }
+
+ if (val == -1) {
+ val = 24;
+ }
+
+ return val;
+ }
+
+ private int getTerminalProperty(String prop)
+ throws IOException, InterruptedException {
+ // tty properties are cached so we don't have to worry too much about getting term widht/height
+ if (ttyProps == null || System.currentTimeMillis() - ttyPropsLastFetched > 1000) {
+ ttyProps = stty("-a");
+ ttyPropsLastFetched = System.currentTimeMillis();
+ }
+ // need to be able handle both output formats:
+ // speed 9600 baud; 24 rows; 140 columns;
+ // and:
+ // speed 38400 baud; rows = 49; columns = 111; ypixels = 0; xpixels = 0;
+ for (StringTokenizer tok = new StringTokenizer(ttyProps, ";\n");
+ tok.hasMoreTokens();) {
+ String str = tok.nextToken().trim();
+
+ if (str.startsWith(prop)) {
+ int index = str.lastIndexOf(" ");
+
+ return Integer.parseInt(str.substring(index).trim());
+ } else if (str.endsWith(prop)) {
+ int index = str.indexOf(" ");
+
+ return Integer.parseInt(str.substring(0, index).trim());
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * Execute the stty command with the specified arguments
+ * against the current active terminal.
+ */
+ protected static String stty(final String args)
+ throws IOException, InterruptedException {
+ return exec("stty " + args + " < /dev/tty").trim();
+ }
+
+ /**
+ * Execute the specified command and return the output
+ * (both stdout and stderr).
+ */
+ private static String exec(final String cmd)
+ throws IOException, InterruptedException {
+ return exec(new String[] {
+ "sh",
+ "-c",
+ cmd
+ });
+ }
+
+ /**
+ * Execute the specified command and return the output
+ * (both stdout and stderr).
+ */
+ private static String exec(final String[] cmd)
+ throws IOException, InterruptedException {
+ ByteArrayOutputStream bout = new ByteArrayOutputStream();
+
+ Process p = Runtime.getRuntime().exec(cmd);
+ int c;
+ InputStream in = null;
+ InputStream err = null;
+ OutputStream out = null;
+
+ try {
+ 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 {
+ try {in.close();} catch (Exception e) {}
+ try {err.close();} catch (Exception e) {}
+ try {out.close();} catch (Exception e) {}
+ }
+
+ String result = new String(bout.toByteArray());
+
+ return result;
+ }
+
+ /**
+ * The command to use to set the terminal options. Defaults
+ * to "stty", or the value of the system property "jline.sttyCommand".
+ */
+ public static void setSttyCommand(String cmd) {
+ sttyCommand = cmd;
+ }
+
+ /**
+ * The command to use to set the terminal options. Defaults
+ * to "stty", or the value of the system property "jline.sttyCommand".
+ */
+ public static String getSttyCommand() {
+ return sttyCommand;
+ }
+
+ public synchronized boolean isEchoEnabled() {
+ return echoEnabled;
+ }
+
+
+ public synchronized void enableEcho() {
+ try {
+ stty("echo");
+ echoEnabled = true;
+ } catch (Exception e) {
+ consumeException(e);
+ }
+ }
+
+ public synchronized void disableEcho() {
+ try {
+ stty("-echo");
+ echoEnabled = false;
+ } catch (Exception e) {
+ consumeException(e);
+ }
+ }
+
+ /**
+ * This is awkward and inefficient, but probably the minimal way to add
+ * UTF-8 support to JLine
+ *
+ * @author <a href="mailto:Marc.Herbert@continuent.com">Marc Herbert</a>
+ */
+ static class ReplayPrefixOneCharInputStream extends InputStream {
+ byte firstByte;
+ int byteLength;
+ InputStream wrappedStream;
+ int byteRead;
+
+ final String encoding;
+
+ public ReplayPrefixOneCharInputStream(String encoding) {
+ this.encoding = encoding;
+ }
+
+ public void setInput(int recorded, InputStream wrapped) throws IOException {
+ this.byteRead = 0;
+ this.firstByte = (byte) recorded;
+ this.wrappedStream = wrapped;
+
+ byteLength = 1;
+ if (encoding.equalsIgnoreCase("UTF-8"))
+ setInputUTF8(recorded, wrapped);
+ else if (encoding.equalsIgnoreCase("UTF-16"))
+ byteLength = 2;
+ else if (encoding.equalsIgnoreCase("UTF-32"))
+ byteLength = 4;
+ }
+
+
+ public void setInputUTF8(int recorded, InputStream wrapped) throws IOException {
+ // 110yyyyy 10zzzzzz
+ if ((firstByte & (byte) 0xE0) == (byte) 0xC0)
+ this.byteLength = 2;
+ // 1110xxxx 10yyyyyy 10zzzzzz
+ else if ((firstByte & (byte) 0xF0) == (byte) 0xE0)
+ this.byteLength = 3;
+ // 11110www 10xxxxxx 10yyyyyy 10zzzzzz
+ else if ((firstByte & (byte) 0xF8) == (byte) 0xF0)
+ this.byteLength = 4;
+ else
+ throw new IOException("invalid UTF-8 first byte: " + firstByte);
+ }
+
+ public int read() throws IOException {
+ if (available() == 0)
+ return -1;
+
+ byteRead++;
+
+ if (byteRead == 1)
+ return firstByte;
+
+ return wrappedStream.read();
+ }
+
+ /**
+ * InputStreamReader is greedy and will try to read bytes in advance. We
+ * do NOT want this to happen since we use a temporary/"losing bytes"
+ * InputStreamReader above, that's why we hide the real
+ * wrappedStream.available() here.
+ */
+ public int available() {
+ return byteLength - byteRead;
+ }
+ }
+}
diff --git a/src/src/main/java/jline/UnsupportedTerminal.java b/src/src/main/java/jline/UnsupportedTerminal.java
new file mode 100644
index 0000000..2d87a18
--- /dev/null
+++ b/src/src/main/java/jline/UnsupportedTerminal.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.io.IOException;
+
+/**
+ * A no-op unsupported terminal.
+ *
+ * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public class UnsupportedTerminal extends Terminal {
+ private Thread maskThread = null;
+
+ public void initializeTerminal() {
+ // nothing we need to do (or can do) for windows.
+ }
+
+ public boolean getEcho() {
+ return true;
+ }
+
+
+ public boolean isEchoEnabled() {
+ return true;
+ }
+
+
+ public void enableEcho() {
+ }
+
+
+ public void disableEcho() {
+ }
+
+
+ /**
+ * Always returng 80, since we can't access this info on Windows.
+ */
+ public int getTerminalWidth() {
+ return 80;
+ }
+
+ /**
+ * Always returng 24, since we can't access this info on Windows.
+ */
+ public int getTerminalHeight() {
+ return 80;
+ }
+
+ public boolean isSupported() {
+ return false;
+ }
+
+ public void beforeReadLine(final ConsoleReader reader, final String prompt,
+ final Character mask) {
+ if ((mask != null) && (maskThread == null)) {
+ final String fullPrompt = "\r" + prompt
+ + " "
+ + " "
+ + " "
+ + "\r" + prompt;
+
+ maskThread = new Thread("JLine Mask Thread") {
+ public void run() {
+ while (!interrupted()) {
+ try {
+ reader.out.write(fullPrompt);
+ reader.out.flush();
+ sleep(3);
+ } catch (IOException ioe) {
+ return;
+ } catch (InterruptedException ie) {
+ return;
+ }
+ }
+ }
+ };
+
+ maskThread.setPriority(Thread.MAX_PRIORITY);
+ maskThread.setDaemon(true);
+ maskThread.start();
+ }
+ }
+
+ public void afterReadLine(final ConsoleReader reader, final String prompt,
+ final Character mask) {
+ if ((maskThread != null) && maskThread.isAlive()) {
+ maskThread.interrupt();
+ }
+
+ maskThread = null;
+ }
+}
diff --git a/src/src/main/java/jline/WindowsTerminal.java b/src/src/main/java/jline/WindowsTerminal.java
new file mode 100644
index 0000000..d036088
--- /dev/null
+++ b/src/src/main/java/jline/WindowsTerminal.java
@@ -0,0 +1,520 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.io.*;
+
+import jline.UnixTerminal.ReplayPrefixOneCharInputStream;
+
+/**
+ * <p>
+ * Terminal implementation for Microsoft Windows. Terminal initialization in
+ * {@link #initializeTerminal} is accomplished by extracting the
+ * <em>jline_<i>version</i>.dll</em>, saving it to the system temporary
+ * directoy (determined by the setting of the <em>java.io.tmpdir</em> System
+ * property), loading the library, and then calling the Win32 APIs <a
+ * href="http://msdn.microsoft.com/library/default.asp?
+ * url=/library/en-us/dllproc/base/setconsolemode.asp">SetConsoleMode</a> and
+ * <a href="http://msdn.microsoft.com/library/default.asp?
+ * url=/library/en-us/dllproc/base/getconsolemode.asp">GetConsoleMode</a> to
+ * disable character echoing.
+ * </p>
+ *
+ * <p>
+ * By default, the {@link #readCharacter} method will attempt to test to see if
+ * the specified {@link InputStream} is {@link System#in} or a wrapper around
+ * {@link FileDescriptor#in}, and if so, will bypass the character reading to
+ * directly invoke the readc() method in the JNI library. This is so the class
+ * can read special keys (like arrow keys) which are otherwise inaccessible via
+ * the {@link System#in} stream. Using JNI reading can be bypassed by setting
+ * the <code>jline.WindowsTerminal.directConsole</code> system property
+ * to <code>false</code>.
+ * </p>
+ *
+ * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public class WindowsTerminal extends Terminal {
+ // constants copied from wincon.h
+
+ /**
+ * 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.
+ */
+ private static final int 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.
+ */
+ private static final int 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.
+ */
+ private static final int 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.
+ */
+ private static final int 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.
+ */
+ private static final int 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.
+ */
+ private static final int 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.
+ */
+ private static final int ENABLE_WRAP_AT_EOL_OUTPUT = 2;
+
+ /**
+ * On windows terminals, this character indicates that a 'special' key has
+ * been pressed. This means that a key such as an arrow key, or delete, or
+ * home, etc. will be indicated by the next character.
+ */
+ public static final int SPECIAL_KEY_INDICATOR = 224;
+
+ /**
+ * On windows terminals, this character indicates that a special key on the
+ * number pad has been pressed.
+ */
+ public static final int NUMPAD_KEY_INDICATOR = 0;
+
+ /**
+ * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR,
+ * this character indicates an left arrow key press.
+ */
+ public static final int LEFT_ARROW_KEY = 75;
+
+ /**
+ * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR
+ * this character indicates an
+ * right arrow key press.
+ */
+ public static final int RIGHT_ARROW_KEY = 77;
+
+ /**
+ * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR
+ * this character indicates an up
+ * arrow key press.
+ */
+ public static final int UP_ARROW_KEY = 72;
+
+ /**
+ * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR
+ * this character indicates an
+ * down arrow key press.
+ */
+ public static final int DOWN_ARROW_KEY = 80;
+
+ /**
+ * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR
+ * this character indicates that
+ * the delete key was pressed.
+ */
+ public static final int DELETE_KEY = 83;
+
+ /**
+ * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR
+ * this character indicates that
+ * the home key was pressed.
+ */
+ public static final int HOME_KEY = 71;
+
+ /**
+ * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR
+ * this character indicates that
+ * the end key was pressed.
+ */
+ public static final char END_KEY = 79;
+
+ /**
+ * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR
+ * this character indicates that
+ * the page up key was pressed.
+ */
+ public static final char PAGE_UP_KEY = 73;
+
+ /**
+ * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR
+ * this character indicates that
+ * the page down key was pressed.
+ */
+ public static final char PAGE_DOWN_KEY = 81;
+
+ /**
+ * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR
+ * this character indicates that
+ * the insert key was pressed.
+ */
+ public static final char INSERT_KEY = 82;
+
+ /**
+ * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR,
+ * this character indicates that the escape key was pressed.
+ */
+ public static final char ESCAPE_KEY = 0;
+
+ private Boolean directConsole;
+
+ private boolean echoEnabled;
+
+ String encoding = System.getProperty("jline.WindowsTerminal.input.encoding", System.getProperty("file.encoding"));
+ ReplayPrefixOneCharInputStream replayStream = new ReplayPrefixOneCharInputStream(encoding);
+ InputStreamReader replayReader;
+
+ public WindowsTerminal() {
+ String dir = System.getProperty("jline.WindowsTerminal.directConsole");
+
+ if ("true".equals(dir)) {
+ directConsole = Boolean.TRUE;
+ } else if ("false".equals(dir)) {
+ directConsole = Boolean.FALSE;
+ }
+
+ try {
+ replayReader = new InputStreamReader(replayStream, encoding);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ }
+
+ private native int getConsoleMode();
+
+ private native void setConsoleMode(final int mode);
+
+ private native int readByte();
+
+ private native int getWindowsTerminalWidth();
+
+ private native int getWindowsTerminalHeight();
+
+ public int readCharacter(final InputStream in) throws IOException {
+ // if we can detect that we are directly wrapping the system
+ // input, then bypass the input stream and read directly (which
+ // allows us to access otherwise unreadable strokes, such as
+ // the arrow keys)
+ if (directConsole == Boolean.FALSE) {
+ return super.readCharacter(in);
+ } else if ((directConsole == Boolean.TRUE)
+ || ((in == System.in) || (in instanceof FileInputStream
+ && (((FileInputStream) in).getFD() == FileDescriptor.in)))) {
+ return readByte();
+ } else {
+ return super.readCharacter(in);
+ }
+ }
+
+ public void initializeTerminal() throws Exception {
+ loadLibrary("jline");
+
+ final int originalMode = getConsoleMode();
+
+ setConsoleMode(originalMode & ~ENABLE_ECHO_INPUT);
+
+ // set the console to raw mode
+ int newMode = originalMode
+ & ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT
+ | ENABLE_PROCESSED_INPUT | ENABLE_WINDOW_INPUT);
+ echoEnabled = false;
+ setConsoleMode(newMode);
+
+ // at exit, restore the original tty configuration (for JDK 1.3+)
+ try {
+ Runtime.getRuntime().addShutdownHook(new Thread() {
+ public void start() {
+ // restore the old console mode
+ setConsoleMode(originalMode);
+ }
+ });
+ } catch (AbstractMethodError ame) {
+ // JDK 1.3+ only method. Bummer.
+ consumeException(ame);
+ }
+ }
+
+ private void loadLibrary(final String name) throws IOException {
+ // store the DLL in the temporary directory for the System
+ String version = WindowsTerminal.class.getPackage().getImplementationVersion();
+
+ if (version == null) {
+ version = "";
+ }
+
+ version = version.replace('.', '_');
+
+ File f = new File(System.getProperty("java.io.tmpdir"), name + "_"
+ + version + ".dll");
+ boolean exists = f.isFile(); // check if it already exists
+
+ // extract the embedded jline.dll file from the jar and save
+ // it to the current directory
+ int bits = 32;
+
+ // check for 64-bit systems and use to appropriate DLL
+ if (System.getProperty("os.arch").indexOf("64") != -1)
+ bits = 64;
+
+ InputStream in = new BufferedInputStream(WindowsTerminal.class.getResourceAsStream(name + bits + ".dll"));
+
+ OutputStream fout = null;
+ try {
+ fout = new BufferedOutputStream(
+ new FileOutputStream(f));
+ byte[] bytes = new byte[1024 * 10];
+
+ for (int n = 0; n != -1; n = in.read(bytes)) {
+ fout.write(bytes, 0, n);
+ }
+
+ } catch (IOException ioe) {
+ // We might get an IOException trying to overwrite an existing
+ // jline.dll file if there is another process using the DLL.
+ // If this happens, ignore errors.
+ if (!exists) {
+ throw ioe;
+ }
+ } finally {
+ if (fout != null) {
+ try {
+ fout.close();
+ } catch (IOException ioe) {
+ // ignore
+ }
+ }
+ }
+
+ // try to clean up the DLL after the JVM exits
+ f.deleteOnExit();
+
+ // now actually load the DLL
+ System.load(f.getAbsolutePath());
+ }
+
+ public int readVirtualKey(InputStream in) throws IOException {
+ int indicator = readCharacter(in);
+
+ // in Windows terminals, arrow keys are represented by
+ // a sequence of 2 characters. E.g., the up arrow
+ // key yields 224, 72
+ if (indicator == SPECIAL_KEY_INDICATOR
+ || indicator == NUMPAD_KEY_INDICATOR) {
+ int key = readCharacter(in);
+
+ switch (key) {
+ case UP_ARROW_KEY:
+ return CTRL_P; // translate UP -> CTRL-P
+ case LEFT_ARROW_KEY:
+ return CTRL_B; // translate LEFT -> CTRL-B
+ case RIGHT_ARROW_KEY:
+ return CTRL_F; // translate RIGHT -> CTRL-F
+ case DOWN_ARROW_KEY:
+ return CTRL_N; // translate DOWN -> CTRL-N
+ case DELETE_KEY:
+ return CTRL_QM; // translate DELETE -> CTRL-?
+ case HOME_KEY:
+ return CTRL_A;
+ case END_KEY:
+ return CTRL_E;
+ case PAGE_UP_KEY:
+ return CTRL_K;
+ case PAGE_DOWN_KEY:
+ return CTRL_L;
+ case ESCAPE_KEY:
+ return CTRL_OB; // translate ESCAPE -> CTRL-[
+ case INSERT_KEY:
+ return CTRL_C;
+ default:
+ return 0;
+ }
+ } else if (indicator > 128) {
+ // handle unicode characters longer than 2 bytes,
+ // thanks to Marc.Herbert@continuent.com
+ replayStream.setInput(indicator, in);
+ // replayReader = new InputStreamReader(replayStream, encoding);
+ indicator = replayReader.read();
+
+ }
+
+ return indicator;
+
+ }
+
+ public boolean isSupported() {
+ return true;
+ }
+
+ /**
+ * Windows doesn't support ANSI codes by default; disable them.
+ */
+ public boolean isANSISupported() {
+ return false;
+ }
+
+ public boolean getEcho() {
+ return false;
+ }
+
+ /**
+ * Unsupported; return the default.
+ *
+ * @see Terminal#getTerminalWidth
+ */
+ public int getTerminalWidth() {
+ return getWindowsTerminalWidth();
+ }
+
+ /**
+ * Unsupported; return the default.
+ *
+ * @see Terminal#getTerminalHeight
+ */
+ public int getTerminalHeight() {
+ return getWindowsTerminalHeight();
+ }
+
+ /**
+ * No-op for exceptions we want to silently consume.
+ */
+ private void consumeException(final Throwable e) {
+ }
+
+ /**
+ * Whether or not to allow the use of the JNI console interaction.
+ */
+ public void setDirectConsole(Boolean directConsole) {
+ this.directConsole = directConsole;
+ }
+
+ /**
+ * Whether or not to allow the use of the JNI console interaction.
+ */
+ public Boolean getDirectConsole() {
+ return this.directConsole;
+ }
+
+ public synchronized boolean isEchoEnabled() {
+ return echoEnabled;
+ }
+
+ public synchronized void enableEcho() {
+ // Must set these four modes at the same time to make it work fine.
+ setConsoleMode(getConsoleMode() | ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT
+ | ENABLE_PROCESSED_INPUT | ENABLE_WINDOW_INPUT);
+ echoEnabled = true;
+ }
+
+ public synchronized void disableEcho() {
+ // Must set these four modes at the same time to make it work fine.
+ setConsoleMode(getConsoleMode()
+ & ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT
+ | ENABLE_PROCESSED_INPUT | ENABLE_WINDOW_INPUT));
+ echoEnabled = true;
+ }
+
+ public InputStream getDefaultBindings() {
+ return WindowsTerminal.class.getResourceAsStream("windowsbindings.properties");
+ }
+
+ /**
+ * This is awkward and inefficient, but probably the minimal way to add
+ * UTF-8 support to JLine
+ *
+ * @author <a href="mailto:Marc.Herbert@continuent.com">Marc Herbert</a>
+ */
+ static class ReplayPrefixOneCharInputStream extends InputStream {
+ byte firstByte;
+ int byteLength;
+ InputStream wrappedStream;
+ int byteRead;
+
+ final String encoding;
+
+ public ReplayPrefixOneCharInputStream(String encoding) {
+ this.encoding = encoding;
+ }
+
+ public void setInput(int recorded, InputStream wrapped) throws IOException {
+ this.byteRead = 0;
+ this.firstByte = (byte) recorded;
+ this.wrappedStream = wrapped;
+
+ byteLength = 1;
+ if (encoding.equalsIgnoreCase("UTF-8"))
+ setInputUTF8(recorded, wrapped);
+ else if (encoding.equalsIgnoreCase("UTF-16"))
+ byteLength = 2;
+ else if (encoding.equalsIgnoreCase("UTF-32"))
+ byteLength = 4;
+ }
+
+
+ public void setInputUTF8(int recorded, InputStream wrapped) throws IOException {
+ // 110yyyyy 10zzzzzz
+ if ((firstByte & (byte) 0xE0) == (byte) 0xC0)
+ this.byteLength = 2;
+ // 1110xxxx 10yyyyyy 10zzzzzz
+ else if ((firstByte & (byte) 0xF0) == (byte) 0xE0)
+ this.byteLength = 3;
+ // 11110www 10xxxxxx 10yyyyyy 10zzzzzz
+ else if ((firstByte & (byte) 0xF8) == (byte) 0xF0)
+ this.byteLength = 4;
+ else
+ throw new IOException("invalid UTF-8 first byte: " + firstByte);
+ }
+
+ public int read() throws IOException {
+ if (available() == 0)
+ return -1;
+
+ byteRead++;
+
+ if (byteRead == 1)
+ return firstByte;
+
+ return wrappedStream.read();
+ }
+
+ /**
+ * InputStreamReader is greedy and will try to read bytes in advance. We
+ * do NOT want this to happen since we use a temporary/"losing bytes"
+ * InputStreamReader above, that's why we hide the real
+ * wrappedStream.available() here.
+ */
+ public int available() {
+ return byteLength - byteRead;
+ }
+ }
+
+}
diff --git a/src/src/main/java/jline/package.html b/src/src/main/java/jline/package.html
new file mode 100644
index 0000000..c807431
--- /dev/null
+++ b/src/src/main/java/jline/package.html
@@ -0,0 +1,9 @@
+<body>
+<p>
+The core JLine API. The central class is
+<a href="ConsoleReader">jline.ConsoleReader</a>}, which
+is a reader for obtaining input from an arbitrary
+InputStream (usually <em>System.in</em>).
+</p>
+</body>
+
diff --git a/src/src/main/native/Makefile b/src/src/main/native/Makefile
new file mode 100644
index 0000000..c620814
--- /dev/null
+++ b/src/src/main/native/Makefile
@@ -0,0 +1,8 @@
+
+
+#export PATH=${PATH}:/usr/lib/gcc-lib/i686-pc-cygwin/3.3.3
+JDK='/C/Program Files/Java/jdk1.5.0/'
+
+native:
+ #gcc -I'C:/Program Files/Java/'*'/include/' -I'C:/Program Files/Java/'*'/include//win32/' -mno-cygwin -Wl,--add-stdcall-alias -shared -o jline.dll jline_WindowsTerminal.c
+ gcc -L /usr/lib/mingw/ -I${JDK}/include -I${JDK}/include/win32 -mwindows -mno-cygwin -Wl,--add-stdcall-alias -shared -o jline.dll jline_WindowsTerminal.c
diff --git a/src/src/main/native/jline_WindowsTerminal.c b/src/src/main/native/jline_WindowsTerminal.c
new file mode 100644
index 0000000..4e78a66
--- /dev/null
+++ b/src/src/main/native/jline_WindowsTerminal.c
@@ -0,0 +1,57 @@
+#include "jline_WindowsTerminal.h"
+#include <windows.h>
+
+JNIEXPORT jint JNICALL Java_jline_WindowsTerminal_getConsoleMode
+ (JNIEnv *env, jobject ob)
+{
+ DWORD mode;
+ HANDLE hConsole = GetStdHandle (STD_INPUT_HANDLE);
+
+ if (hConsole == INVALID_HANDLE_VALUE)
+ return -1;
+
+ if (!GetConsoleMode (hConsole, &mode))
+ return -1;
+
+ // CloseHandle (hConsole);
+
+ // printf ("JNI get mode=%d\n", mode);
+ return mode;
+}
+
+JNIEXPORT void JNICALL Java_jline_WindowsTerminal_setConsoleMode
+ (JNIEnv *env, jobject ob, jint mode)
+{
+ DWORD m = (DWORD)mode;
+ HANDLE hConsole = GetStdHandle (STD_INPUT_HANDLE);
+
+ if (hConsole == INVALID_HANDLE_VALUE)
+ return;
+
+ // printf ("JNI set mode=%d\n", m);
+ SetConsoleMode (hConsole, m);
+ // CloseHandle (hConsole);
+}
+
+JNIEXPORT jint JNICALL Java_jline_WindowsTerminal_readByte (JNIEnv * env, jclass class)
+{
+ return getch ();
+}
+
+JNIEXPORT jint JNICALL Java_jline_WindowsTerminal_getWindowsTerminalWidth (JNIEnv * env, jclass class)
+{
+ HANDLE inputHandle = GetStdHandle (STD_INPUT_HANDLE);
+ HANDLE outputHandle = GetStdHandle (STD_OUTPUT_HANDLE);
+ PCONSOLE_SCREEN_BUFFER_INFO info = malloc (sizeof (CONSOLE_SCREEN_BUFFER_INFO));
+ GetConsoleScreenBufferInfo (outputHandle, info);
+ return info->srWindow.Right - info->srWindow.Left+1;
+}
+
+JNIEXPORT jint JNICALL Java_jline_WindowsTerminal_getWindowsTerminalHeight (JNIEnv * env, jclass class)
+{
+ HANDLE inputHandle = GetStdHandle (STD_INPUT_HANDLE);
+ HANDLE outputHandle = GetStdHandle (STD_OUTPUT_HANDLE);
+ PCONSOLE_SCREEN_BUFFER_INFO info = malloc (sizeof (CONSOLE_SCREEN_BUFFER_INFO));
+ GetConsoleScreenBufferInfo (outputHandle, info);
+ return info->srWindow.Bottom - info->srWindow.Top+1;
+}
diff --git a/src/src/main/native/jline_WindowsTerminal.h b/src/src/main/native/jline_WindowsTerminal.h
new file mode 100644
index 0000000..5078b93
--- /dev/null
+++ b/src/src/main/native/jline_WindowsTerminal.h
@@ -0,0 +1,68 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class jline_WindowsTerminal */
+
+#ifndef _Included_jline_WindowsTerminal
+#define _Included_jline_WindowsTerminal
+#ifdef __cplusplus
+extern "C" {
+#endif
+/* Inaccessible static: term */
+#undef jline_WindowsTerminal_ENABLE_LINE_INPUT
+#define jline_WindowsTerminal_ENABLE_LINE_INPUT 2L
+#undef jline_WindowsTerminal_ENABLE_ECHO_INPUT
+#define jline_WindowsTerminal_ENABLE_ECHO_INPUT 4L
+#undef jline_WindowsTerminal_ENABLE_PROCESSED_INPUT
+#define jline_WindowsTerminal_ENABLE_PROCESSED_INPUT 1L
+#undef jline_WindowsTerminal_ENABLE_WINDOW_INPUT
+#define jline_WindowsTerminal_ENABLE_WINDOW_INPUT 8L
+#undef jline_WindowsTerminal_ENABLE_MOUSE_INPUT
+#define jline_WindowsTerminal_ENABLE_MOUSE_INPUT 16L
+#undef jline_WindowsTerminal_ENABLE_PROCESSED_OUTPUT
+#define jline_WindowsTerminal_ENABLE_PROCESSED_OUTPUT 1L
+#undef jline_WindowsTerminal_ENABLE_WRAP_AT_EOL_OUTPUT
+#define jline_WindowsTerminal_ENABLE_WRAP_AT_EOL_OUTPUT 2L
+/*
+ * Class: jline_WindowsTerminal
+ * Method: getConsoleMode
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_jline_WindowsTerminal_getConsoleMode
+ (JNIEnv *, jobject);
+
+/*
+ * Class: jline_WindowsTerminal
+ * Method: setConsoleMode
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL Java_jline_WindowsTerminal_setConsoleMode
+ (JNIEnv *, jobject, jint);
+
+/*
+ * Class: jline_WindowsTerminal
+ * Method: readByte
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_jline_WindowsTerminal_readByte
+ (JNIEnv *, jobject);
+
+/*
+ * Class: jline_WindowsTerminal
+ * Method: getWindowsTerminalWidth
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_jline_WindowsTerminal_getWindowsTerminalWidth
+ (JNIEnv *, jobject);
+
+/*
+ * Class: jline_WindowsTerminal
+ * Method: getWindowsTerminalHeight
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_jline_WindowsTerminal_getWindowsTerminalHeight
+ (JNIEnv *, jobject);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/src/src/main/resources/jline/CandidateListCompletionHandler.properties b/src/src/main/resources/jline/CandidateListCompletionHandler.properties
new file mode 100644
index 0000000..18ee221
--- /dev/null
+++ b/src/src/main/resources/jline/CandidateListCompletionHandler.properties
@@ -0,0 +1,5 @@
+display-candidates: Display all {0} possibilities? (y or n)
+display-candidates-yes: y
+display-candidates-no: n
+display-more: --More--
+
diff --git a/src/src/main/resources/jline/jline32.dll b/src/src/main/resources/jline/jline32.dll
new file mode 100644
index 0000000..a0d3b11
--- /dev/null
+++ b/src/src/main/resources/jline/jline32.dll
Binary files differ
diff --git a/src/src/main/resources/jline/jline64.dll b/src/src/main/resources/jline/jline64.dll
new file mode 100644
index 0000000..922d6b0
--- /dev/null
+++ b/src/src/main/resources/jline/jline64.dll
Binary files differ
diff --git a/src/src/main/resources/jline/keybindings-mac.properties b/src/src/main/resources/jline/keybindings-mac.properties
new file mode 100644
index 0000000..6f13615
--- /dev/null
+++ b/src/src/main/resources/jline/keybindings-mac.properties
@@ -0,0 +1,62 @@
+# Keybinding mapping for JLine. The format is:
+# [key code]: [logical operation]
+
+# CTRL-B: move to the previous character
+2: PREV_CHAR
+
+# CTRL-G: move to the previous word
+7: PREV_WORD
+
+# CTRL-F: move to the next character
+6: NEXT_CHAR
+
+# CTRL-A: move to the beginning of the line
+1: MOVE_TO_BEG
+
+# CTRL-D: close out the input stream
+4: EXIT
+
+# CTRL-E: move the cursor to the end of the line
+5: MOVE_TO_END
+
+# BACKSPACE, CTRL-H: delete the previous character
+# 8 is the ASCII code for backspace and therefor
+# deleting the previous character
+8: DELETE_PREV_CHAR
+
+# TAB, CTRL-I: signal that console completion should be attempted
+9: COMPLETE
+
+# CTRL-J, CTRL-M: newline
+10: NEWLINE
+
+# CTRL-K: erase the current line
+11: KILL_LINE
+
+# ENTER: newline
+13: NEWLINE
+
+# CTRL-L: clear screen
+12: CLEAR_SCREEN
+
+# CTRL-N: scroll to the next element in the history buffer
+14: NEXT_HISTORY
+
+# CTRL-P: scroll to the previous element in the history buffer
+16: PREV_HISTORY
+
+# CTRL-R: redraw the current line
+18: REDISPLAY
+
+# CTRL-U: delete all the characters before the cursor position
+21: KILL_LINE_PREV
+
+# CTRL-V: paste the contents of the clipboard (useful for Windows terminal)
+22: PASTE
+
+# CTRL-W: delete the word directly before the cursor
+23: DELETE_PREV_WORD
+
+# DELETE, CTRL-?: delete the previous character
+# 127 is the ASCII code for delete
+127: DELETE_PREV_CHAR
diff --git a/src/src/main/resources/jline/keybindings.properties b/src/src/main/resources/jline/keybindings.properties
new file mode 100644
index 0000000..9585e3a
--- /dev/null
+++ b/src/src/main/resources/jline/keybindings.properties
@@ -0,0 +1,68 @@
+# Keybinding mapping for JLine. The format is:
+# [key code]: [logical operation]
+
+# CTRL-A: move to the beginning of the line
+1: MOVE_TO_BEG
+
+# CTRL-B: move to the previous character
+2: PREV_CHAR
+
+# CTRL-D: close out the input stream
+4: EXIT
+
+# CTRL-E: move the cursor to the end of the line
+5: MOVE_TO_END
+
+# CTRL-F: move to the next character
+6: NEXT_CHAR
+
+# CTRL-G: move to the previous word
+7: ABORT
+
+# BACKSPACE, CTRL-H: delete the previous character
+# 8 is the ASCII code for backspace and therefor
+# deleting the previous character
+8: DELETE_PREV_CHAR
+
+# TAB, CTRL-I: signal that console completion should be attempted
+9: COMPLETE
+
+# CTRL-J, CTRL-M: newline
+10: NEWLINE
+
+# CTRL-K: erase the current line
+11: KILL_LINE
+
+# CTRL-L: clear screen
+12: CLEAR_SCREEN
+
+# ENTER: newline
+13: NEWLINE
+
+# CTRL-N: scroll to the next element in the history buffer
+14: NEXT_HISTORY
+
+# CTRL-P: scroll to the previous element in the history buffer
+16: PREV_HISTORY
+
+# CTRL-R: redraw the current line
+18: SEARCH_PREV
+
+# CTRL-U: delete all the characters before the cursor position
+21: KILL_LINE_PREV
+
+# CTRL-V: paste the contents of the clipboard (useful for Windows terminal)
+22: PASTE
+
+# CTRL-W: delete the word directly before the cursor
+23: DELETE_PREV_WORD
+
+# CTRL-X: temporary location for PREV_WORD to make tests pass
+24: PREV_WORD
+
+# ESCAPE probably not intended this way, but it does the right thing for now
+27: REDISPLAY
+
+# DELETE, CTRL-?: delete the next character
+# 127 is the ASCII code for delete
+127: DELETE_NEXT_CHAR
diff --git a/src/src/main/resources/jline/windowsbindings.properties b/src/src/main/resources/jline/windowsbindings.properties
new file mode 100644
index 0000000..d599c69
--- /dev/null
+++ b/src/src/main/resources/jline/windowsbindings.properties
@@ -0,0 +1,68 @@
+# Keybinding mapping for JLine. The format is:
+# [key code]: [logical operation]
+
+# CTRL-A: move to the beginning of the line
+1: MOVE_TO_BEG
+
+# CTRL-B: move to the previous character
+2: PREV_CHAR
+
+# CTRL-C: toggle overtype mode (frankly, I wasn't sure where to bind this)
+3: INSERT
+
+# CTRL-D: close out the input stream
+4: EXIT
+
+# CTRL-E: move the cursor to the end of the line
+5: MOVE_TO_END
+
+# CTRL-F: move to the next character
+6: NEXT_CHAR
+
+# CTRL-G: move to the previous word
+7: ABORT
+
+# CTRL-H: delete the previous character
+8: DELETE_PREV_CHAR
+
+# TAB, CTRL-I: signal that console completion should be attempted
+9: COMPLETE
+
+# CTRL-J, CTRL-M: newline
+10: NEWLINE
+
+# CTRL-K: Vertical tab - on windows we'll move to the start of the history
+11: START_OF_HISTORY
+
+# CTRL-L: Form feed - on windows, we'll move to the end of the history
+12: END_OF_HISTORY
+
+# ENTER: newline
+13: NEWLINE
+
+# CTRL-N: scroll to the next element in the history buffer
+14: NEXT_HISTORY
+
+# CTRL-P: scroll to the previous element in the history buffer
+16: PREV_HISTORY
+
+# CTRL-R: search backwards in history
+18: SEARCH_PREV
+
+# CTRL-U: delete all the characters before the cursor position
+21: KILL_LINE_PREV
+
+# CTRL-V: paste the contents of the clipboard (useful for Windows terminal)
+22: PASTE
+
+# CTRL-W: delete the word directly before the cursor
+23: DELETE_PREV_WORD
+
+# CTRL-X: temporary location for PREV_WORD to make tests pass
+24: PREV_WORD
+
+# CTRL-[: escape - clear the current line.
+27: CLEAR_LINE
+
+# CTRL-?: delete the previous character
+127: DELETE_NEXT_CHAR
diff --git a/src/src/site/apt/building.apt b/src/src/site/apt/building.apt
new file mode 100644
index 0000000..a091378
--- /dev/null
+++ b/src/src/site/apt/building.apt
@@ -0,0 +1,39 @@
+ ------
+ jline
+ ------
+
+Building JLine
+
+ Building JLine requires an installation of {{{http://maven.apache.org/}Maven 2}}.
+
+ Source code is included in the JLine distribution, or can be checked out from CVS as described {{{source-repository.html}here}}.
+
+Maven Repository
+
+ If you are using Maven 2, you can add JLine as an automatic dependency by adding the following to your project's pom.xml file:
+
++--------------------------------+
+
+ <repositories>
+ ...
+ <repository>
+ <id>jline</id>
+ <name>JLine Project Repository</name>
+ <url>http://jline.sourceforge.net/m2repo</url>
+ </repository>
+ </repositories>
+ <dependencies>
+ ...
+ <dependency>
+ <groupId>jline</groupId>
+ <artifactId>jline</artifactId>
+ <version>0.9.9</version>
+ </dependency>
+ </dependencies>
+
++--------------------------------+
+
+
+
+
+
diff --git a/src/src/site/apt/downloads.apt b/src/src/site/apt/downloads.apt
new file mode 100644
index 0000000..de90db9
--- /dev/null
+++ b/src/src/site/apt/downloads.apt
@@ -0,0 +1,39 @@
+ ------
+ jline
+ ------
+
+Download JLine
+
+ JLine packages can be downloaded from:
+
+ {{{http://sourceforge.net/project/showfiles.php?group_id=64033}http://sourceforge.net/project/showfiles.php?group_id=64033}}
+
+
+Maven Repository
+
+ If you are using Maven 2, you can add JLine as an automatic dependency by adding the following to your project's pom.xml file:
+
++--------------------------------+
+
+ <repositories>
+ ...
+ <repository>
+ <id>jline</id>
+ <name>JLine Project Repository</name>
+ <url>http://jline.sourceforge.net/m2repo</url>
+ </repository>
+ </repositories>
+ <dependencies>
+ ...
+ <dependency>
+ <groupId>jline</groupId>
+ <artifactId>jline</artifactId>
+ <version>0.9.9</version>
+ </dependency>
+ </dependencies>
+
++--------------------------------+
+
+
+
+
diff --git a/src/src/site/docbook/index.xml b/src/src/site/docbook/index.xml
new file mode 100644
index 0000000..c68b42f
--- /dev/null
+++ b/src/src/site/docbook/index.xml
@@ -0,0 +1,492 @@
+<!DOCTYPE book
+ PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.docbook.org/xml/4.1.2/docbookx.dtd">
+
+<book>
+ <bookinfo>
+ <title>
+ JLine
+ </title>
+ <copyright>
+ <year>2002, 2003, 2004, 2005, 2006, 2007</year>
+ <holder>Marc Prud'hommeaux</holder>
+ </copyright>
+ </bookinfo>
+ <part id="manual">
+ <title>JLine Manual</title>
+
+ <chapter id="introduction">
+ <title>Introduction</title>
+ <para>
+ JLine is a Java library for handling console input.
+ It is similar in functionality to BSD editline and GNU
+ readline. People familiar with the readline/editline
+ capabilities for modern shells (such as bash and tcsh) will
+ find most of the command editing features of JLine to
+ be familiar.
+ </para>
+ </chapter>
+
+ <chapter id="license">
+ <title>License and Terms of Use</title>
+ <para>
+ JLine is distributed under the BSD license, meaning that
+ you are completely free to redistribute, modify, or sell it
+ with almost no restrictins.
+ For more information on the BSD license, see
+ <ulink url="http://www.opensource.org/licenses/bsd-license.php"
+ >http://www.opensource.org/licenses/bsd-license.php</ulink>.
+ </para>
+ <para>
+ For information on obtaining the software under another
+ license, contact the copyright holder:
+ <ulink url="mailto:mwp1@cornell.edu">mwp1@cornell.edu</ulink>.
+ </para>
+ </chapter>
+
+ <chapter id="obtaining">
+ <title>Obtaining JLine</title>
+ <para>
+ JLine is hosted on SourceForge, and is located at
+ <ulink url="http://jline.sf.net">http://jline.sf.net</ulink>.
+ The latest release can be downloaded from
+ <ulink url=
+ "http://sourceforge.net/project/showfiles.php?group_id=64033">http://sourceforge.net/project/showfiles.php?group_id=64033</ulink>.
+ API documentation can be found in the
+ <ulink url="apidocs">apidocs/</ulink> directory.
+ </para>
+ </chapter>
+
+ <chapter id="installation">
+ <title>Installation</title>
+ <para>
+ JLine has no library dependencies, aside from a JVM
+ of version 1.2 or higher. To install JLine, download the
+ <filename>jline.jar</filename> file, and either place it in
+ the system-wide java extensions directory, or
+ manually add it to your
+ <computeroutput>CLASSPATH</computeroutput>.
+ The extensions directory is dependent on your operating
+ system. Some few examples are:
+ <itemizedlist>
+ <listitem><para>
+ Macintosh OS X:
+ <filename>/Library/Java/Extensions</filename> or
+ <filename>/System/Library/Java/Extensions</filename>
+ </para></listitem>
+ <listitem><para>
+ Microsoft Windows:
+ <filename>JAVAHOME\jre\lib\ext</filename>
+ (example:
+ <filename>C:\j2sdk1.4.1_03\jre\lib\ext</filename>)
+ </para></listitem>
+ <listitem><para>
+ UNIX Systems:
+ <filename>JAVAHOME/jre/lib/ext</filename>
+ (example:
+ <filename>/usr/local/java/jre/lib/ext</filename>)
+ </para></listitem>
+ </itemizedlist>
+ </para>
+ <para>
+ JLine is not 100% pure Java. On Windows, it relies on a
+ <filename>.dll</filename> file to initialize the terminal
+ to be able to accept unbuffered input. However,
+ no installation is necessary for this: when initialized,
+ JLine will dynamically extract the DLL to a temporary
+ directory and load it. For more details, see the
+ documentation for the <classname>jline.WindowsTerminal</classname> class.
+ </para>
+ <para>
+ On UNIX systems (including Macintosh OS X), JLine will
+ execute the <filename>stty</filename> command to initialize
+ the terminal to allow unbuffered input. For more details,
+ see the documentation for the <classname>jline.UnixTerminal</classname> class.
+ </para>
+ <para>
+ For both Windows and UNIX systems, JLine will fail to
+ initialize if it is run inside a strict security manager
+ that does not allow the loading of libraries, writing
+ to the file system, or executing external programs. However,
+ for most console applications, this is usually not the case.
+ </para>
+ </chapter>
+ <chapter id="supported_platforms">
+ <title>Supported Platforms</title>
+ <para>
+ JLine should work on any Windows system, or any
+ minimally compliant POSIX system (includling Linux and
+ Macintosh OS X).
+ </para>
+ <para>
+ The platforms on which JLine has been confirmed to work are:
+ <itemizedlist>
+ <listitem><para>
+ Microsoft Windows XP
+ </para></listitem>
+ <listitem><para>
+ RedHat Linux 9.0
+ </para></listitem>
+ <listitem><para>
+ Debian Linux 3.0
+ </para></listitem>
+ <listitem><para>
+ Macintosh OS X 10.3
+ </para></listitem>
+ </itemizedlist>
+ </para>
+ <para>
+ Please report successes or failures to the author:
+ <ulink url="mailto:mwp1@cornell.edu">mwp1@cornell.edu</ulink>.
+ </para>
+ </chapter>
+ <chapter id="features">
+ <title>Features</title>
+ <section id="features_history">
+ <title>Command History</title>
+ <para>
+ </para>
+ </section>
+ <section id="features_completion">
+ <title>Tab completion</title>
+ <para>
+ </para>
+ </section>
+ <section id="features_line_editing">
+ <title>Line editing</title>
+ <para>
+ </para>
+ </section>
+ <section id="features_keybindings">
+ <title>Custom Keybindings</title>
+ <para>
+ You can create your own keybindings by creating a
+ <filename>HOME/.jlinebindings.properties"</filename>
+ file. You can override the location of this file with
+ the "<computeroutput>jline.keybindings</computeroutput>"
+ system property.
+ </para>
+ <!--
+ <para>
+ The default keybindings are as follows:
+ <programlisting>
+ </programlisting>
+ </para>
+ -->
+ </section>
+ <section id="features_masking">
+ <title>Character masking</title>
+ <para>
+ </para>
+ </section>
+ </chapter>
+ <chapter id="api">
+ <title>API</title>
+ <para>
+ This section discusses some common usages of the JLine API.
+ For in-depth usage of the JLine API, see the
+ <ulink url="apidocs">apidocs</ulink>.
+ </para>
+ <section id="reading_password">
+ <title>Reading a password from the console</title>
+ <para>
+ A common task that console applications need to do is
+ read in a password. While it is standard for software
+ to not echo password strings as they are typed,
+ the Java core APIs surprisingly do not provide any
+ means to do this.
+ </para>
+ <para>
+ JLine can read a password with the following code:
+ <programlisting>
+String password = new <classname>jline.ConsoleReader</classname>().readLine(new Character('*'));
+ </programlisting>
+ This will replace every character types on the console
+ with a star character.
+ </para>
+ <para>
+ Alternately, you can have it not echo password
+ character at all:
+ <programlisting>
+String password = new <classname>jline.ConsoleReader</classname>().readLine(new Character(0));
+ </programlisting>
+ </para>
+ <para>
+ The <filename>jline-demo.jar</filename> file contains
+ a sample application that reads the password. To run
+ the sample, execute:
+ <programlisting>
+java -cp jline-demo.jar jline.example.PasswordReader "*"
+ </programlisting>
+ </para>
+ </section>
+ </chapter>
+ <chapter id="faq">
+ <title>Frequently Asked Questions</title>
+ <section id="faq_unsupported_platform"><title>
+ Can I disable JLine if it isn't working on my platform?
+ </title>
+ <para>
+ You can disable JLine by setting the System property
+ "<computeroutput>jline.terminal</computeroutput>"
+ to
+ "<classname>jline.UnsupportedTerminal</classname>". For example:
+ <programlisting>
+java -Djline.terminal=jline.UnsupportedTerminal jline.example.Example simple
+ </programlisting>
+ </para>
+ </section>
+ <section id="faq_custom_keybindings"><title>
+ How do I customize the key bindings?
+ </title>
+ <para>
+ You can create your own keybindings by creating a
+ <filename>HOME/.jlinebindings.properties"</filename>
+ file. You can override the location of this file with
+ the "<computeroutput>jline.keybindings</computeroutput>"
+ system property. To examine the format to use, see the
+ <filename>src/jline/keybindings.properties</filename>
+ file in the source distribution.
+ </para>
+ </section>
+ <section id="faq_jline_as_default"><title>
+ Can I use JLine as the default console input stream for
+ all applications?
+ </title>
+ <para>
+ No, but you can use the <classname>jline.ConsoleRunner</classname> application
+ to set up the system input stream and continue on
+ the launch another program. For example, to use JLine
+ as the input handler for the popular
+ <ulink url="http://www.beanshell.org">BeanShell</ulink>
+ console application, you can run:
+ <programlisting>
+java <classname>jline.ConsoleRunner</classname> <ulink url="http://www.beanshell.org/manual/standalonemode.html">bsh.Interpreter</ulink>
+ </programlisting>
+ </para>
+ </section>
+ <section id="faq_jline_beanshell"><title>
+ Can I use JLine as the input handler for <ulink url="http://www.beanshell.org">BeanShell</ulink>?
+ </title>
+ <para>
+ Yes. Try running:
+ <programlisting>
+java <classname>jline.ConsoleRunner</classname> <ulink url="http://www.beanshell.org/manual/standalonemode.html">bsh.Interpreter</ulink>
+ </programlisting>
+ </para>
+ </section>
+ <section id="faq_jline_jdb"><title>
+ Can I use JLine as the input handler for <ulink url="http://java.sun.com/j2se/1.3/docs/tooldocs/solaris/jdb.html">jdb</ulink> (the java debugger)?
+ </title>
+ <para>
+ Yes. Try running:
+ <programlisting>
+java <classname>jline.ConsoleRunner</classname> com.sun.tools.example.debug.tty.TTY <emphasis>args</emphasis>
+ </programlisting>
+ </para>
+ </section>
+ <section id="faq_pure_java"><title>
+ Is JLine <trademark>100% pure Java</trademark>?
+ </title>
+ <para>
+ No: JLine uses a couple small native methods in the Windows
+ platform. On Unix, it is technically pure java, but relies
+ on the execution of external (non-java) programs. See the
+ <link linkend="installation">installation section</link>
+ for more details.
+ </para>
+ </section>
+ <section id="faq_password"><title>
+ How do I make it so password characters are no echoed
+ to the screen?
+ </title>
+ <para>
+ See <link linkend="reading_password"/>.
+ </para>
+ </section>
+ <section id="faq_cursrs"><title>
+ Is JLine a full-featured curses implementation?
+ </title>
+ <para>
+ No: JLine has no ability to position the cursor on the
+ console. It might someday evolve into a plausible
+ Java curses implementation.
+ </para>
+ </section>
+ </chapter>
+ </part>
+
+ <appendix id="known_bugs">
+ <title>Known Bugs</title>
+ <itemizedlist>
+ <listitem><para>
+ Clearing the screen (CTRL-L) doesn't currently work on Windows.
+ </para></listitem>
+ </itemizedlist>
+ </appendix>
+
+ <appendix id="contributors">
+ <title>Contributors</title>
+ <para>
+ The following people have contributed to improving JLine over the
+ years:
+ </para>
+ <itemizedlist>
+ <listitem><para>
+ Marc Prud'hommeaux
+ </para></listitem>
+ <listitem><para>
+ Damian Steer
+ </para></listitem>
+ <listitem><para>
+ Dale Kemp
+ </para></listitem>
+ <listitem><para>
+ Jun Liu
+ </para></listitem>
+ <listitem><para>
+ malcolm@epcc.ed.ac.uk
+ </para></listitem>
+ <listitem><para>
+ Simon Patarin
+ </para></listitem>
+ <listitem><para>
+ Amy Isard
+ </para></listitem>
+ <listitem><para>
+ Ryan Bell
+ </para></listitem>
+ <listitem><para>
+ Marc Herbert
+ </para></listitem>
+ <listitem><para>
+ Christian Salm
+ </para></listitem>
+ </itemizedlist>
+ </appendix>
+
+ <appendix id="todo">
+ <title>Future enhancements</title>
+ <itemizedlist>
+ <listitem><para>
+ Add localization for all strings.
+ </para></listitem>
+ <listitem><para>
+ Create a BNFCompletor that can handle any BNF.
+ </para></listitem>
+ </itemizedlist>
+ </appendix>
+
+ <appendix id="changelog">
+ <title>Change Log</title>
+ <itemizedlist>
+ <title>0.9.93 2007-11-13</title>
+ <listitem><para>
+ Fixed backspace handling on Unix/OS X.
+ </para></listitem>
+ </itemizedlist>
+ <itemizedlist>
+ <title>0.9.92 2007-10-30</title>
+ <listitem><para>
+ JLine now works with 64-bit Windows systems.
+ </para></listitem>
+ </itemizedlist>
+ <itemizedlist>
+ <title>0.9.91 2007-03-11</title>
+ <listitem><para>
+ Added ConsoleReader.setUsePagination() method which allows
+ configuration of pagination when the number of rows of
+ candidates exceeds the height of the detected terminal, thanks
+ to a patch by Damian Steer.
+ </para></listitem>
+ <listitem><para>
+ Better support for UTF-8 inputs (issue #1623521).
+ </para></listitem>
+ <listitem><para>
+ Improved list of supported keys on Windows (issue #1649790).
+ </para></listitem>
+ </itemizedlist>
+ <itemizedlist>
+ <title>0.9.5 2006-03-08</title>
+ <listitem><para>
+ Fixed problem with "stty" on Solaris, which doesn't
+ understand "stty size" to query the terminal size. It now
+ uses "stty -a", which supposedly outputs a POSIX standard
+ format.
+ </para></listitem>
+ <listitem><para>
+ Support HOME and END keys, thanks to a patch by
+ Dale Kemp.
+ </para></listitem>
+ </itemizedlist>
+ <itemizedlist>
+ <title>0.9.1 2005-01-29</title>
+ <listitem><para>
+ Fixed problem with the 0.9.0 distribution that
+ failed to include the Windows jline.dll in the jline.jar,
+ rendering it inoperable on Windows.
+ </para></listitem>
+ <listitem><para>
+ Implemented proper interception or arrow keys on Windows,
+ meaning that history can now be navigated with the UP
+ and DOWN keys, and line editing can take place with
+ the LEFT and RIGHT arrow keys.
+ </para></listitem>
+ </itemizedlist>
+ <itemizedlist>
+ <title>0.9.0 2005-01-23</title>
+ <listitem><para>
+ Changed license from GPL to BSD.
+ </para></listitem>
+ <listitem><para>
+ Made "CTRL-L" map to clearing the screen.
+ </para></listitem>
+ </itemizedlist>
+ <itemizedlist>
+ <title>0.8.1 2003-11-18</title>
+ <listitem><para>
+ Fixed accidental dependency on JVM 1.4.
+ </para></listitem>
+ </itemizedlist>
+ <itemizedlist>
+ <title>0.8.0 2003-11-17</title>
+ <listitem><para>
+ Windows support using a native .dll
+ </para></listitem>
+ <listitem><para>
+ A new ClassNameCompletor
+ </para></listitem>
+ <listitem><para>
+ Many doc improvements
+ </para></listitem>
+ </itemizedlist>
+ <itemizedlist>
+ <title>0.6.0 2003-07-08</title>
+ <listitem><para>
+ Many bugfixes
+ </para></listitem>
+ <listitem><para>
+ Better release system
+ </para></listitem>
+ <listitem><para>
+ Automatically set terminal property by
+ issuing stty on UNIX systems
+ </para></listitem>
+ <listitem><para>
+ Additional tab-completion handlers
+ </para></listitem>
+ <listitem><para>
+ Tested on Debian Linux and Mac OS 10.2
+ </para></listitem>
+ <listitem><para>
+ Example includes dictionary, filename, and simple completion
+ </para></listitem>
+ </itemizedlist>
+ <itemizedlist>
+ <title>0.3.0 2002-10-05</title>
+ <listitem><para>
+ Initial release
+ </para></listitem>
+ </itemizedlist>
+ </appendix>
+</book>
diff --git a/src/src/site/fml/faq.fml b/src/src/site/fml/faq.fml
new file mode 100644
index 0000000..b9902b0
--- /dev/null
+++ b/src/src/site/fml/faq.fml
@@ -0,0 +1,26 @@
+ <?xml version="1.0"?>
+<faqs title="JLine FAQ">
+ <part id="general">
+ <faq>
+ <question>How does JLine work?</question>
+ <answer>
+ <p>
+ On Windows, JLine uses a native .dll (which it automatically
+ extracts from the jline jar and loads at runtime) to access
+ native console features. On UNIX systems, JLine will perform
+ the necessary terminal setup by launching the "stty" command.
+ </p>
+ </answer>
+ </faq>
+ <faq>
+ <question>What platforms has JLine been tested on?</question>
+ <answer>
+ <p>
+ Various flavors of Windows (95, 98, NT, XP, 2000), Mac OS X,
+ and multiple UNIX systems (Linux, Solaris, HPUX).
+ </p>
+ </answer>
+ </faq>
+
+ </part>
+</faqs>
diff --git a/src/src/site/resources/css/site.css b/src/src/site/resources/css/site.css
new file mode 100755
index 0000000..771dc3f
--- /dev/null
+++ b/src/src/site/resources/css/site.css
@@ -0,0 +1,311 @@
+
+body {
+ min-width: 600px;
+ width: 600px;
+ width: auto !important;
+ background-color: #fff;
+ font-family: Verdana, sans;
+}
+
+body,div,span,td,p,h2,h3,h4,h5,h6,a,ul,li {
+ font-family: Verdana, sans;
+ font-size: 11px;
+ color: #5A5A5A;
+ font-style: normal;
+}
+
+a,a:hover,a:visited,a:active {
+ color: #5A5A5A;
+ /* text-decoration: underline; */
+}
+
+/* main layout */
+#banner {
+ color: #FFA500;
+ border: none;
+ margin: 0 0 0 0;
+ background-color: #fff;
+ background-image: url(../images/header.jpg);
+ background-position: right;
+ background-repeat: no-repeat;
+ height: 100px;
+}
+
+#bannerLeft img{
+ margin: 10px 0 0 10px;
+}
+
+#leftColumn {
+ background-color: transparent;
+ position: absolute;
+ top: 140px;
+ left: 20px;
+ width: 180px;
+ margin: 0px;
+ padding: 0px;
+ border: none;
+}
+
+#bodyColumn {
+ margin: 0 0 20px 220px;
+ background-color: #fff;
+ padding: 30px;
+ position: relative;
+ background-image: url(../images/dotted.png);
+ background-repeat: repeat-y;
+}
+
+#footer div.xright {
+ color: #fff;
+ margin-right: 10px;
+}
+
+/* end main layout */
+.deprecated {
+ text-decoration: line-through;
+}
+
+.comment {
+ color: green;
+}
+
+.source pre {
+ font-family: "Andale Mono", monospace;
+ font-size: 11px;
+ background-color: #ddd;
+ width: 100%;
+ color: #5A5A5A;
+ border-width: 0px;
+ padding-top: 6px;
+ padding-left: 3px;
+}
+
+#breadcrumbs {
+ background-color: #FE1100;
+ border: none;
+ height: 15px;
+}
+
+/*
+ workaround for bug in the Doxia docbook renderer that opens
+ anchors (e.g. <a name="index">), but doesn't ever close them
+*/
+#section a,a:hover,a:visited,a:active {
+ text-decoration: none;
+}
+
+
+#breadcrumbs a {
+ color: #fff;
+ margin-left: 20px;
+ text-decoration: none;
+}
+
+h1 {
+ border: none;
+ padding-left: 0;
+ font-weight: bold;
+ text-transform: capitalize;
+ background-color: #7FAABB !important;
+ color: #FFFFFF !important;
+ font-size: 19px !important;
+}
+
+h2 {
+ border: none;
+ padding-left: 0;
+ font-size: 13px;
+ font-weight: bold;
+ text-transform: capitalize;
+ background-color: #7FAABB !important;
+ color: #FFFFFF !important;
+ font-size: 17px !important;
+}
+
+h3 {
+ border: none;
+ font-weight: bolder;
+ padding-left: 0;
+ background-color: #8BBBD1 !important;
+ color: #FFFFFF !important;
+}
+
+#navcolumn {
+ padding: 0;
+ overflow: hidden;
+}
+
+#navcolumn ul {
+ margin: 0px 0 3px 0;
+ background-repeat: repeat-x;
+}
+
+#navcolumn h5 {
+ border: none;
+ background-image: url(../images/dotted.png);
+ background-repeat: repeat-x;
+ padding: 4px 0 3px 20px;
+ font-size: 11px !important;
+ margin-top: -1px;
+}
+
+#navcolumn ul {
+ margin-bottom: 8px;
+}
+
+#navcolumn li {
+ margin: 0px 0 0px 3px;
+ padding: 2px;
+ list-style-position: outside;
+ font-size: 7.5pt !important;
+ padding-left: 16px;
+ padding-left /**/: 2px !important;
+ /* warning, don't reformat, there should be no comment between padding-left and comment, to fix IE5 issues */
+}
+
+#menuDownloadable_docs li {
+ background-image: url(../images/ico_file_pdf.png);
+ padding-top: 3px;
+ padding-bottom: 1px;
+}
+
+#navcolumn strong {
+ color: #000000;
+ font-weight: bold;
+}
+
+#navcolumn strong a {
+ color: #000000;
+ font-weight: bold;
+}
+
+#navcolumn a {
+ padding-left: 14px;
+ text-decoration: underline;
+ padding-bottom: 2px;
+ color: #5a5a5a;
+}
+
+#navcolumn a img {
+ margin-top: 0;
+}
+
+#navcolumn a#poweredBy img {
+ margin: 0 0 15px 20px;
+ width: 90px;
+ height: 30px;
+}
+
+#navcolumn #lastPublished {
+ color: #999;
+ margin: 0 0 0 20px;
+}
+
+#navcolumn a:hover {
+ color: Olive;
+ padding-left: 14px;
+ text-decoration: underline;
+ padding-bottom: 2px;
+}
+
+#breadcrumbs div.xright,#breadcrumbs div.xleft {
+ color: #fff;
+ display: inline;
+ font-size: 7pt !important;
+}
+
+#banner a#projectLogo img {
+ float: left;
+ background-color: #fff !important;
+ margin: 20px 0 0 20px !important;
+}
+
+#navcolumn li {
+ color: #000000;
+}
+
+#navcolumn strong {
+ color: #000000;
+ font-weight: bold;
+ margin-left: 15px;
+}
+
+div.source {
+ background-color: #ddd;
+}
+
+div.source pre,code,td.code {
+ font-size: 8pt !important;
+ font-family: monospace;
+ margin: 0;
+}
+
+td.code {
+ font-size: 10pt !important;
+ font-family: monospace;
+}
+
+div#legend {
+ display: none;
+}
+
+table td.source {
+ border: none !important;
+}
+
+table td,table th {
+ font-size: 8pt !important;
+ font-family: verdana;
+}
+
+table th {
+ font-weight: bold;
+}
+
+.collapsed {
+ background-image: url(../images/collapsed.png) !important;
+}
+
+li.expanded {
+ background-image: url(../images/expanded.png) !important;
+}
+
+/*
+li.expanded ul {
+ margin-top: 5px !important;
+}
+*/
+
+a.externalLink,a.newWindow {
+ padding-right: 9px !important;
+ background-image: none !important; /*ie5*/
+}
+
+a.externalLink /* */ {
+ background-image: url(../images/external.png) !important;
+}
+
+a.newWindow /* */ {
+ background-image: url(../images/newwindow.png) !important;
+}
+
+table {
+ width: 68%; /* fix for ie 5.x */
+}
+
+i {
+ content: "\"/*"
+}
+
+table {
+ width: 100%;
+}
+/* remove banner: comment the following lines for the full layout */ /*
+#banner, #breadcrumbs {
+ display: none !important;
+}
+#leftColumn {
+ position: relative;
+ top: 0;
+}
+*/
diff --git a/src/src/site/resources/images/collapsed.png b/src/src/site/resources/images/collapsed.png
new file mode 100755
index 0000000..a02c1e6
--- /dev/null
+++ b/src/src/site/resources/images/collapsed.png
Binary files differ
diff --git a/src/src/site/resources/images/dotted.png b/src/src/site/resources/images/dotted.png
new file mode 100755
index 0000000..8a4d443
--- /dev/null
+++ b/src/src/site/resources/images/dotted.png
Binary files differ
diff --git a/src/src/site/resources/images/expanded.png b/src/src/site/resources/images/expanded.png
new file mode 100755
index 0000000..8a19dbf
--- /dev/null
+++ b/src/src/site/resources/images/expanded.png
Binary files differ
diff --git a/src/src/site/resources/images/external.png b/src/src/site/resources/images/external.png
new file mode 100755
index 0000000..19f2895
--- /dev/null
+++ b/src/src/site/resources/images/external.png
Binary files differ
diff --git a/src/src/site/resources/images/ico_file_pdf.png b/src/src/site/resources/images/ico_file_pdf.png
new file mode 100644
index 0000000..9ceb00f
--- /dev/null
+++ b/src/src/site/resources/images/ico_file_pdf.png
Binary files differ
diff --git a/src/src/site/resources/images/logo.jpg b/src/src/site/resources/images/logo.jpg
new file mode 100644
index 0000000..1f1da5c
--- /dev/null
+++ b/src/src/site/resources/images/logo.jpg
Binary files differ
diff --git a/src/src/site/resources/images/newwindow.png b/src/src/site/resources/images/newwindow.png
new file mode 100755
index 0000000..1374c22
--- /dev/null
+++ b/src/src/site/resources/images/newwindow.png
Binary files differ
diff --git a/src/src/site/site.xml b/src/src/site/site.xml
new file mode 100644
index 0000000..274ec83
--- /dev/null
+++ b/src/src/site/site.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<project name="JLine">
+ <bannerLeft>
+ <name>JLine</name>
+ <src>images/logo.jpg</src>
+ <href>http://jline.sourceforge.net/</href>
+ </bannerLeft>
+ <bannerRight>
+ <name>SourceForge</name>
+ <src>http://sourceforge.net/sflogo.php?group_id=64033</src>
+ <href>http://sourceforge.net/</href>
+ </bannerRight>
+ <body>
+ <menu name="JLine">
+ <item name="Manual" href="index.html"/>
+ <item name="FAQs" href="faq.html"/>
+ <item name="Download" href="downloads.html"/>
+ <item name="Building" href="building.html"/>
+ </menu>
+
+ <menu name="Community">
+ <item name="Forums"
+ href="http://sourceforge.net/forum/?group_id=64033"/>
+ <item name="Issue Tracker"
+ href="http://sourceforge.net/tracker/?group_id=64033"/>
+ <item name="News"
+ href="http://sourceforge.net/news/?group_id=64033"/>
+ </menu>
+
+ <menu name="Project">
+ <item name="Javadocs"
+ href="apidocs/index.html"/>
+ <item name="Browse Source Code"
+ href="http://jline.cvs.sourceforge.net/jline/"/>
+ </menu>
+
+ ${reports}
+
+ </body>
+</project>
diff --git a/src/src/test/java/jline/ConsoleReaderTest.java b/src/src/test/java/jline/ConsoleReaderTest.java
new file mode 100644
index 0000000..4a25783
--- /dev/null
+++ b/src/src/test/java/jline/ConsoleReaderTest.java
@@ -0,0 +1,162 @@
+package jline;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.io.Writer;
+
+import junit.framework.TestCase;
+
+public class ConsoleReaderTest extends TestCase {
+
+ public ConsoleReaderTest(String name) {
+ super(name);
+ }
+
+ protected void setUp() throws Exception {
+ System.setProperty("jline.WindowsTerminal.directConsole", "false");
+ }
+
+ public void testDeleteAndBackspaceKeymappings() throws Exception {
+ // test only works on Windows
+ if (!(Terminal.getTerminal() instanceof WindowsTerminal))
+ return;
+
+ ConsoleReader consoleReader = new ConsoleReader();
+ assertNotNull(consoleReader);
+ assertEquals(127, consoleReader
+ .getKeyForAction(ConsoleReader.DELETE_NEXT_CHAR));
+ assertEquals(8, consoleReader
+ .getKeyForAction(ConsoleReader.DELETE_PREV_CHAR));
+ }
+
+ public void testReadline() throws Exception {
+ ConsoleReader consoleReader = createConsole("Sample String\r\n"
+ .getBytes());
+ assertNotNull(consoleReader);
+ String line = consoleReader.readLine();
+ assertEquals("Sample String", line);
+
+ }
+
+ public void testDeleteOnWindowsTerminal() throws Exception {
+ // test only works on Windows
+ if (!(Terminal.getTerminal() instanceof WindowsTerminal))
+ return;
+
+ char[] characters = new char[] { 'S', 's',
+ WindowsTerminal.SPECIAL_KEY_INDICATOR,
+ WindowsTerminal.LEFT_ARROW_KEY,
+ WindowsTerminal.SPECIAL_KEY_INDICATOR,
+ WindowsTerminal.DELETE_KEY, '\r', 'n' };
+ assertWindowsKeyBehavior("S", characters);
+ }
+
+ public void testNumpadDeleteOnWindowsTerminal() throws Exception {
+ // test only works on Windows
+ if (!(Terminal.getTerminal() instanceof WindowsTerminal))
+ return;
+
+ char[] characters = new char[] { 'S', 's',
+ WindowsTerminal.NUMPAD_KEY_INDICATOR,
+ WindowsTerminal.LEFT_ARROW_KEY,
+ WindowsTerminal.NUMPAD_KEY_INDICATOR,
+ WindowsTerminal.DELETE_KEY, '\r', 'n' };
+ assertWindowsKeyBehavior("S", characters);
+ }
+
+ public void testHomeKeyOnWindowsTerminal() throws Exception {
+ // test only works on Windows
+ if (!(Terminal.getTerminal() instanceof WindowsTerminal))
+ return;
+
+ char[] characters = new char[] { 'S', 's',
+ WindowsTerminal.SPECIAL_KEY_INDICATOR,
+ WindowsTerminal.HOME_KEY, 'x', '\r', '\n' };
+ assertWindowsKeyBehavior("xSs", characters);
+
+ }
+
+ public void testEndKeyOnWindowsTerminal() throws Exception {
+ // test only works on Windows
+ if (!(Terminal.getTerminal() instanceof WindowsTerminal))
+ return;
+
+ char[] characters = new char[] { 'S', 's',
+ WindowsTerminal.SPECIAL_KEY_INDICATOR,
+ WindowsTerminal.HOME_KEY, 'x',
+ WindowsTerminal.SPECIAL_KEY_INDICATOR, WindowsTerminal.END_KEY,
+ 'j', '\r', '\n' };
+ assertWindowsKeyBehavior("xSsj", characters);
+ }
+
+ public void testPageUpOnWindowsTerminal() throws Exception {
+ // test only works on Windows
+ if (!(Terminal.getTerminal() instanceof WindowsTerminal))
+ return;
+
+ char[] characters = new char[] { WindowsTerminal.SPECIAL_KEY_INDICATOR,
+ WindowsTerminal.PAGE_UP_KEY, '\r', '\n' };
+ assertWindowsKeyBehavior("dir", characters);
+ }
+
+ public void testPageDownOnWindowsTerminal() throws Exception {
+ // test only works on Windows
+ if (!(Terminal.getTerminal() instanceof WindowsTerminal))
+ return;
+
+ char[] characters = new char[] { WindowsTerminal.SPECIAL_KEY_INDICATOR,
+ WindowsTerminal.PAGE_DOWN_KEY, '\r', '\n' };
+ assertWindowsKeyBehavior("mkdir monkey", characters);
+ }
+
+ public void testEscapeOnWindowsTerminal() throws Exception {
+ // test only works on Windows
+ if (!(Terminal.getTerminal() instanceof WindowsTerminal))
+ return;
+
+ char[] characters = new char[] { 's', 's', 's',
+ WindowsTerminal.SPECIAL_KEY_INDICATOR,
+ WindowsTerminal.ESCAPE_KEY, '\r', '\n' };
+ assertWindowsKeyBehavior("", characters);
+ }
+
+ public void testInsertOnWindowsTerminal() throws Exception {
+ // test only works on Windows
+ if (!(Terminal.getTerminal() instanceof WindowsTerminal))
+ return;
+
+ char[] characters = new char[] { 'o', 'p', 's',
+ WindowsTerminal.SPECIAL_KEY_INDICATOR,
+ WindowsTerminal.HOME_KEY,
+ WindowsTerminal.SPECIAL_KEY_INDICATOR,
+ WindowsTerminal.INSERT_KEY, 'o', 'o', 'p', 's', '\r', '\n' };
+ assertWindowsKeyBehavior("oops", characters);
+ }
+
+ private void assertWindowsKeyBehavior(String expected, char[] input)
+ throws Exception {
+ StringBuffer buffer = new StringBuffer();
+ buffer.append(input);
+ ConsoleReader reader = createConsole(buffer.toString().getBytes());
+ assertNotNull(reader);
+ String line = reader.readLine();
+ assertEquals(expected, line);
+ }
+
+ private ConsoleReader createConsole(byte[] bytes) throws Exception {
+ InputStream in = new ByteArrayInputStream(bytes);
+ Writer writer = new StringWriter();
+ ConsoleReader reader = new ConsoleReader(in, writer);
+ reader.setHistory(createSeededHistory());
+ return reader;
+ }
+
+ private History createSeededHistory() {
+ History history = new History();
+ history.addToHistory("dir");
+ history.addToHistory("cd c:\\");
+ history.addToHistory("mkdir monkey");
+ return history;
+ }
+}
diff --git a/src/src/test/java/jline/JLineTestCase.java b/src/src/test/java/jline/JLineTestCase.java
new file mode 100644
index 0000000..92e09d4
--- /dev/null
+++ b/src/src/test/java/jline/JLineTestCase.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import junit.framework.*;
+
+import java.io.*;
+
+public abstract class JLineTestCase extends TestCase {
+ ConsoleReader console;
+
+ public JLineTestCase(String test) {
+ super(test);
+ }
+
+ public void setUp() throws Exception {
+ super.setUp();
+ console = new ConsoleReader(null, new PrintWriter(
+ new OutputStreamWriter(new ByteArrayOutputStream())), null,
+ new UnixTerminal());
+ }
+
+ public void assertBuffer(String expected, Buffer buffer) throws IOException {
+ assertBuffer(expected, buffer, true);
+ }
+
+ public void assertBuffer(String expected, Buffer buffer, boolean clear)
+ throws IOException {
+ // clear current buffer, if any
+ if (clear) {
+ console.finishBuffer();
+ console.getHistory().clear();
+ }
+
+ console.setInput(new ByteArrayInputStream(buffer.getBytes()));
+
+ // run it through the reader
+ while (console.readLine((String) null) != null) {
+ ;
+ }
+
+ assertEquals(expected, console.getCursorBuffer().toString());
+ }
+
+ private int getKeyForAction(short logicalAction) {
+ int action = console.getKeyForAction(logicalAction);
+
+ if (action == -1) {
+ fail("Keystroke for logical action " + logicalAction
+ + " was not bound in the console");
+ }
+
+ return action;
+ }
+
+ /**
+ * TODO: Fix this so tests don't break on windows machines.
+ *
+ * @author Ryan
+ *
+ */
+ class Buffer {
+ private final ByteArrayOutputStream bout = new ByteArrayOutputStream();
+
+ public Buffer() {
+ }
+
+ public Buffer(String str) {
+ append(str);
+ }
+
+ public byte[] getBytes() {
+ return bout.toByteArray();
+ }
+
+ public Buffer op(short operation) {
+ return append(getKeyForAction(operation));
+ }
+
+ public Buffer ctrlA() {
+ return append(getKeyForAction(ConsoleReader.MOVE_TO_BEG));
+ }
+
+ public Buffer ctrlU() {
+ return append(getKeyForAction(ConsoleReader.KILL_LINE_PREV));
+ }
+
+ public Buffer tab() {
+ return append(getKeyForAction(ConsoleReader.COMPLETE));
+ }
+
+ public Buffer back() {
+ return append(getKeyForAction(ConsoleReader.DELETE_PREV_CHAR));
+ }
+
+ public Buffer left() {
+ return append(UnixTerminal.ARROW_START).append(
+ UnixTerminal.ARROW_PREFIX).append(UnixTerminal.ARROW_LEFT);
+ }
+
+ public Buffer right() {
+ return append(UnixTerminal.ARROW_START).append(
+ UnixTerminal.ARROW_PREFIX).append(UnixTerminal.ARROW_RIGHT);
+ }
+
+ public Buffer up() {
+ return append(UnixTerminal.ARROW_START).append(
+ UnixTerminal.ARROW_PREFIX).append(UnixTerminal.ARROW_UP);
+ }
+
+ public Buffer down() {
+ return append(UnixTerminal.ARROW_START).append(
+ UnixTerminal.ARROW_PREFIX).append(UnixTerminal.ARROW_DOWN);
+ }
+
+ public Buffer append(String str) {
+ byte[] bytes = str.getBytes();
+
+ for (int i = 0; i < bytes.length; i++) {
+ append(bytes[i]);
+ }
+
+ return this;
+ }
+
+ public Buffer append(int i) {
+ return append((byte) i);
+ }
+
+ public Buffer append(byte b) {
+ bout.write(b);
+
+ return this;
+ }
+ }
+}
diff --git a/src/src/test/java/jline/TestCompletion.java b/src/src/test/java/jline/TestCompletion.java
new file mode 100644
index 0000000..a481e26
--- /dev/null
+++ b/src/src/test/java/jline/TestCompletion.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+import java.util.*;
+
+/**
+ * Tests command history.
+ *
+ * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public class TestCompletion extends JLineTestCase {
+ public TestCompletion(String test) {
+ super(test);
+ }
+
+ public void testSimpleCompletor() throws Exception {
+ // clear any current completors
+ for (Iterator i = console.getCompletors().iterator(); i.hasNext();
+ console.removeCompletor((Completor) i.next())) {
+ ;
+ }
+
+ console.addCompletor
+ (new SimpleCompletor(new String[] { "foo", "bar", "baz" }));
+
+ assertBuffer("foo ", new Buffer("f").op(ConsoleReader.COMPLETE));
+ // single tab completes to unabbiguous "ba"
+ assertBuffer("ba", new Buffer("b").op(ConsoleReader.COMPLETE));
+ assertBuffer("ba", new Buffer("ba").op(ConsoleReader.COMPLETE));
+ assertBuffer("baz ", new Buffer("baz").op(ConsoleReader.COMPLETE));
+ }
+
+ public void testArgumentCompletor() throws Exception {
+ // clear any current completors
+ for (Iterator i = console.getCompletors().iterator(); i.hasNext();
+ console.removeCompletor((Completor) i.next())) {
+ ;
+ }
+
+ console.addCompletor(new ArgumentCompletor
+ (new SimpleCompletor(new String[] { "foo", "bar", "baz" })));
+
+ assertBuffer("foo foo ", new Buffer("foo f").
+ op(ConsoleReader.COMPLETE));
+ assertBuffer("foo ba", new Buffer("foo b").
+ op(ConsoleReader.COMPLETE));
+ assertBuffer("foo ba", new Buffer("foo ba").
+ op(ConsoleReader.COMPLETE));
+ assertBuffer("foo baz ", new Buffer("foo baz").
+ op(ConsoleReader.COMPLETE));
+
+ // test completion in the mid range
+ assertBuffer("foo baz",
+ new Buffer("f baz").left().left().left().left().
+ op(ConsoleReader.COMPLETE));
+ assertBuffer("ba foo",
+ new Buffer("b foo").left().left().left().left().
+ op(ConsoleReader.COMPLETE));
+ assertBuffer("foo ba baz",
+ new Buffer("foo b baz").left().left().left().left().
+ op(ConsoleReader.COMPLETE));
+ assertBuffer("foo foo baz",
+ new Buffer("foo f baz").left().left().left().left().
+ op(ConsoleReader.COMPLETE));
+ }
+}
diff --git a/src/src/test/java/jline/TestEditLine.java b/src/src/test/java/jline/TestEditLine.java
new file mode 100644
index 0000000..88b4524
--- /dev/null
+++ b/src/src/test/java/jline/TestEditLine.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+
+/**
+ * Tests various features of editing lines.
+ *
+ * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public class TestEditLine extends JLineTestCase {
+ public TestEditLine(String test) {
+ super(test);
+ }
+
+ public void testDeletePreviousWord() throws Exception {
+ Buffer b = new Buffer("This is a test");
+
+ assertBuffer("This is a ", b = b.op(ConsoleReader.DELETE_PREV_WORD));
+ assertBuffer("This is ", b = b.op(ConsoleReader.DELETE_PREV_WORD));
+ assertBuffer("This ", b = b.op(ConsoleReader.DELETE_PREV_WORD));
+ assertBuffer("", b = b.op(ConsoleReader.DELETE_PREV_WORD));
+ assertBuffer("", b = b.op(ConsoleReader.DELETE_PREV_WORD));
+ assertBuffer("", b = b.op(ConsoleReader.DELETE_PREV_WORD));
+ }
+
+ public void testMoveToEnd() throws Exception {
+ Buffer b = new Buffer("This is a test");
+
+ assertBuffer("This is a XtestX",
+ new Buffer("This is a test").op(ConsoleReader.PREV_WORD)
+ .append('X')
+ .op(ConsoleReader.MOVE_TO_END)
+ .append('X'));
+
+ assertBuffer("This is Xa testX",
+ new Buffer("This is a test").op(ConsoleReader.PREV_WORD)
+ .op(ConsoleReader.PREV_WORD)
+ .append('X')
+ .op(ConsoleReader.MOVE_TO_END)
+ .append('X'));
+
+ assertBuffer("This Xis a testX",
+ new Buffer("This is a test").op(ConsoleReader.PREV_WORD)
+ .op(ConsoleReader.PREV_WORD)
+ .op(ConsoleReader.PREV_WORD)
+ .append('X')
+ .op(ConsoleReader.MOVE_TO_END)
+ .append('X'));
+ }
+
+ public void testPreviousWord() throws Exception {
+ assertBuffer("This is a Xtest",
+ new Buffer("This is a test").op(ConsoleReader.PREV_WORD)
+ .append('X'));
+ assertBuffer("This is Xa test",
+ new Buffer("This is a test").op(ConsoleReader.PREV_WORD)
+ .op(ConsoleReader.PREV_WORD)
+ .append('X'));
+ assertBuffer("This Xis a test",
+ new Buffer("This is a test").op(ConsoleReader.PREV_WORD)
+ .op(ConsoleReader.PREV_WORD)
+ .op(ConsoleReader.PREV_WORD)
+ .append('X'));
+ assertBuffer("XThis is a test",
+ new Buffer("This is a test").op(ConsoleReader.PREV_WORD)
+ .op(ConsoleReader.PREV_WORD)
+ .op(ConsoleReader.PREV_WORD)
+ .op(ConsoleReader.PREV_WORD)
+ .append('X'));
+ assertBuffer("XThis is a test",
+ new Buffer("This is a test").op(ConsoleReader.PREV_WORD)
+ .op(ConsoleReader.PREV_WORD)
+ .op(ConsoleReader.PREV_WORD)
+ .op(ConsoleReader.PREV_WORD)
+ .op(ConsoleReader.PREV_WORD)
+ .append('X'));
+ assertBuffer("XThis is a test",
+ new Buffer("This is a test").op(ConsoleReader.PREV_WORD)
+ .op(ConsoleReader.PREV_WORD)
+ .op(ConsoleReader.PREV_WORD)
+ .op(ConsoleReader.PREV_WORD)
+ .op(ConsoleReader.PREV_WORD)
+ .op(ConsoleReader.PREV_WORD)
+ .append('X'));
+ }
+
+ public void testLineStart() throws Exception {
+ assertBuffer("XThis is a test",
+ new Buffer("This is a test").ctrlA().append('X'));
+ assertBuffer("TXhis is a test",
+ new Buffer("This is a test").ctrlA().right().append('X'));
+ }
+
+ public void testClearLine() throws Exception {
+ assertBuffer("", new Buffer("This is a test").ctrlU());
+ assertBuffer("t", new Buffer("This is a test").left().ctrlU());
+ assertBuffer("st", new Buffer("This is a test").left().left().ctrlU());
+ }
+
+ public void testRight() throws Exception {
+ Buffer b = new Buffer("This is a test");
+ b = b.left().right().back();
+ assertBuffer("This is a tes", b);
+ b = b.left().left().left().right().left().back();
+ assertBuffer("This is ates", b);
+ b.append('X');
+ assertBuffer("This is aXtes", b);
+ }
+
+ public void testLeft() throws Exception {
+ Buffer b = new Buffer("This is a test");
+ b = b.left().left().left();
+ assertBuffer("This is a est", b = b.back());
+ assertBuffer("This is aest", b = b.back());
+ assertBuffer("This is est", b = b.back());
+ assertBuffer("This isest", b = b.back());
+ assertBuffer("This iest", b = b.back());
+ assertBuffer("This est", b = b.back());
+ assertBuffer("Thisest", b = b.back());
+ assertBuffer("Thiest", b = b.back());
+ assertBuffer("Thest", b = b.back());
+ assertBuffer("Test", b = b.back());
+ assertBuffer("est", b = b.back());
+ assertBuffer("est", b = b.back());
+ assertBuffer("est", b = b.back());
+ assertBuffer("est", b = b.back());
+ assertBuffer("est", b = b.back());
+ }
+
+ public void testBackspace() throws Exception {
+ Buffer b = new Buffer("This is a test");
+ assertBuffer("This is a tes", b = b.back());
+ assertBuffer("This is a te", b = b.back());
+ assertBuffer("This is a t", b = b.back());
+ assertBuffer("This is a ", b = b.back());
+ assertBuffer("This is a", b = b.back());
+ assertBuffer("This is ", b = b.back());
+ assertBuffer("This is", b = b.back());
+ assertBuffer("This i", b = b.back());
+ assertBuffer("This ", b = b.back());
+ assertBuffer("This", b = b.back());
+ assertBuffer("Thi", b = b.back());
+ assertBuffer("Th", b = b.back());
+ assertBuffer("T", b = b.back());
+ assertBuffer("", b = b.back());
+ assertBuffer("", b = b.back());
+ assertBuffer("", b = b.back());
+ assertBuffer("", b = b.back());
+ assertBuffer("", b = b.back());
+ }
+
+ public void testBuffer() throws Exception {
+ assertBuffer("This is a test", new Buffer("This is a test"));
+ }
+}
diff --git a/src/src/test/java/jline/TestHistory.java b/src/src/test/java/jline/TestHistory.java
new file mode 100644
index 0000000..32bebd7
--- /dev/null
+++ b/src/src/test/java/jline/TestHistory.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline;
+
+
+/**
+ * Tests command history.
+ *
+ * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ */
+public class TestHistory extends JLineTestCase {
+ public TestHistory(String test) {
+ super(test);
+ }
+
+ public void testSingleHistory() throws Exception {
+ Buffer b = new Buffer().
+ append("test line 1").op(ConsoleReader.NEWLINE).
+ append("test line 2").op(ConsoleReader.NEWLINE).
+ append("test line 3").op(ConsoleReader.NEWLINE).
+ append("test line 4").op(ConsoleReader.NEWLINE).
+ append("test line 5").op(ConsoleReader.NEWLINE).
+ append("");
+
+ assertBuffer("", b);
+
+ assertBuffer("test line 5", b = b.op(ConsoleReader.PREV_HISTORY));
+ assertBuffer("test line 5", b = b.op(ConsoleReader.PREV_CHAR));
+ assertBuffer("test line 4", b = b.op(ConsoleReader.PREV_HISTORY));
+ assertBuffer("test line 5", b = b.op(ConsoleReader.NEXT_HISTORY));
+ assertBuffer("test line 4", b = b.op(ConsoleReader.PREV_HISTORY));
+ assertBuffer("test line 3", b = b.op(ConsoleReader.PREV_HISTORY));
+ assertBuffer("test line 2", b = b.op(ConsoleReader.PREV_HISTORY));
+ assertBuffer("test line 1", b = b.op(ConsoleReader.PREV_HISTORY));
+
+ // beginning of history
+ assertBuffer("test line 1", b = b.op(ConsoleReader.PREV_HISTORY));
+ assertBuffer("test line 1", b = b.op(ConsoleReader.PREV_HISTORY));
+ assertBuffer("test line 1", b = b.op(ConsoleReader.PREV_HISTORY));
+ assertBuffer("test line 1", b = b.op(ConsoleReader.PREV_HISTORY));
+
+ assertBuffer("test line 2", b = b.op(ConsoleReader.NEXT_HISTORY));
+ assertBuffer("test line 3", b = b.op(ConsoleReader.NEXT_HISTORY));
+ assertBuffer("test line 4", b = b.op(ConsoleReader.NEXT_HISTORY));
+ assertBuffer("test line 5", b = b.op(ConsoleReader.NEXT_HISTORY));
+
+ // end of history
+ assertBuffer("", b = b.op(ConsoleReader.NEXT_HISTORY));
+ assertBuffer("", b = b.op(ConsoleReader.NEXT_HISTORY));
+ assertBuffer("", b = b.op(ConsoleReader.NEXT_HISTORY));
+
+ assertBuffer("test line 5", b = b.op(ConsoleReader.PREV_HISTORY));
+ assertBuffer("test line 4", b = b.op(ConsoleReader.PREV_HISTORY));
+ b = b.op(ConsoleReader.MOVE_TO_BEG).append("XXX")
+ .op(ConsoleReader.NEWLINE);
+ assertBuffer("XXXtest line 4", b = b.op(ConsoleReader.PREV_HISTORY));
+ assertBuffer("test line 5", b = b.op(ConsoleReader.PREV_HISTORY));
+ assertBuffer("test line 4", b = b.op(ConsoleReader.PREV_HISTORY));
+ assertBuffer("test line 5", b = b.op(ConsoleReader.NEXT_HISTORY));
+ assertBuffer("XXXtest line 4", b = b.op(ConsoleReader.NEXT_HISTORY));
+ assertBuffer("", b = b.op(ConsoleReader.NEXT_HISTORY));
+
+ assertBuffer("XXXtest line 4", b = b.op(ConsoleReader.PREV_HISTORY));
+ assertBuffer("XXXtest line 4", b = b.op(ConsoleReader.NEWLINE).
+ op(ConsoleReader.PREV_HISTORY));
+ assertBuffer("XXXtest line 4", b = b.op(ConsoleReader.NEWLINE).
+ op(ConsoleReader.PREV_HISTORY));
+ assertBuffer("XXXtest line 4", b = b.op(ConsoleReader.NEWLINE).
+ op(ConsoleReader.PREV_HISTORY));
+ assertBuffer("XXXtest line 4", b = b.op(ConsoleReader.NEWLINE).
+ op(ConsoleReader.PREV_HISTORY));
+ }
+}
diff --git a/src/src/test/java/jline/example/Example.java b/src/src/test/java/jline/example/Example.java
new file mode 100644
index 0000000..c019b29
--- /dev/null
+++ b/src/src/test/java/jline/example/Example.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2002-2006, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline.example;
+
+import jline.*;
+
+import java.io.*;
+import java.util.*;
+import java.util.zip.*;
+
+public class Example {
+ public static void usage() {
+ System.out.println("Usage: java " + Example.class.getName()
+ + " [none/simple/files/dictionary [trigger mask]]");
+ System.out.println(" none - no completors");
+ System.out.println(" simple - a simple completor that comples "
+ + "\"foo\", \"bar\", and \"baz\"");
+ System.out
+ .println(" files - a completor that comples " + "file names");
+ System.out.println(" dictionary - a completor that comples "
+ + "english dictionary words");
+ System.out.println(" classes - a completor that comples "
+ + "java class names");
+ System.out
+ .println(" trigger - a special word which causes it to assume "
+ + "the next line is a password");
+ System.out.println(" mask - is the character to print in place of "
+ + "the actual password character");
+ System.out.println("\n E.g - java Example simple su '*'\n"
+ + "will use the simple compleator with 'su' triggering\n"
+ + "the use of '*' as a password mask.");
+ }
+
+ public static void main(String[] args) throws IOException {
+ Character mask = null;
+ String trigger = null;
+
+ ConsoleReader reader = new ConsoleReader();
+ reader.setBellEnabled(false);
+ reader.setDebug(new PrintWriter(new FileWriter("writer.debug", true)));
+
+ if ((args == null) || (args.length == 0)) {
+ usage();
+
+ return;
+ }
+
+ List completors = new LinkedList();
+
+ if (args.length > 0) {
+ if (args[0].equals("none")) {
+ } else if (args[0].equals("files")) {
+ completors.add(new FileNameCompletor());
+ } else if (args[0].equals("classes")) {
+ completors.add(new ClassNameCompletor());
+ } else if (args[0].equals("dictionary")) {
+ completors.add(new SimpleCompletor(new GZIPInputStream(
+ Example.class.getResourceAsStream("english.gz"))));
+ } else if (args[0].equals("simple")) {
+ completors.add(new SimpleCompletor(new String[] { "foo", "bar",
+ "baz" }));
+ } else {
+ usage();
+
+ return;
+ }
+ }
+
+ if (args.length == 3) {
+ mask = new Character(args[2].charAt(0));
+ trigger = args[1];
+ }
+
+ reader.addCompletor(new ArgumentCompletor(completors));
+
+ String line;
+ PrintWriter out = new PrintWriter(System.out);
+
+ while ((line = reader.readLine("prompt> ")) != null) {
+ out.println("======>\"" + line + "\"");
+ out.flush();
+
+ // If we input the special word then we will mask
+ // the next line.
+ if ((trigger != null) && (line.compareTo(trigger) == 0)) {
+ line = reader.readLine("password> ", mask);
+ }
+ if (line.equalsIgnoreCase("quit") || line.equalsIgnoreCase("exit")) {
+ break;
+ }
+ }
+ }
+}
diff --git a/src/src/test/java/jline/example/PasswordReader.java b/src/src/test/java/jline/example/PasswordReader.java
new file mode 100644
index 0000000..1331855
--- /dev/null
+++ b/src/src/test/java/jline/example/PasswordReader.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ */
+package jline.example;
+
+import jline.*;
+
+import java.io.*;
+
+public class PasswordReader {
+ public static void usage() {
+ System.out.println("Usage: java "
+ + PasswordReader.class.getName() + " [mask]");
+ }
+
+ public static void main(String[] args) throws IOException {
+ ConsoleReader reader = new ConsoleReader();
+
+ Character mask = (args.length == 0)
+ ? new Character((char) 0)
+ : new Character(args[0].charAt(0));
+
+ String line = null;
+ do {
+ line = reader.readLine("enter password> ", mask);
+ System.out.println("Got password: " + line);
+ } while(line != null && line.length() > 0);
+ }
+}
diff --git a/src/src/test/resources/jline/example/english.gz b/src/src/test/resources/jline/example/english.gz
new file mode 100644
index 0000000..f0a85c0
--- /dev/null
+++ b/src/src/test/resources/jline/example/english.gz
Binary files differ