diff options
Diffstat (limited to 'src/jline')
94 files changed, 7192 insertions, 7167 deletions
diff --git a/src/jline/README.md b/src/jline/README.md new file mode 100644 index 0000000000..6d129e721d --- /dev/null +++ b/src/jline/README.md @@ -0,0 +1,27 @@ +Description +----------- + +JLine 2.x + +License +------- + +BSD + +Building +-------- + +### Requirements + +* Maven 2+ +* Java 5+ + +Check out and build: + + git clone git://github.com/jdillon/jline2.git + cd jline2 + mvn install + +To build the jdk14 jar: + + mvn install -Dretro diff --git a/src/jline/pom.xml b/src/jline/pom.xml index ac0e188585..71043e32c2 100644 --- a/src/jline/pom.xml +++ b/src/jline/pom.xml @@ -1,224 +1,273 @@ <?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"> - -<properties> - <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> -</properties> - <modelVersion>4.0.0</modelVersion> - <groupId>jline</groupId> - <artifactId>jline</artifactId> - <packaging>jar</packaging> - <name>JLine</name> - <version>0.9.95-SNAPSHOT</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&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> - </developers> - <licenses> - <license> - <name>BSD</name> - <url>LICENSE.txt</url> - </license> - </licenses> - <scm> - <connection>scm:cvs:pserver:anonymous@jline.cvs.sourceforge.net:/cvsroot/jline:jline</connection> - <developerConnection>scm:cvs:ext:${maven.username}@jline.cvs.sourceforge.net:/cvsroot/jline:jline</developerConnection> - <url>http://jline.cvs.sourceforge.net/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.codehaus.mojo</groupId> - <artifactId>jalopy-maven-plugin</artifactId> - <version>1.0-SNAPSHOT</version> - <configuration> - <fileFormat>UNIX</fileFormat> - <convention>codestyle.xml</convention> - </configuration> - <executions> - <execution> - <goals> - <goal>format</goal> - </goals> - </execution> - </executions> - </plugin> - --> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-surefire-plugin</artifactId> - <configuration> - <!-- <testFailureIgnore>true</testFailureIgnore> --> - <useFile>false</useFile> - <trimStackTrace>false</trimStackTrace> - </configuration> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-compiler-plugin</artifactId> - <configuration> - <source>1.5</source> - <target>1.5</target> - <showWarnings>true</showWarnings> - <compilerArgument>-Xlint:unchecked</compilerArgument> - </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> - </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>dependencies</report> --> - <!-- <report>cim</report> --> - <!-- <report>cobertura</report> --> - <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> +<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> + + <parent> + <groupId>org.sonatype.forge</groupId> + <artifactId>forge-parent</artifactId> + <version>6</version> + </parent> + + <groupId>org.sonatype.jline</groupId> + <artifactId>jline</artifactId> + <name>JLine</name> + <version>2.6-SNAPSHOT</version> + + <organization> + <name>Sonatype</name> + <url>http://sonatype.org</url> + </organization> + + <licenses> + <license> + <name>The BSD License</name> + <url>http://www.opensource.org/licenses/bsd-license.php</url> + <distribution>repo</distribution> + </license> + </licenses> + + <scm> + <connection>scm:git:git://github.com/jdillon/jline2.git</connection> + <developerConnection>scm:git:ssh://git@github.com/jdillon/jline2.git</developerConnection> + <url>http://github.com/jdillon/jline2</url> + </scm> + + <ciManagement> + <system>Hudson</system> + <url>https://grid.sonatype.org/ci/job/JLine2</url> + </ciManagement> + + <developers> + <developer> + <id>jdillon</id> + <name>Jason Dillon</name> + <email>jason@planet57.com</email> + <roles> + <role>Build Master</role> + <role>Developer</role> + </roles> + </developer> + </developers> + + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + </properties> + + <!-- + <repositories> + <repository> + <id>jansi</id> + <url>http://jansi.fusesource.org/repo/snapshot</url> + <releases> + <enabled>false</enabled> + </releases> + <snapshots> + <enabled>true</enabled> + </snapshots> + </repository> + </repositories> + --> + + <dependencies> + <dependency> + <groupId>org.fusesource.jansi</groupId> + <artifactId>jansi</artifactId> + <version>1.4</version> + <!--<scope>provided</scope>--> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.8.1</version> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <defaultGoal>install</defaultGoal> + + <resources> + <resource> + <directory>${project.basedir}/src/main/resources</directory> + <filtering>false</filtering> + <includes> + <include>**/*</include> + </includes> + </resource> + + <resource> + <directory>${project.basedir}/src/main/filtered-resources</directory> + <filtering>true</filtering> + <includes> + <include>**/*</include> + </includes> + </resource> + </resources> + + <testResources> + <testResource> + <directory>${project.basedir}/src/test/resources</directory> + <filtering>false</filtering> + <includes> + <include>**/*</include> + </includes> + </testResource> + + <testResource> + <directory>${project.basedir}/src/test/filtered-resources</directory> + <filtering>true</filtering> + <includes> + <include>**/*</include> + </includes> + </testResource> + </testResources> + + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <version>2.6</version> + <configuration> + <redirectTestOutputToFile>true</redirectTestOutputToFile> + <forkMode>once</forkMode> + <argLine>-ea</argLine> + <failIfNoTests>false</failIfNoTests> + <workingDirectory>${project.build.directory}</workingDirectory> + <excludes> + <exclude>**/Abstract*.java</exclude> + <exclude>**/Test*.java</exclude> + </excludes> + <includes> + <include>**/*Test.java</include> + </includes> + </configuration> + </plugin> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>2.3.2</version> + <configuration> + <source>1.5</source> + <target>1.5</target> + </configuration> + </plugin> + + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <version>2.1.0</version> + <executions> + <execution> + <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> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + <version>2.3.1</version> + <configuration> + <archive> + <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile> + </archive> + </configuration> + <executions> + <execution> + <goals> + <goal>test-jar</goal> + </goals> + </execution> + </executions> + </plugin> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-scm-plugin</artifactId> + <version>1.4</version> + </plugin> + + <!-- include all the dependencies into the jar so it can run standalone --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + <version>1.4</version> + <executions> + <execution> + <goals> + <goal>shade</goal> + </goals> + <configuration> + <artifactSet> + <excludes> + <exclude>junit:junit</exclude> + </excludes> + </artifactSet> + <filters> + <filter> + <artifact>org.fusesource.jansi:jansi</artifact> + <excludes> + <exclude>META-INF/maven/**</exclude> + <exclude>*.txt</exclude> + <exclude>junit/**</exclude> + <exclude>org/junit/**</exclude> + <exclude>org/hamcrest/**</exclude> + <exclude>org/fusesource/hawtjni/runtime/Jni*</exclude> + <exclude>org/fusesource/hawtjni/runtime/*Flag*</exclude> + <exclude>org/fusesource/hawtjni/runtime/T32*</exclude> + <exclude>org/fusesource/hawtjni/runtime/NativeStats*</exclude> + </excludes> + </filter> + </filters> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + + <profiles> + <profile> + <id>retro</id> + <activation> + <property> + <name>retro</name> + <value>true</value> + </property> + </activation> + <build> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>retrotranslator-maven-plugin</artifactId> + <version>1.0-alpha-4</version> + <executions> + <execution> + <goals> + <goal>translate-project</goal> + </goals> + <configuration> + <classifier>jdk14</classifier> + <attach>true</attach> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + </profile> + </profiles> + +</project>
\ No newline at end of file diff --git a/src/jline/src/assembly/assembly.xml b/src/jline/src/assembly/assembly.xml deleted file mode 100644 index 216c697a53..0000000000 --- a/src/jline/src/assembly/assembly.xml +++ /dev/null @@ -1,55 +0,0 @@ -<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/jline/src/main/java/jline/ANSIBuffer.java b/src/jline/src/main/java/jline/ANSIBuffer.java deleted file mode 100644 index c2e33180bb..0000000000 --- a/src/jline/src/main/java/jline/ANSIBuffer.java +++ /dev/null @@ -1,405 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ -package 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/jline/src/main/java/jline/AnsiWindowsTerminal.java b/src/jline/src/main/java/jline/AnsiWindowsTerminal.java new file mode 100644 index 0000000000..43351952ff --- /dev/null +++ b/src/jline/src/main/java/jline/AnsiWindowsTerminal.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2009 the original author(s). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * MODIFICATIONS: methods to deal with wrapping the output stream. + */ + +package jline; + +import org.fusesource.jansi.AnsiConsole; +import org.fusesource.jansi.AnsiOutputStream; +import org.fusesource.jansi.WindowsAnsiOutputStream; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; + +/** + * ANSI-supported {@link WindowsTerminal}. + * + * @since 2.0 + */ +public class AnsiWindowsTerminal + extends WindowsTerminal +{ + private final boolean ansiSupported = detectAnsiSupport(); + + @Override + public OutputStream wrapOutIfNeeded(OutputStream out) { + return wrapOutputStream(out); + } + + /** + * Returns an ansi output stream handler. We return whatever was + * passed if we determine we cannot handle ansi based on Kernel32 calls. + * + * @return an @{link AltWindowAnsiOutputStream} instance or the passed + * stream. + */ + private static OutputStream wrapOutputStream(final OutputStream stream) { + String os = System.getProperty("os.name"); + if( os.startsWith("Windows") ) { + // On windows we know the console does not interpret ANSI codes.. + try { + return new WindowsAnsiOutputStream(stream); + } catch (Throwable ignore) { + // this happens when JNA is not in the path.. or + // this happens when the stdout is being redirected to a file. + } + // Use the ANSIOutputStream to strip out the ANSI escape sequences. + return new AnsiOutputStream(stream); + } + return stream; + } + + private static boolean detectAnsiSupport() { + OutputStream out = AnsiConsole.wrapOutputStream(new ByteArrayOutputStream()); + try { + out.close(); + } + catch (Exception e) { + // ignore; + } + return out instanceof WindowsAnsiOutputStream; + } + + public AnsiWindowsTerminal() throws Exception { + super(); + } + + @Override + public boolean isAnsiSupported() { + return ansiSupported; + } + + @Override + public boolean newlineAtWrapNeeded() { + return false; + } +} diff --git a/src/jline/src/main/java/jline/ArgumentCompletor.java b/src/jline/src/main/java/jline/ArgumentCompletor.java deleted file mode 100644 index 2cd572ce2f..0000000000 --- a/src/jline/src/main/java/jline/ArgumentCompletor.java +++ /dev/null @@ -1,439 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ -package 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<Completor> 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<String> 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<String> subCandidates = new LinkedList<String>(); - - 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<String> args = new LinkedList<String>(); - 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/jline/src/main/java/jline/CandidateCycleCompletionHandler.java b/src/jline/src/main/java/jline/CandidateCycleCompletionHandler.java deleted file mode 100644 index a0bf208cdc..0000000000 --- a/src/jline/src/main/java/jline/CandidateCycleCompletionHandler.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ -package 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/jline/src/main/java/jline/CandidateListCompletionHandler.java b/src/jline/src/main/java/jline/CandidateListCompletionHandler.java deleted file mode 100644 index 17f03d5e68..0000000000 --- a/src/jline/src/main/java/jline/CandidateListCompletionHandler.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ -package 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<String> 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<String> candidates, boolean eagerNewlines) - throws IOException { - Set<String> distinct = new HashSet<String>(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<String> copy = new ArrayList<String>(); - - for (Iterator<String> i = candidates.iterator(); i.hasNext();) { - String 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<String> 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/jline/src/main/java/jline/ClassNameCompletor.java b/src/jline/src/main/java/jline/ClassNameCompletor.java deleted file mode 100644 index 5c3ca87dca..0000000000 --- a/src/jline/src/main/java/jline/ClassNameCompletor.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ -package 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<URL> urls = new HashSet<URL>(); - - 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<String> classes = new HashSet<String>(); - - for (Iterator i = urls.iterator(); i.hasNext();) { - URL url = (URL) i.next(); - File file = new File(url.getFile()); - - if (file.isDirectory()) { - Set<String> files = getClassFiles(file.getAbsolutePath(), - new HashSet<String>(), file, new int[] { 200 }); - classes.addAll(files); - - continue; - } - - if ((file == null) || !file.isFile()) // TODO: handle directories - { - 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<String> classNames = new TreeSet<String>(); - - for (Iterator<String> 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<String> getClassFiles(String root, Set<String> 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/jline/src/main/java/jline/CompletionHandler.java b/src/jline/src/main/java/jline/CompletionHandler.java deleted file mode 100644 index 5dffdcbaae..0000000000 --- a/src/jline/src/main/java/jline/CompletionHandler.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ -package 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<String> candidates, int position) - throws IOException; -} diff --git a/src/jline/src/main/java/jline/Completor.java b/src/jline/src/main/java/jline/Completor.java deleted file mode 100644 index ed1238a93d..0000000000 --- a/src/jline/src/main/java/jline/Completor.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ -package 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<String> candidates); -} diff --git a/src/jline/src/main/java/jline/ConsoleOperations.java b/src/jline/src/main/java/jline/ConsoleOperations.java deleted file mode 100644 index 585ed401d9..0000000000 --- a/src/jline/src/main/java/jline/ConsoleOperations.java +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ -package 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 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; - -} diff --git a/src/jline/src/main/java/jline/ConsoleReader.java b/src/jline/src/main/java/jline/ConsoleReader.java deleted file mode 100644 index 66655cce73..0000000000 --- a/src/jline/src/main/java/jline/ConsoleReader.java +++ /dev/null @@ -1,1625 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ -package 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<String, Short> KEYMAP_NAMES; - - static { - Map<String, Short> names = new TreeMap<String, Short>(); - - 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)); - - KEYMAP_NAMES = new TreeMap<String, Short>(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<Completor> completors = new LinkedList<Completor>(); - - private Character echoCharacter = null; - - private Map<Character, ActionListener> triggeredActions = new HashMap<Character, ActionListener>(); - - - /** - * Adding a triggered Action allows to give another curse 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 defaultBindingFile = - new File(System.getProperty("user.home"), - ".jlinebindings.properties").getAbsolutePath(); - String bindingFile = System.getProperty("jline.keybindings", defaultBindingFile); - - 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 Terminal.setupTerminal().getTerminalWidth(); - } - - /** - * Query the terminal to find the current width; - * - * @see Terminal#getTerminalHeight - * - * @return the height of the current terminal. - */ - public int getTermheight() { - return Terminal.setupTerminal().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 ((prompt == null) ? 0 : prompt.length()) + buf.cursor; - } - - 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); - } - - while (true) { - int[] next = readBinding(); - - if (next == null) { - return null; - } - - int c = next[0]; - int code = next[1]; - - if (c == -1) { - return null; - } - - boolean success = true; - - switch (code) { - case EXIT: // ctrl-d - - if (buf.buffer.length() == 0) { - return null; - } - 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 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 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 - printString(((char) 27) + "[2J"); - flushConsole(); - - // then send the ANSI code to go to position 1,1 - printString(((char) 27) + "[1;1H"); - flushConsole(); - - 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<String> candidates = new LinkedList<String>(); - 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<Completor> 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.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); // 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); - char[] chars = buf.buffer.substring(buf.cursor).toCharArray(); - if (mask != null) - Arrays.fill(chars, mask.charValue()); - - printCharacters(chars); - - clearAhead(clear); - 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; - } - - // 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 { - 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; - - count = moveCursor(-1 * num) * -1; - // debug ("Deleting from " + buf.cursor + " for " + count); - buf.buffer.delete(buf.cursor, buf.cursor + count); - 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 { - if (moveCursor(1) == 0) { - return false; - } - - while (moveCursor(1) != 0) { - ; - } - - return true; - } - - /** - * Delete the character at the current position and redraw the remainder of - * the buffer. - */ - private final boolean deleteCurrentCharacter() throws IOException { - boolean success = buf.buffer.length() > 0; - if (!success) { - return false; - } - - if (buf.cursor == buf.buffer.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 where - * if less than 0, move abs(<i>where</i>) to the left, - * otherwise move <i>where</i> to the right. - * - * @return the number of spaces we moved - */ - public 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. - * - * @see #where - * - * @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; - - 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); - } - - /** - * 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; - } - -} diff --git a/src/jline/src/main/java/jline/ConsoleReaderInputStream.java b/src/jline/src/main/java/jline/ConsoleReaderInputStream.java deleted file mode 100644 index d2a14d8e72..0000000000 --- a/src/jline/src/main/java/jline/ConsoleReaderInputStream.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ -package 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<InputStream> { - private final ConsoleReader reader; - private ConsoleLineInputStream next = null; - private ConsoleLineInputStream prev = null; - - public ConsoleEnumeration(final ConsoleReader reader) { - this.reader = reader; - } - - public InputStream nextElement() { - if (next != null) { - InputStream n = next; - prev = next; - next = null; - - return n; - } - - return new ConsoleLineInputStream(reader); - } - - public boolean hasMoreElements() { - // the last line was null - if ((prev != null) && (prev.wasNull == true)) { - return false; - } - - if (next == null) { - next = (ConsoleLineInputStream) nextElement(); - } - - return next != null; - } - } - - private static class ConsoleLineInputStream extends InputStream { - private final 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/jline/src/main/java/jline/ConsoleRunner.java b/src/jline/src/main/java/jline/ConsoleRunner.java deleted file mode 100644 index dac4b7c88a..0000000000 --- a/src/jline/src/main/java/jline/ConsoleRunner.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ -package 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 { - public static final String property = "jline.history"; - - public static void main(final String[] args) throws Exception { - String historyFileName = null; - - List<String> argList = new ArrayList<String>(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 - ConsoleReader 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<Completor> completorList = new ArrayList<Completor>(); - - 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/jline/src/main/java/jline/FileNameCompletor.java b/src/jline/src/main/java/jline/FileNameCompletor.java deleted file mode 100644 index d1d63735d1..0000000000 --- a/src/jline/src/main/java/jline/FileNameCompletor.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ -package 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<String> 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<String> 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<String> 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/jline/src/main/java/jline/History.java b/src/jline/src/main/java/jline/History.java deleted file mode 100644 index 9e96f69146..0000000000 --- a/src/jline/src/main/java/jline/History.java +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ -package 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<String> history = new ArrayList<String>(); - - 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<String> lines = new ArrayList<String>(); - 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<String> 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; - } -} diff --git a/src/jline/src/main/java/jline/MultiCompletor.java b/src/jline/src/main/java/jline/MultiCompletor.java deleted file mode 100644 index 2d43a204cc..0000000000 --- a/src/jline/src/main/java/jline/MultiCompletor.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ -package 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<Completor> 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<String> cand) { - int[] positions = new int[completors.length]; - List<List<String>> copies = new ArrayList<List<String>>(); - for (int i = 0; i < completors.length; i++) { - copies.add(null); - } - - for (int i = 0; i < completors.length; i++) { - // clone and save the candidate list - copies.set(i, new LinkedList<String>(cand)); - positions[i] = completors[i].complete(buffer, pos, copies.get(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.size(); i++) { - if (positions[i] == maxposition) { - cand.addAll(copies.get(i)); - } - } - - return maxposition; - } - - public void setCompletors(final Completor[] completors) { - this.completors = completors; - } - - public Completor[] getCompletors() { - return this.completors; - } -} diff --git a/src/jline/src/main/java/jline/NoInterruptUnixTerminal.java b/src/jline/src/main/java/jline/NoInterruptUnixTerminal.java new file mode 100644 index 0000000000..b2cd22b5c7 --- /dev/null +++ b/src/jline/src/main/java/jline/NoInterruptUnixTerminal.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2009 the original author(s). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package jline; + +// Based on Apache Karaf impl + +/** + * Non-interruptable (via CTRL-C) {@link UnixTerminal}. + * + * @since 2.0 + */ +public class NoInterruptUnixTerminal + extends UnixTerminal +{ + public NoInterruptUnixTerminal() throws Exception { + super(); + } + + @Override + public void init() throws Exception { + super.init(); + getSettings().set("intr undef"); + } + + @Override + public void restore() throws Exception { + getSettings().set("intr ^C"); + super.restore(); + } +} diff --git a/src/jline/src/main/java/jline/NullCompletor.java b/src/jline/src/main/java/jline/NullCompletor.java deleted file mode 100644 index aa6cdf744e..0000000000 --- a/src/jline/src/main/java/jline/NullCompletor.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ -package 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/jline/src/main/java/jline/SimpleCompletor.java b/src/jline/src/main/java/jline/SimpleCompletor.java deleted file mode 100644 index 3581277409..0000000000 --- a/src/jline/src/main/java/jline/SimpleCompletor.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ -package 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<String> 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<String> words = new LinkedList<String>(); - 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<String> clist) { - String start = (buffer == null) ? "" : buffer; - - SortedSet<String> 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<String> candidates) { - if (filter != null) { - TreeSet<String> filtered = new TreeSet<String>(); - - 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<String>(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/jline/src/main/java/jline/Terminal.java b/src/jline/src/main/java/jline/Terminal.java index 53a5aff4d4..fb86c07c12 100644 --- a/src/jline/src/main/java/jline/Terminal.java +++ b/src/jline/src/main/java/jline/Terminal.java @@ -4,177 +4,53 @@ * 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.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; /** - * 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. + * Representation of the input terminal for a platform. * - * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> + * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> + * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> + * @since 2.0 */ -public abstract class Terminal implements ConsoleOperations { - private static Terminal term; +public interface Terminal +{ + void init() throws Exception; - /** - * @see #setupTerminal - */ - public static Terminal getTerminal() { - return setupTerminal(); - } + void restore() throws Exception; - /** - * Reset the current terminal to null. - */ - public static void resetTerminal() { - term = null; - } + void reset() throws Exception; - /** - * <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; - } + boolean isSupported(); - /** - * Returns true if the current console supports ANSI - * codes. - */ - public boolean isANSISupported() { - return true; - } + int getWidth(); - /** - * 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(); - } + int getHeight(); - /** - * 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); - } + boolean isAnsiSupported(); /** - * 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. + * When ANSI is not natively handled, the output will have to be wrapped. */ - public abstract void initializeTerminal() throws Exception; + OutputStream wrapOutIfNeeded(OutputStream out); /** - * Returns the current width of the terminal (in characters) + * For terminals that don't wrap when character is written in last column. */ - public abstract int getTerminalWidth(); + boolean newlineAtWrapNeeded(); - /** - * Returns the current height of the terminal (in lines) - */ - public abstract int getTerminalHeight(); + boolean isEchoEnabled(); - /** - * Returns true if this terminal is capable of initializing the - * terminal to use jline. - */ - public abstract boolean isSupported(); + void setEchoEnabled(boolean enabled); - /** - * Returns true if the terminal will echo all characters type. - */ - public abstract boolean getEcho(); + int readCharacter(InputStream in) throws IOException; - /** - * 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(); + int readVirtualKey(InputStream in) throws IOException; - public InputStream getDefaultBindings() { - return Terminal.class.getResourceAsStream("keybindings.properties"); - } + InputStream getDefaultBindings(); } diff --git a/src/jline/src/main/java/jline/TerminalFactory.java b/src/jline/src/main/java/jline/TerminalFactory.java new file mode 100644 index 0000000000..7a45cc9c51 --- /dev/null +++ b/src/jline/src/main/java/jline/TerminalFactory.java @@ -0,0 +1,173 @@ +/* + * 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 jline.internal.Configuration; +import jline.internal.Log; + +import java.text.MessageFormat; +import java.util.HashMap; +import java.util.Map; + +/** + * Creates terminal instances. + * + * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> + * @since 2.0 + */ +public class TerminalFactory +{ + public static final String JLINE_TERMINAL = "jline.terminal"; + + public static final String AUTO = "auto"; + + public static final String UNIX = "unix"; + + public static final String WIN = "win"; + + public static final String WINDOWS = "windows"; + + public static final String NONE = "none"; + + public static final String OFF = "off"; + + public static final String FALSE = "false"; + + private static final InheritableThreadLocal<Terminal> holder = new InheritableThreadLocal<Terminal>(); + + public static synchronized Terminal create() { + if (Log.TRACE) { + //noinspection ThrowableInstanceNeverThrown + Log.trace(new Throwable("CREATE MARKER")); + } + + String type = Configuration.getString(JLINE_TERMINAL); + if (type == null) { + type = AUTO; + } + + Log.debug("Creating terminal; type=", type); + + Terminal t; + try { + String tmp = type.toLowerCase(); + + if (tmp.equals(UNIX)) { + t = getFlavor(Flavor.UNIX); + } + else if (tmp.equals(WIN) | tmp.equals(WINDOWS)) { + t = getFlavor(Flavor.WINDOWS); + } + else if (tmp.equals(NONE) || tmp.equals(OFF) || tmp.equals(FALSE)) { + t = new UnsupportedTerminal(); + } + else { + if (tmp.equals(AUTO)) { + String os = Configuration.getOsName(); + Flavor flavor = Flavor.UNIX; + if (os.contains(WINDOWS)) { + flavor = Flavor.WINDOWS; + } + t = getFlavor(flavor); + } + else { + try { + t = (Terminal) Thread.currentThread().getContextClassLoader().loadClass(type).newInstance(); + } + catch (Exception e) { + throw new IllegalArgumentException(MessageFormat.format("Invalid terminal type: {0}", type), e); + } + } + } + } + catch (Exception e) { + Log.error("Failed to construct terminal; falling back to unsupported", e); + t = new UnsupportedTerminal(); + } + + Log.debug("Created Terminal: ", t); + + try { + t.init(); + } + catch (Exception e) { + Log.error("Terminal initialization failed; falling back to unsupported", e); + return new UnsupportedTerminal(); + } + + return t; + } + + public static synchronized void reset() { + holder.remove(); + } + + public static synchronized void resetIf(final Terminal t) { + if (holder.get() == t) { + reset(); + } + } + + public static enum Type + { + AUTO, + WINDOWS, + UNIX, + NONE + } + + public static synchronized void configure(final String type) { + assert type != null; + System.setProperty(JLINE_TERMINAL, type); + } + + public static synchronized void configure(final Type type) { + assert type != null; + configure(type.name().toLowerCase()); + } + + // + // Flavor Support + // + + public static enum Flavor + { + WINDOWS, + UNIX + } + + private static final Map<Flavor, Class<? extends Terminal>> FLAVORS = new HashMap<Flavor, Class<? extends Terminal>>(); + + static { + registerFlavor(Flavor.WINDOWS, AnsiWindowsTerminal.class); + registerFlavor(Flavor.UNIX, UnixTerminal.class); + } + + public static synchronized Terminal get() { + Terminal t = holder.get(); + if (t == null) { + t = create(); + holder.set(t); + } + return t; + } + + public static Terminal getFlavor(final Flavor flavor) throws Exception { + Class<? extends Terminal> type = FLAVORS.get(flavor); + if (type != null) { + return type.newInstance(); + } + + throw new InternalError(); + } + + public static void registerFlavor(final Flavor flavor, final Class<? extends Terminal> type) { + FLAVORS.put(flavor, type); + } + +}
\ No newline at end of file diff --git a/src/jline/src/main/java/jline/TerminalSupport.java b/src/jline/src/main/java/jline/TerminalSupport.java new file mode 100644 index 0000000000..22f51c5e80 --- /dev/null +++ b/src/jline/src/main/java/jline/TerminalSupport.java @@ -0,0 +1,163 @@ +/* + * 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 jline.internal.Log; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * Provides support for {@link Terminal} instances. + * + * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> + * @since 2.0 + */ +public abstract class TerminalSupport + implements Terminal +{ + public static String DEFAULT_KEYBINDINGS_PROPERTIES = "keybindings.properties"; + + public static final int DEFAULT_WIDTH = 80; + + public static final int DEFAULT_HEIGHT = 24; + + private Thread shutdownHook; + + private boolean supported; + + private boolean echoEnabled; + + private boolean ansiSupported; + + protected TerminalSupport(final boolean supported) { + this.supported = supported; + } + + public void init() throws Exception { + installShutdownHook(new RestoreHook()); + } + + public void restore() throws Exception { + TerminalFactory.resetIf(this); + removeShutdownHook(); + } + + public void reset() throws Exception { + restore(); + init(); + } + + protected void installShutdownHook(final Thread hook) { + assert hook != null; + + if (shutdownHook != null) { + throw new IllegalStateException("Shutdown hook already installed"); + } + + try { + Runtime.getRuntime().addShutdownHook(hook); + shutdownHook = hook; + } + catch (AbstractMethodError e) { + // JDK 1.3+ only method. Bummer. + Log.trace("Failed to register shutdown hook: ", e); + } + } + + protected void removeShutdownHook() { + if (shutdownHook != null) { + try { + Runtime.getRuntime().removeShutdownHook(shutdownHook); + } + catch (AbstractMethodError e) { + // JDK 1.3+ only method. Bummer. + Log.trace("Failed to remove shutdown hook: ", e); + } + catch (IllegalStateException e) { + // The VM is shutting down, not a big deal; ignore + } + shutdownHook = null; + } + } + + public final boolean isSupported() { + return supported; + } + + public synchronized boolean isAnsiSupported() { + return ansiSupported; + } + + protected synchronized void setAnsiSupported(final boolean supported) { + this.ansiSupported = supported; + Log.debug("Ansi supported: ", supported); + } + + /** + * Subclass to change behavior if needed. + * @return the passed out + */ + public OutputStream wrapOutIfNeeded(OutputStream out) { + return out; + } + + /** + * Defaults to true which was the behaviour before this method was added. + */ + public boolean newlineAtWrapNeeded() { + return true; + } + + public int getWidth() { + return DEFAULT_WIDTH; + } + + public int getHeight() { + return DEFAULT_HEIGHT; + } + + public synchronized boolean isEchoEnabled() { + return echoEnabled; + } + + public synchronized void setEchoEnabled(final boolean enabled) { + this.echoEnabled = enabled; + Log.debug("Echo enabled: ", enabled); + } + + public int readCharacter(final InputStream in) throws IOException { + return in.read(); + } + + public int readVirtualKey(final InputStream in) throws IOException { + return readCharacter(in); + } + + public InputStream getDefaultBindings() { + return TerminalSupport.class.getResourceAsStream(DEFAULT_KEYBINDINGS_PROPERTIES); + } + + // + // RestoreHook + // + + protected class RestoreHook + extends Thread + { + public void start() { + try { + restore(); + } + catch (Exception e) { + Log.trace("Failed to restore: ", e); + } + } + } +}
\ No newline at end of file diff --git a/src/jline/src/main/java/jline/UnixTerminal.java b/src/jline/src/main/java/jline/UnixTerminal.java index a873b40ff1..755d899765 100644 --- a/src/jline/src/main/java/jline/UnixTerminal.java +++ b/src/jline/src/main/java/jline/UnixTerminal.java @@ -4,112 +4,72 @@ * 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.*; +import jline.console.Key; +import jline.internal.Configuration; +import jline.internal.Log; +import jline.internal.ReplayPrefixOneCharInputStream; +import jline.internal.TerminalLineSettings; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.HashMap; +import java.util.Map; + +import static jline.UnixTerminal.UnixKey.*; +import static jline.console.Key.*; /** - * <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> + * Terminal that is used for unix platforms. Terminal initialization + * is handled by issuing the <em>stty</em> command against the + * <em>/dev/tty</em> file to disable character echoing and enable + * character input. All known unix systems (including + * Linux and Macintosh OS X) support the <em>stty</em>), so this + * implementation should work for an reasonable POSIX system. * - * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> - * @author Updates <a href="mailto:dwkemp@gmail.com">Dale Kemp</a> 2005-12-03 + * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> + * @author <a href="mailto:dwkemp@gmail.com">Dale Kemp</a> + * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> + * @author <a href="mailto:jbonofre@apache.org">Jean-Baptiste Onofré</a> + * @since 2.0 */ -public class UnixTerminal extends 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 boolean backspaceDeleteSwitched = false; - private static String sttyCommand = - System.getProperty("jline.sttyCommand", "stty"); - - - String encoding = System.getProperty("jline.UnixTerminal.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); - } - } +public class UnixTerminal + extends TerminalSupport +{ + private final TerminalLineSettings settings = new TerminalLineSettings(); - protected void checkBackspace(){ - String[] ttyConfigSplit = ttyConfig.split(":|="); - String eof; - - if ("gfmt1".equals(ttyConfigSplit[0]) && ttyConfigSplit.length > 20) { - // BSD style -g - eof = ttyConfigSplit[20]; - } else if (ttyConfigSplit.length > 6 && ttyConfigSplit[6] != null) { - eof = ttyConfigSplit[6]; - } else { - return; - } + private final ReplayPrefixOneCharInputStream replayStream; - backspaceDeleteSwitched = eof.equals("7f"); + private final InputStreamReader replayReader; + + public UnixTerminal() throws Exception { + super(true); + + this.replayStream = new ReplayPrefixOneCharInputStream(Configuration.getInputEncoding()); + this.replayReader = new InputStreamReader(replayStream, replayStream.getEncoding()); + } + + protected TerminalLineSettings getSettings() { + return settings; } /** - * Remove line-buffered input by invoking "stty -icanon min 1" - * against the current terminal. + * 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); - } + @Override + public void init() throws Exception { + super.init(); - checkBackspace(); + setAnsiSupported(true); // set the console to be character-buffered instead of line-buffered - stty("-icanon min 1"); - - // disable character echoing - stty("-echo"); - echoEnabled = false; + settings.set("-icanon min 1"); - // 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); - } + setEchoEnabled(false); } /** @@ -117,315 +77,156 @@ public class UnixTerminal extends Terminal { * 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 = '\b'; - else if (c == '\b') - 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) { - //also the escape key is 27 - //thats why we read until we - //have something different than 27 - //this is a bugfix, because otherwise - //pressing escape and than an arrow key - //was an undefined state - while (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; + @Override + public void restore() throws Exception { + settings.restore(); + super.restore(); + // print a newline after the terminal exits. + // this should probably be a configurable. + System.out.println(); } /** - * No-op for exceptions we want to silently consume. + * Returns the value of <tt>stty columns</tt> param. */ - private void consumeException(Throwable e) { - } - - public boolean isSupported() { - return true; - } - - public boolean getEcho() { - return false; + @Override + public int getWidth() { + int w = settings.getProperty("columns"); + return w < 1 ? DEFAULT_WIDTH : w; } /** - * 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. + * Returns the value of <tt>stty rows>/tt> param. */ - public int getTerminalWidth() { - int val = -1; - - try { - val = getTerminalProperty("columns"); - } catch (Exception e) { - } - - if (val == -1) { - val = 80; - } - - return val; + @Override + public int getHeight() { + int h = settings.getProperty("rows"); + return h < 1 ? DEFAULT_HEIGHT : h; } - /** - * 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; - + @Override + public synchronized void setEchoEnabled(final boolean enabled) { try { - val = getTerminalProperty("rows"); - } catch (Exception e) { + if (enabled) { + settings.set("echo"); + } + else { + settings.set("-echo"); + } + super.setEchoEnabled(enabled); } - - if (val == -1) { - val = 24; + catch (Exception e) { + Log.error("Failed to ", (enabled ? "enable" : "disable"), " echo: ", e); } - - return val; } - private static int getTerminalProperty(String prop) - throws IOException, InterruptedException { - // 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; - String props = stty("-a"); - - for (StringTokenizer tok = new StringTokenizer(props, ";\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(" "); + @Override + public int readVirtualKey(final InputStream in) throws IOException { + int c = readCharacter(in); - return Integer.parseInt(str.substring(0, index).trim()); - } + if (Key.valueOf(c) == DELETE && settings.getProperty("erase") == DELETE.code) { + c = BACKSPACE.code; } - return -1; - } + UnixKey key = UnixKey.valueOf(c); - /** - * Execute the stty command with the specified arguments - * against the current active terminal. - */ - private 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; + // in Unix terminals, arrow keys are represented by a sequence of 3 characters. E.g., the up arrow key yields 27, 91, 68 + if (key == ARROW_START) { + // also the escape key is 27 thats why we read until we have something different than 27 + // this is a bugfix, because otherwise pressing escape and than an arrow key was an undefined state + while (key == ARROW_START) { + c = readCharacter(in); + key = UnixKey.valueOf(c); + } - in = p.getInputStream(); + if (key == ARROW_PREFIX || key == O_PREFIX) { + c = readCharacter(in); + key = UnixKey.valueOf(c); - while ((c = in.read()) != -1) { - bout.write(c); + if (key == ARROW_UP) { + return CTRL_P.code; + } + else if (key == ARROW_DOWN) { + return CTRL_N.code; + } + else if (key == ARROW_LEFT) { + return CTRL_B.code; + } + else if (key == ARROW_RIGHT) { + return CTRL_F.code; + } + else if (key == HOME_CODE) { + return CTRL_A.code; + } + else if (key == END_CODE) { + return CTRL_E.code; + } + else if (key == DEL_THIRD) { + readCharacter(in); // read 4th & ignore + return DELETE.code; + } + } } - in = p.getErrorStream(); - - while ((c = in.read()) != -1) { - bout.write(c); + // 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(); } - p.waitFor(); - - String result = new String(bout.toByteArray()); - - return result; + return c; } /** - * The command to use to set the terminal options. Defaults - * to "stty", or the value of the system property "jline.sttyCommand". + * Unix keys. */ - public static void setSttyCommand(String cmd) { - sttyCommand = cmd; - } + public static enum UnixKey + { + ARROW_START(27), - /** - * 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; - } + ARROW_PREFIX(91), - public synchronized boolean isEchoEnabled() { - return echoEnabled; - } + ARROW_LEFT(68), + ARROW_RIGHT(67), - public synchronized void enableEcho() { - try { - stty("echo"); - echoEnabled = true; - } catch (Exception e) { - consumeException(e); - } - } + ARROW_UP(65), - public synchronized void disableEcho() { - try { - stty("-echo"); - echoEnabled = false; - } catch (Exception e) { - consumeException(e); - } - } + ARROW_DOWN(66), - /** - * 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; + O_PREFIX(79), - final String encoding; + HOME_CODE(72), - public ReplayPrefixOneCharInputStream(String encoding) { - this.encoding = encoding; - } + END_CODE(70), - 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; - } + DEL_THIRD(51), + + DEL_SECOND(126),; + public final short code; - 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); + UnixKey(final int code) { + this.code = (short) code; } - public int read() throws IOException { - if (available() == 0) - return -1; + private static final Map<Short, UnixKey> codes; - byteRead++; + static { + Map<Short, UnixKey> map = new HashMap<Short, UnixKey>(); - if (byteRead == 1) - return firstByte; + for (UnixKey key : UnixKey.values()) { + map.put(key.code, key); + } - return wrappedStream.read(); + codes = map; } - /** - * 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; + public static UnixKey valueOf(final int code) { + return codes.get((short) code); } } -} +}
\ No newline at end of file diff --git a/src/jline/src/main/java/jline/UnsupportedTerminal.java b/src/jline/src/main/java/jline/UnsupportedTerminal.java index 2d87a18f6c..55c399c311 100644 --- a/src/jline/src/main/java/jline/UnsupportedTerminal.java +++ b/src/jline/src/main/java/jline/UnsupportedTerminal.java @@ -4,95 +4,22 @@ * 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; +package jline; /** - * A no-op unsupported terminal. + * An unsupported terminal. * - * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> + * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> + * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> + * @since 2.0 */ -public class UnsupportedTerminal extends 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; +public class UnsupportedTerminal + extends TerminalSupport +{ + public UnsupportedTerminal() { + super(false); + setAnsiSupported(false); + setEchoEnabled(true); } } diff --git a/src/jline/src/main/java/jline/WindowsTerminal.java b/src/jline/src/main/java/jline/WindowsTerminal.java index 5d603cbcf0..dcaa3a9cde 100644 --- a/src/jline/src/main/java/jline/WindowsTerminal.java +++ b/src/jline/src/main/java/jline/WindowsTerminal.java @@ -4,16 +4,30 @@ * 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.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.HashMap; +import java.util.Map; + +import jline.internal.Configuration; +import org.fusesource.jansi.internal.WindowsSupport; + +import jline.internal.Log; +import jline.internal.ReplayPrefixOneCharInputStream; -import jline.UnixTerminal.ReplayPrefixOneCharInputStream; +import static jline.WindowsTerminal.ConsoleMode.*; +import static jline.WindowsTerminal.WindowsKey.*; +import static jline.console.Key.*; /** - * <p> * Terminal implementation for Microsoft Windows. Terminal initialization in - * {@link #initializeTerminal} is accomplished by extracting the + * {@link #init} is accomplished by extracting the * <em>jline_<i>version</i>.dll</em>, saving it to the system temporary * directoy (determined by the setting of the <em>java.io.tmpdir</em> System * property), loading the library, and then calling the Win32 APIs <a @@ -22,8 +36,7 @@ import jline.UnixTerminal.ReplayPrefixOneCharInputStream; * <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/> * <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 @@ -31,483 +44,423 @@ import jline.UnixTerminal.ReplayPrefixOneCharInputStream; * 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.disableDirectConsole</code> system property - * to <code>true</code>. + * the <code>jline.WindowsTerminal.directConsole</code> system property + * to <code>false</code>. * </p> * * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> + * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> + * @since 2.0 */ -public class WindowsTerminal extends Terminal { - // constants copied from wincon.h +public class WindowsTerminal + extends TerminalSupport +{ + public static final String JLINE_WINDOWS_TERMINAL_INPUT_ENCODING = "jline.WindowsTerminal.input.encoding"; - /** - * 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; + public static final String JLINE_WINDOWS_TERMINAL_OUTPUT_ENCODING = "jline.WindowsTerminal.output.encoding"; - /** - * 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; + public static final String JLINE_WINDOWS_TERMINAL_DIRECT_CONSOLE = "jline.WindowsTerminal.directConsole"; - /** - * 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; + public static final String WINDOWSBINDINGS_PROPERTIES = "windowsbindings.properties"; - /** - * 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; + public static final String ANSI = WindowsTerminal.class.getName() + ".ansi"; - /** - * 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; + private boolean directConsole; - /** - * 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; + private int originalMode; - /** - * 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; + private final ReplayPrefixOneCharInputStream replayStream; - /** - * 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; + private final InputStreamReader replayReader; - /** - * 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; + public WindowsTerminal() throws Exception { + super(true); - /** - * 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; + this.replayStream = + new ReplayPrefixOneCharInputStream(Configuration.getString(JLINE_WINDOWS_TERMINAL_INPUT_ENCODING, Configuration.getFileEncoding())); + this.replayReader = new InputStreamReader(replayStream, replayStream.getEncoding()); + } - /** - * 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; + @Override + public void init() throws Exception { + super.init(); - /** - * 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; + setAnsiSupported(Boolean.getBoolean(ANSI)); - /** - * 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; + // + // FIXME: Need a way to disable direct console and sysin detection muck + // - /** - * 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; + setDirectConsole(Boolean.getBoolean(JLINE_WINDOWS_TERMINAL_DIRECT_CONSOLE)); - /** - * 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; + this.originalMode = getConsoleMode(); + setConsoleMode(originalMode & ~ENABLE_ECHO_INPUT.code); + setEchoEnabled(false); + } /** - * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR - * this character indicates that - * the end key was pressed. + * 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 static final char END_KEY = 79; + @Override + public void restore() throws Exception { + // restore the old console mode + setConsoleMode(originalMode); + super.restore(); + } - /** - * 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; + @Override + public int getWidth() { + int w = getWindowsTerminalWidth(); + return w < 1 ? DEFAULT_WIDTH : w; + } - /** - * 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; + @Override + public int getHeight() { + int h = getWindowsTerminalHeight(); + return h < 1 ? DEFAULT_HEIGHT : h; + } + + @Override + public void setEchoEnabled(final boolean enabled) { + // Must set these four modes at the same time to make it work fine. + if (enabled) { + setConsoleMode(getConsoleMode() | + ENABLE_ECHO_INPUT.code | + ENABLE_LINE_INPUT.code | + ENABLE_PROCESSED_INPUT.code | + ENABLE_WINDOW_INPUT.code); + } + else { + setConsoleMode(getConsoleMode() & + ~(ENABLE_LINE_INPUT.code | + ENABLE_ECHO_INPUT.code | + ENABLE_PROCESSED_INPUT.code | + ENABLE_WINDOW_INPUT.code)); + } + super.setEchoEnabled(enabled); + } /** - * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR - * this character indicates that - * the insert key was pressed. + * Whether or not to allow the use of the JNI console interaction. */ - public static final char INSERT_KEY = 82; + public void setDirectConsole(final boolean flag) { + this.directConsole = flag; + Log.debug("Direct console: ", flag); + } /** - * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR, - * this character indicates that the escape key was pressed. + * Whether or not to allow the use of the JNI console interaction. */ - 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); - } - + public Boolean getDirectConsole() { + return directConsole; } - private native int getConsoleMode(); - - private native void setConsoleMode(final int mode); - - private native int readByte(); - - private native int getWindowsTerminalWidth(); - - private native int getWindowsTerminalHeight(); + @Override public int readCharacter(final InputStream in) throws IOException { // if we can detect that we are directly wrapping the system // input, then bypass the input stream and read directly (which // allows us to access otherwise unreadable strokes, such as // the arrow keys) - if (directConsole == Boolean.FALSE) { - return super.readCharacter(in); - } else if ((directConsole == Boolean.TRUE) - || ((in == System.in) || (in instanceof FileInputStream - && (((FileInputStream) in).getFD() == FileDescriptor.in)))) { + + if (directConsole || isSystemIn(in)) { return readByte(); - } else { + } + 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 boolean isSystemIn(final InputStream in) throws IOException { + assert in != null; + + if (in == System.in) { + return true; + } + else if (in instanceof FileInputStream && ((FileInputStream) in).getFD() == FileDescriptor.in) { + return true; } - } - private void loadLibrary(final String name) throws IOException { - // store the DLL in the temporary directory for the System - String version = getClass().getPackage().getImplementationVersion(); + return false; + } - if (version == null) { - version = ""; - } + @Override + public int readVirtualKey(final InputStream in) throws IOException { + int indicator = readCharacter(in); - version = version.replace('.', '_'); + // in Windows terminals, arrow keys are represented by + // a sequence of 2 characters. E.g., the up arrow + // key yields 224, 72 + if (indicator == SPECIAL_KEY_INDICATOR.code || indicator == NUMPAD_KEY_INDICATOR.code) { + int c = readCharacter(in); + WindowsKey key = WindowsKey.valueOf(c); - File f = new File(System.getProperty("java.io.tmpdir"), name + "_" - + version + ".dll"); - boolean exists = f.isFile(); // check if it already exists + switch (key) { + case UP_ARROW_KEY: + return CTRL_P.code; // translate UP -> CTRL-P - // extract the embedded jline.dll file from the jar and save - // it to the current directory - int bits = 32; + case LEFT_ARROW_KEY: + return CTRL_B.code; // translate LEFT -> CTRL-B - // check for 64-bit systems and use to appropriate DLL - if (System.getProperty("os.arch").indexOf("64") != -1) - bits = 64; + case RIGHT_ARROW_KEY: + return CTRL_F.code; // translate RIGHT -> CTRL-F - InputStream in = new BufferedInputStream(getClass() - .getResourceAsStream(name + bits + ".dll")); + case DOWN_ARROW_KEY: + return CTRL_N.code; // translate DOWN -> CTRL-N - try { - OutputStream fout = new BufferedOutputStream( - new FileOutputStream(f)); - byte[] bytes = new byte[1024 * 10]; + case DELETE_KEY: + return CTRL_QM.code; // translate DELETE -> CTRL-? - for (int n = 0; n != -1; n = in.read(bytes)) { - fout.write(bytes, 0, n); - } + case HOME_KEY: + return CTRL_A.code; - fout.close(); - } 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; - } - } + case END_KEY: + return CTRL_E.code; - // try to clean up the DLL after the JVM exits - f.deleteOnExit(); + case PAGE_UP_KEY: + return CTRL_K.code; - // now actually load the DLL - System.load(f.getAbsolutePath()); - } + case PAGE_DOWN_KEY: + return CTRL_L.code; - public int readVirtualKey(InputStream in) throws IOException { - int indicator = readCharacter(in); + case ESCAPE_KEY: + return CTRL_OB.code; // translate ESCAPE -> CTRL-[ - // 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); + case INSERT_KEY: + return CTRL_C.code; - 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; + 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(); + } + 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; + @Override + public InputStream getDefaultBindings() { + return WindowsTerminal.class.getResourceAsStream(WINDOWSBINDINGS_PROPERTIES); } - public boolean getEcho() { - return false; + // + // Native Bits + // + private int getConsoleMode() { + return WindowsSupport.getConsoleMode(); } - /** - * Unsupported; return the default. - * - * @see Terminal#getTerminalWidth - */ - public int getTerminalWidth() { - return getWindowsTerminalWidth(); + private void setConsoleMode(int mode) { + WindowsSupport.setConsoleMode(mode); } - /** - * Unsupported; return the default. - * - * @see Terminal#getTerminalHeight - */ - public int getTerminalHeight() { - return getWindowsTerminalHeight(); + private int readByte() { + return WindowsSupport.readByte(); } - /** - * No-op for exceptions we want to silently consume. - */ - private void consumeException(final Throwable e) { + private int getWindowsTerminalWidth() { + return WindowsSupport.getWindowsTerminalWidth(); } - /** - * Whether or not to allow the use of the JNI console interaction. - */ - public void setDirectConsole(Boolean directConsole) { - this.directConsole = directConsole; + private int getWindowsTerminalHeight() { + return WindowsSupport.getWindowsTerminalHeight(); } /** - * Whether or not to allow the use of the JNI console interaction. + * Console mode + * <p/> + * Constants copied <tt>wincon.h</tt>. */ - public Boolean getDirectConsole() { - return this.directConsole; - } + public static enum ConsoleMode + { + /** + * The ReadFile or ReadConsole function returns only when a carriage return + * character is read. If this mode is disable, the functions return when one + * or more characters are available. + */ + ENABLE_LINE_INPUT(2), - public synchronized boolean isEchoEnabled() { - return echoEnabled; - } + /** + * Characters read by the ReadFile or ReadConsole function are written to + * the active screen buffer as they are read. This mode can be used only if + * the ENABLE_LINE_INPUT mode is also enabled. + */ + ENABLE_ECHO_INPUT(4), - 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; - } + /** + * CTRL+C is processed by the system and is not placed in the input buffer. + * If the input buffer is being read by ReadFile or ReadConsole, other + * control keys are processed by the system and are not returned in the + * ReadFile or ReadConsole buffer. If the ENABLE_LINE_INPUT mode is also + * enabled, backspace, carriage return, and linefeed characters are handled + * by the system. + */ + ENABLE_PROCESSED_INPUT(1), - 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; - } + /** + * User interactions that change the size of the console screen buffer are + * reported in the console's input buffee. Information about these events + * can be read from the input buffer by applications using + * theReadConsoleInput function, but not by those using ReadFile + * orReadConsole. + */ + ENABLE_WINDOW_INPUT(8), - public InputStream getDefaultBindings() { - return getClass().getResourceAsStream("windowsbindings.properties"); + /** + * If the mouse pointer is within the borders of the console window and the + * window has the keyboard focus, mouse events generated by mouse movement + * and button presses are placed in the input buffer. These events are + * discarded by ReadFile or ReadConsole, even when this mode is enabled. + */ + ENABLE_MOUSE_INPUT(16), + + /** + * When enabled, text entered in a console window will be inserted at the + * current cursor location and all text following that location will not be + * overwritten. When disabled, all following text will be overwritten. An OR + * operation must be performed with this flag and the ENABLE_EXTENDED_FLAGS + * flag to enable this functionality. + */ + ENABLE_PROCESSED_OUTPUT(1), + + /** + * This flag enables the user to use the mouse to select and edit text. To + * enable this option, use the OR to combine this flag with + * ENABLE_EXTENDED_FLAGS. + */ + ENABLE_WRAP_AT_EOL_OUTPUT(2),; + + public final int code; + + ConsoleMode(final int code) { + this.code = code; + } } /** - * 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> + * Windows keys. + * <p/> + * Constants copied <tt>wincon.h</tt>. */ - static class ReplayPrefixOneCharInputStream extends InputStream { - byte firstByte; - int byteLength; - InputStream wrappedStream; - int byteRead; + public static enum WindowsKey + { + /** + * On windows terminals, this character indicates that a 'special' key has + * been pressed. This means that a key such as an arrow key, or delete, or + * home, etc. will be indicated by the next character. + */ + SPECIAL_KEY_INDICATOR(224), - final String encoding; + /** + * On windows terminals, this character indicates that a special key on the + * number pad has been pressed. + */ + NUMPAD_KEY_INDICATOR(0), - public ReplayPrefixOneCharInputStream(String encoding) { - this.encoding = encoding; - } + /** + * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR, + * this character indicates an left arrow key press. + */ + LEFT_ARROW_KEY(75), - 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; - } + /** + * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR + * this character indicates an + * right arrow key press. + */ + RIGHT_ARROW_KEY(77), + + /** + * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR + * this character indicates an up + * arrow key press. + */ + UP_ARROW_KEY(72), + /** + * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR + * this character indicates an + * down arrow key press. + */ + DOWN_ARROW_KEY(80), + + /** + * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR + * this character indicates that + * the delete key was pressed. + */ + DELETE_KEY(83), + + /** + * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR + * this character indicates that + * the home key was pressed. + */ + HOME_KEY(71), - 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); + /** + * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR + * this character indicates that + * the end key was pressed. + */ + END_KEY(79), + + /** + * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR + * this character indicates that + * the page up key was pressed. + */ + PAGE_UP_KEY(73), + + /** + * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR + * this character indicates that + * the page down key was pressed. + */ + PAGE_DOWN_KEY(81), + + /** + * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR + * this character indicates that + * the insert key was pressed. + */ + INSERT_KEY(82), + + /** + * When following the SPECIAL_KEY_INDICATOR or NUMPAD_KEY_INDICATOR, + * this character indicates that the escape key was pressed. + */ + ESCAPE_KEY(0),; + + public final int code; + + WindowsKey(final int code) { + this.code = code; } - public int read() throws IOException { - if (available() == 0) - return -1; + private static final Map<Integer, WindowsKey> codes; - byteRead++; + static { + Map<Integer, WindowsKey> map = new HashMap<Integer, WindowsKey>(); - if (byteRead == 1) - return firstByte; + for (WindowsKey key : WindowsKey.values()) { + map.put(key.code, key); + } - return wrappedStream.read(); + codes = map; } - /** - * 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; + public static WindowsKey valueOf(final int code) { + return codes.get(code); } } - } diff --git a/src/jline/src/main/java/jline/console/ConsoleReader.java b/src/jline/src/main/java/jline/console/ConsoleReader.java new file mode 100644 index 0000000000..fe9e85aed9 --- /dev/null +++ b/src/jline/src/main/java/jline/console/ConsoleReader.java @@ -0,0 +1,2103 @@ +/* + * 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.console; + +import jline.Terminal; +import jline.TerminalFactory; +import jline.console.completer.CandidateListCompletionHandler; +import jline.console.completer.Completer; +import jline.console.completer.CompletionHandler; +import jline.console.history.History; +import jline.console.history.MemoryHistory; +import jline.internal.Configuration; +import jline.internal.Log; +import org.fusesource.jansi.AnsiOutputStream; + +import java.awt.Toolkit; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.event.ActionListener; +import java.io.*; +import java.util.*; + +/** + * A reader for console applications. It supports custom tab-completion, + * saveable command history, and command line editing. On some platforms, + * platform-specific commands will need to be issued before the reader will + * function properly. See {@link jline.Terminal#init} for convenience + * methods for issuing platform-specific setup commands. + * + * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> + * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> + */ +public class ConsoleReader +{ + public static final String JLINE_NOBELL = "jline.nobell"; + + public static final String JLINE_EXPANDEVENTS = "jline.expandevents"; + + public static final char BACKSPACE = '\b'; + + public static final char RESET_LINE = '\r'; + + public static final char KEYBOARD_BELL = '\07'; + + public static final char NULL_MASK = 0; + + public static final int TAB_WIDTH = 4; + + private static final ResourceBundle + resources = ResourceBundle.getBundle(CandidateListCompletionHandler.class.getName()); + + private final Terminal terminal; + + private InputStream in; + + private final Writer out; + + private final CursorBuffer buf = new CursorBuffer(); + + private String prompt; + + private boolean bellEnabled = true; + + private boolean expandEvents = false; + + private Character mask; + + private Character echoCharacter; + + private StringBuffer searchTerm = null; + + private String previousSearchTerm = ""; + + private int searchIndex = -1; + + public ConsoleReader(final InputStream in, final OutputStream out, final InputStream bindings, final Terminal term) throws + IOException + { + this.in = in; + this.terminal = term != null ? term : TerminalFactory.get(); + this.out = new PrintWriter(terminal.wrapOutIfNeeded(out)); + this.keyBindings = loadKeyBindings(bindings); + + setBellEnabled(!Configuration.getBoolean(JLINE_NOBELL, false)); + setExpandEvents(Configuration.getBoolean(JLINE_EXPANDEVENTS, false)); + } + + /** + * @deprecated use {@link #ConsoleReader(InputStream, OutputStream, InputStream, Terminal)} + * to let the terminal wrap the output stream if needed. + */ + public ConsoleReader(final InputStream in, final Writer out, final InputStream bindings, final Terminal term) throws + IOException + { + this.in = in; + this.out = out; + this.terminal = term != null ? term : TerminalFactory.get(); + this.keyBindings = loadKeyBindings(bindings); + + setBellEnabled(!Configuration.getBoolean(JLINE_NOBELL, false)); + } + + /** + * @deprecated use {@link #ConsoleReader(InputStream, OutputStream, InputStream, Terminal)} + * to let the terminal wrap the output stream if needed. + */ + public ConsoleReader(final InputStream in, final Writer out, final Terminal term) throws IOException { + this(in, out, null, term); + } + + /** + * @deprecated use {@link #ConsoleReader(InputStream, OutputStream, InputStream, Terminal)} + * to let the terminal wrap the output stream if needed. + */ + public ConsoleReader(final InputStream in, final Writer out) throws IOException + { + this(in, out, null, null); + } + + /** + * Create a new reader using {@link FileDescriptor#in} for input and + * {@link System#out} for output. + * <p/> + * {@link FileDescriptor#in} is used because it has a better chance of not being buffered. + */ + public ConsoleReader() throws IOException { + this(new FileInputStream(FileDescriptor.in), System.out, null, null ); + } + + // FIXME: Only used for tests + + void setInput(final InputStream in) { + this.in = in; + } + + public InputStream getInput() { + return in; + } + + public Writer getOutput() { + return out; + } + + public Terminal getTerminal() { + return terminal; + } + + public CursorBuffer getCursorBuffer() { + return buf; + } + + public void setBellEnabled(final boolean enabled) { + this.bellEnabled = enabled; + } + + public boolean isBellEnabled() { + return bellEnabled; + } + + public void setExpandEvents(final boolean expand) { + this.expandEvents = expand; + } + + public boolean getExpandEvents() { + return expandEvents; + } + + public void setPrompt(final String prompt) { + this.prompt = prompt; + } + + public String getPrompt() { + return prompt; + } + + /** + * Set the echo character. For example, to have "*" entered when a password is typed: + * <p/> + * <pre> + * myConsoleReader.setEchoCharacter(new Character('*')); + * </pre> + * <p/> + * Setting the character to + * <p/> + * <pre> + * null + * </pre> + * <p/> + * will restore normal character echoing. Setting the character to + * <p/> + * <pre> + * new Character(0) + * </pre> + * <p/> + * will cause nothing to be echoed. + * + * @param c the character to echo to the console in place of the typed character. + */ + public void setEchoCharacter(final Character c) { + this.echoCharacter = c; + } + + /** + * Returns the echo character. + */ + public Character getEchoCharacter() { + return echoCharacter; + } + + /** + * Erase the current line. + * + * @return false if we failed (e.g., the buffer was empty) + */ + final boolean resetLine() throws IOException { + if (buf.cursor == 0) { + return false; + } + + backspaceAll(); + + return true; + } + + int getCursorPosition() { + // FIXME: does not handle anything but a line with a prompt absolute position + String prompt = getPrompt(); + return ((prompt == null) ? 0 : stripAnsi(lastLine(prompt)).length()) + buf.cursor; + } + + /** + * Returns the text after the last '\n'. + * prompt is returned if no '\n' characters are present. + * null is returned if prompt is null. + */ + private String lastLine(String str) { + if (str == null) return ""; + int last = str.lastIndexOf("\n"); + + if (last >= 0) { + return str.substring(last + 1, str.length()); + } + + return str; + } + + private String stripAnsi(String str) { + if (str == null) return ""; + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + AnsiOutputStream aos = new AnsiOutputStream(baos); + aos.write(str.getBytes()); + aos.flush(); + return baos.toString(); + } catch (IOException e) { + return str; + } + } + + /** + * Move the cursor position to the specified absolute index. + */ + public final boolean setCursorPosition(final int position) throws IOException { + return moveCursor(position - buf.cursor) != 0; + } + + /** + * Set the current buffer's content to the specified {@link String}. The + * visual console will be modified to show the current buffer. + * + * @param buffer the new contents of the buffer. + */ + private void setBuffer(final String buffer) throws IOException { + // don't bother modifying it if it is unchanged + if (buffer.equals(buf.buffer.toString())) { + return; + } + + // obtain the difference between the current buffer and the new one + int sameIndex = 0; + + for (int i = 0, l1 = buffer.length(), l2 = buf.buffer.length(); (i < l1) + && (i < l2); i++) { + if (buffer.charAt(i) == buf.buffer.charAt(i)) { + sameIndex++; + } + else { + break; + } + } + + int diff = buf.cursor - sameIndex; + if (diff < 0) { // we can't backspace here so try from the end of the buffer + moveToEnd(); + diff = buf.buffer.length() - sameIndex; + } + + backspace(diff); // go back for the differences + killLine(); // clear to the end of the line + buf.buffer.setLength(sameIndex); // the new length + putString(buffer.substring(sameIndex)); // append the differences + } + + private void setBuffer(final CharSequence buffer) throws IOException { + setBuffer(String.valueOf(buffer)); + } + + /** + * Output put the prompt + the current buffer + */ + public final void drawLine() throws IOException { + String prompt = getPrompt(); + if (prompt != null) { + print(prompt); + } + + print(buf.buffer.toString()); + + if (buf.length() != buf.cursor) { // not at end of line + back(buf.length() - buf.cursor - 1); + } + } + + /** + * Clear the line and redraw it. + */ + public final void redrawLine() throws IOException { + print(RESET_LINE); +// flush(); + drawLine(); + } + + /** + * Clear the buffer and add its contents to the history. + * + * @return the former contents of the buffer. + */ + final String finishBuffer() throws IOException { // FIXME: Package protected because used by tests + String str = buf.buffer.toString(); + + if (expandEvents) { + str = expandEvents(str); + } + + // we only add it to the history if the buffer is not empty + // and if mask is null, since having a mask typically means + // the string was a password. We clear the mask after this call + if (str.length() > 0) { + if (mask == null && isHistoryEnabled()) { + history.add(str); + } + else { + mask = null; + } + } + + history.moveToEnd(); + + buf.buffer.setLength(0); + buf.cursor = 0; + + return str; + } + + /** + * Expand event designator such as !!, !#, !3, etc... + * See http://www.gnu.org/software/bash/manual/html_node/Event-Designators.html + * + * @param str + * @return + */ + final String expandEvents(String str) throws IOException { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + switch (c) { + case '!': + if (i + 1 < str.length()) { + c = str.charAt(++i); + boolean neg = false; + String rep = null; + int i1, idx; + switch (c) { + case '!': + if (history.size() == 0) { + throw new IllegalArgumentException("!!: event not found"); + } + rep = history.get(history.index() - 1).toString(); + break; + case '#': + sb.append(sb.toString()); + break; + case '?': + i1 = str.indexOf('?', i + 1); + if (i1 < 0) { + i1 = str.length(); + } + String sc = str.substring(i + 1, i1); + i = i1; + idx = searchBackwards(sc); + if (idx < 0) { + throw new IllegalArgumentException("!?" + sc + ": event not found"); + } else { + rep = history.get(idx).toString(); + } + break; + case ' ': + case '\t': + sb.append('!'); + sb.append(c); + break; + case '-': + neg = true; + i++; + // fall through + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + i1 = i; + for (; i < str.length(); i++) { + c = str.charAt(i); + if (c < '0' || c > '9') { + break; + } + } + idx = 0; + try { + idx = Integer.parseInt(str.substring(i1, i)); + } catch (NumberFormatException e) { + throw new IllegalArgumentException((neg ? "!-" : "!") + str.substring(i1, i) + ": event not found"); + } + if (neg) { + if (idx < history.size()) { + rep = (history.get(history.index() - idx)).toString(); + } else { + throw new IllegalArgumentException((neg ? "!-" : "!") + str.substring(i1, i) + ": event not found"); + } + } else { + if (idx >= history.index() - history.size() && idx < history.index()) { + rep = (history.get(idx)).toString(); + } else { + throw new IllegalArgumentException((neg ? "!-" : "!") + str.substring(i1, i) + ": event not found"); + } + } + break; + default: + String ss = str.substring(i); + i = str.length(); + idx = searchBackwards(ss, history.index(), true); + if (idx < 0) { + throw new IllegalArgumentException("!" + ss + ": event not found"); + } else { + rep = history.get(idx).toString(); + } + break; + } + if (rep != null) { + sb.append(rep); + } + } else { + sb.append(c); + } + break; + case '^': + if (i == 0) { + int i1 = str.indexOf('^', i + 1); + int i2 = str.indexOf('^', i1 + 1); + if (i2 < 0) { + i2 = str.length(); + } + if (i1 > 0 && i2 > 0) { + String s1 = str.substring(i + 1, i1); + String s2 = str.substring(i1 + 1, i2); + String s = history.get(history.index() - 1).toString().replace(s1, s2); + sb.append(s); + i = i2 + 1; + break; + } + } + sb.append(c); + break; + default: + sb.append(c); + break; + } + } + String result = sb.toString(); + if (!str.equals(result)) { + print(result); + println(); + flush(); + } + return result; + + } + + /* Handle case where terminal does not move cursor to the next line + * when a character is inserted at the width of the terminal. This also + * fixes backspace issue, where it assumes that the terminal is doing this. + */ + private final void newlineAtWrap() throws IOException { + if (terminal.newlineAtWrapNeeded()) { + int width = getTerminal().getWidth(); + + if ((getCursorPosition() % width == 0) && getCurrentPosition() >= width) + println(); + } + } + + /** + * Write out the specified string to the buffer and the output stream. + */ + public final void putString(final CharSequence str) throws IOException { + buf.write(str); + print(str); + drawBuffer(); + newlineAtWrap(); + } + + /** + * Output the specified character, both to the buffer and the output stream. + */ + private void putChar(final int c, final boolean print) throws IOException { + buf.write((char) c); + + if (print) { + if (mask == null) { + // no masking + print(c); + } + else if (mask == NULL_MASK) { + // Don't print anything + } + else { + print(mask); + } + + drawBuffer(); + newlineAtWrap(); + } + } + + /** + * Redraw the rest of the buffer from the cursor onwards. This is necessary + * for inserting text into the buffer. + * + * @param clear the number of characters to clear after the end of the buffer + */ + private void drawBuffer(final int clear) throws IOException { + // debug ("drawBuffer: " + clear); + if (buf.cursor == buf.length() && clear == 0) { + return; + } + char[] chars = buf.buffer.substring(buf.cursor).toCharArray(); + if (mask != null) { + Arrays.fill(chars, mask); + } + + print(chars); + clearAhead(clear); + if (terminal.isAnsiSupported()) { + if (chars.length > 0) { + // don't ask, it works + //back(Math.max(chars.length - 1, 1), true); + back(chars.length, true); + } + } else { + back(chars.length); + } +// flush(); + } + + /** + * Redraw the rest of the buffer from the cursor onwards. This is necessary + * for inserting text into the buffer. + */ + private void drawBuffer() throws IOException { + drawBuffer(0); + } + + /** + * Clear ahead the specified number of characters without moving the cursor. + */ + private void clearAhead(final int num) throws IOException { + if (num == 0) { + return; + } + + if (terminal.isAnsiSupported()) { + printAnsiSequence("K"); + // if cursor+num wraps, then we need to clear the line(s) below too + int width = terminal.getWidth(); + int cursor = getCursorPosition(); + int curCol = cursor % width; + int endCol = (cursor + num) % width; + int lines = num / width; + if (endCol < curCol) lines++; + for (int i = 0; i < lines; i++) { + printAnsiSequence("B"); + printAnsiSequence("2K"); + } + for (int i = 0; i < lines; i++) { + printAnsiSequence("A"); + } + return; + } + + // print blank extra characters + print(' ', num); + + // we need to flush here so a "clever" console doesn't just ignore the redundancy + // of a space followed by a backspace. +// flush(); + + // reset the visual cursor + back(num, true); + +// flush(); + } + + private void back(final int num) throws IOException { + back(num, false); + } + + /** + * Move the visual cursor backwards without modifying the buffer cursor. + * @param printed if true some characters were printed and we have to deal + * with new line at wrap + */ + private void back(final int num, boolean printed) throws IOException { + if (num == 0) return; + if (terminal.isAnsiSupported()) { + int width = getTerminal().getWidth(); + int cursor = getCursorPosition(); + int realCursor = cursor + num; + // adjust cursor if it did not wrapped on its own + if (printed && terminal.newlineAtWrapNeeded() && realCursor%width == 0) { + realCursor--; + } + int realCol = realCursor % width; + int newCol = cursor % width; + int moveup = num / width; + int delta = realCol - newCol; + if (delta < 0) moveup++; + if (moveup > 0) { + printAnsiSequence(moveup + "A"); + } + printAnsiSequence((1 + newCol) + "G"); + return; + } + print(BACKSPACE, num); +// flush(); + } + + /** + * Flush the console output stream. This is important for printout out single characters (like a backspace or + * keyboard) that we want the console to handle immediately. + */ + public void flush() throws IOException { + out.flush(); + } + + private int backspaceAll() throws IOException { + return backspace(Integer.MAX_VALUE); + } + + /** + * Issue <em>num</em> backspaces. + * + * @return the number of characters backed up + */ + private int backspace(final int num) throws IOException { + if (buf.cursor == 0) { + return 0; + } + + int count = 0; + + int termwidth = getTerminal().getWidth(); + int lines = getCursorPosition() / termwidth; + count = moveCursor(-1 * num) * -1; + buf.buffer.delete(buf.cursor, buf.cursor + count); + if (getCursorPosition() / termwidth != lines) { + if (terminal.isAnsiSupported()) { + // debug("doing backspace redraw: " + getCursorPosition() + " on " + termwidth + ": " + lines); + printAnsiSequence("K"); + } + } + drawBuffer(count); + + return count; + } + + /** + * Issue a backspace. + * + * @return true if successful + */ + public boolean backspace() throws IOException { + return backspace(1) == 1; + } + + private boolean moveToEnd() throws IOException { + return moveCursor(buf.length() - buf.cursor) > 0; + } + + /** + * Delete the character at the current position and redraw the remainder of the buffer. + */ + private boolean deleteCurrentCharacter() throws IOException { + if (buf.length() == 0 || buf.cursor == buf.length()) { + return false; + } + + buf.buffer.deleteCharAt(buf.cursor); + drawBuffer(1); + return true; + } + + private boolean previousWord() throws IOException { + while (isDelimiter(buf.current()) && (moveCursor(-1) != 0)) { + // nothing + } + + while (!isDelimiter(buf.current()) && (moveCursor(-1) != 0)) { + // nothing + } + + return true; + } + + private boolean nextWord() throws IOException { + while (isDelimiter(buf.current()) && (moveCursor(1) != 0)) { + // nothing + } + + while (!isDelimiter(buf.current()) && (moveCursor(1) != 0)) { + // nothing + } + + return true; + } + + private boolean deletePreviousWord() throws IOException { + while (isDelimiter(buf.current()) && backspace()) { + // nothing + } + + while (!isDelimiter(buf.current()) && backspace()) { + // nothing + } + + return true; + } + + /** + * Move the cursor <i>where</i> characters. + * + * @param num If less than 0, move abs(<i>where</i>) to the left, otherwise move <i>where</i> to the right. + * @return The number of spaces we moved + */ + public int moveCursor(final int num) throws IOException { + int where = num; + + if ((buf.cursor == 0) && (where <= 0)) { + return 0; + } + + if ((buf.cursor == buf.buffer.length()) && (where >= 0)) { + return 0; + } + + if ((buf.cursor + where) < 0) { + where = -buf.cursor; + } + else if ((buf.cursor + where) > buf.buffer.length()) { + where = buf.buffer.length() - buf.cursor; + } + + moveInternal(where); + + return where; + } + + /** + * Move the cursor <i>where</i> characters, without checking the current buffer. + * + * @param where the number of characters to move to the right or left. + */ + private void moveInternal(final int where) throws IOException { + // debug ("move cursor " + where + " (" + // + buf.cursor + " => " + (buf.cursor + where) + ")"); + buf.cursor += where; + + if (terminal.isAnsiSupported()) { + if (where < 0) { + back(Math.abs(where)); + } else { + int width = getTerminal().getWidth(); + int cursor = getCursorPosition(); + int oldLine = (cursor - where) / width; + int newLine = cursor / width; + if (newLine > oldLine) { + printAnsiSequence((newLine - oldLine) + "B"); + } + printAnsiSequence(1 +(cursor % width) + "G"); + } +// flush(); + return; + } + + char c; + + if (where < 0) { + int len = 0; + for (int i = buf.cursor; i < buf.cursor - where; i++) { + if (buf.buffer.charAt(i) == '\t') { + len += TAB_WIDTH; + } + else { + len++; + } + } + + char chars[] = new char[len]; + Arrays.fill(chars, BACKSPACE); + out.write(chars); + + return; + } + else if (buf.cursor == 0) { + return; + } + else if (mask != null) { + c = mask; + } + else { + print(buf.buffer.substring(buf.cursor - where, buf.cursor).toCharArray()); + return; + } + + // null character mask: don't output anything + if (mask == NULL_MASK) { + return; + } + + print(c, Math.abs(where)); + } + + // FIXME: replace() is not used + + public final boolean replace(final int num, final String replacement) { + buf.buffer.replace(buf.cursor - num, buf.cursor, replacement); + try { + moveCursor(-num); + drawBuffer(Math.max(0, num - replacement.length())); + moveCursor(replacement.length()); + } + catch (IOException e) { + e.printStackTrace(); + return false; + } + return true; + } + + // + // Key reading + // + + /** + * Read a character from the console. + * + * @return the character, or -1 if an EOF is received. + */ + public final int readVirtualKey() throws IOException { + int c = terminal.readVirtualKey(in); + + Log.trace("Keystroke: ", c); + + // clear any echo characters + clearEcho(c); + + return c; + } + + /** + * Clear the echoed characters for the specified character code. + */ + private int clearEcho(final int c) throws IOException { + // if the terminal is not echoing, then ignore + if (!terminal.isEchoEnabled()) { + return 0; + } + + // otherwise, clear + int num = countEchoCharacters((char) c); + back(num); + drawBuffer(num); + + return num; + } + + private int countEchoCharacters(final char c) { + // tabs as special: we need to determine the number of spaces + // to cancel based on what out current cursor position is + if (c == 9) { + int tabStop = 8; // will this ever be different? + int position = getCursorPosition(); + + return tabStop - (position % tabStop); + } + + return getPrintableCharacters(c).length(); + } + + /** + * Return the number of characters that will be printed when the specified + * character is echoed to the screen + * + * Adapted from cat by Torbjorn Granlund, as repeated in stty by David MacKenzie. + */ + private StringBuilder getPrintableCharacters(final char ch) { + StringBuilder sbuff = new StringBuilder(); + + if (ch >= 32) { + if (ch < 127) { + sbuff.append(ch); + } + else if (ch == 127) { + sbuff.append('^'); + sbuff.append('?'); + } + else { + sbuff.append('M'); + sbuff.append('-'); + + if (ch >= (128 + 32)) { + if (ch < (128 + 127)) { + sbuff.append((char) (ch - 128)); + } + else { + sbuff.append('^'); + sbuff.append('?'); + } + } + else { + sbuff.append('^'); + sbuff.append((char) (ch - 128 + 64)); + } + } + } + else { + sbuff.append('^'); + sbuff.append((char) (ch + 64)); + } + + return sbuff; + } + + public final int readCharacter(final char... allowed) throws IOException { + // if we restrict to a limited set and the current character is not in the set, then try again. + char c; + + Arrays.sort(allowed); // always need to sort before binarySearch + + while (Arrays.binarySearch(allowed, c = (char) readVirtualKey()) < 0) { + // nothing + } + + return c; + } + + // + // Key Bindings + // + + public static final String JLINE_COMPLETION_THRESHOLD = "jline.completion.threshold"; + + public static final String JLINE_KEYBINDINGS = "jline.keybindings"; + + public static final String JLINEBINDINGS_PROPERTIES = ".jlinebindings.properties"; + + /** + * The map for logical operations. + */ + private final short[] keyBindings; + + private short[] loadKeyBindings(InputStream input) throws IOException { + if (input == null) { + try { + File file = new File(Configuration.getUserHome(), JLINEBINDINGS_PROPERTIES); + + String path = Configuration.getString(JLINE_KEYBINDINGS); + if (path != null) { + file = new File(path); + } + + if (file.isFile()) { + Log.debug("Loading user bindings from: ", file); + input = new FileInputStream(file); + } + } + catch (Exception e) { + Log.error("Failed to load user bindings", e); + } + } + + if (input == null) { + Log.debug("Using default bindings"); + input = getTerminal().getDefaultBindings(); + } + + short[] keyBindings = new short[Character.MAX_VALUE * 2]; + + Arrays.fill(keyBindings, Operation.UNKNOWN.code); + + // Loads the key bindings. Bindings file is in the format: + // + // keycode: operation name + + if (input != null) { + input = new BufferedInputStream(input); + Properties p = new Properties(); + p.load(input); + input.close(); + + for (Object key : p.keySet()) { + String val = (String) key; + + try { + short code = Short.parseShort(val); + String name = p.getProperty(val); + Operation op = Operation.valueOf(name); + keyBindings[code] = op.code; + } + catch (NumberFormatException e) { + Log.error("Failed to convert binding code: ", val, e); + } + } + + // hardwired arrow key bindings + // keybindings[VK_UP] = PREV_HISTORY; + // keybindings[VK_DOWN] = NEXT_HISTORY; + // keybindings[VK_LEFT] = PREV_CHAR; + // keybindings[VK_RIGHT] = NEXT_CHAR; + } + + return keyBindings; + } + + int getKeyForAction(final short logicalAction) { + for (int i = 0; i < keyBindings.length; i++) { + if (keyBindings[i] == logicalAction) { + return i; + } + } + + return -1; + } + + int getKeyForAction(final Operation op) { + assert op != null; + return getKeyForAction(op.code); + } + + /** + * Reads the console input and returns an array of the form [raw, key binding]. + */ + private int[] readBinding() throws IOException { + int c = readVirtualKey(); + + if (c == -1) { + return null; + } + + // extract the appropriate key binding + short code = keyBindings[c]; + + Log.trace("Translated: ", c, " -> ", code); + + return new int[]{c, code}; + } + + // + // Line Reading + // + + /** + * Read the next line and return the contents of the buffer. + */ + public String readLine() throws IOException { + return readLine((String) null); + } + + /** + * Read the next line with the specified character mask. If null, then + * characters will be echoed. If 0, then no characters will be echoed. + */ + public String readLine(final Character mask) throws IOException { + return readLine(null, mask); + } + + public String readLine(final String prompt) throws IOException { + return readLine(prompt, null); + } + + /** + * Read a line from the <i>in</i> {@link InputStream}, and return the line + * (without any trailing newlines). + * + * @param prompt The prompt to issue to the console, may be null. + * @return A line that is read from the terminal, or null if there was null input (e.g., <i>CTRL-D</i> + * was pressed). + */ + public String readLine(String prompt, final Character mask) throws IOException { + // prompt may be null + // mask may be null + + // FIXME: This blows, each call to readLine will reset the console's state which doesn't seem very nice. + this.mask = mask; + if (prompt != null) { + setPrompt(prompt); + } + else { + prompt = getPrompt(); + } + + try { + if (!terminal.isSupported()) { + beforeReadLine(prompt, mask); + } + + if (prompt != null && prompt.length() > 0) { + out.write(prompt); + out.flush(); + } + + // if the terminal is unsupported, just use plain-java reading + if (!terminal.isSupported()) { + return readLine(in); + } + + String originalPrompt = this.prompt; + + final int NORMAL = 1; + final int SEARCH = 2; + int state = NORMAL; + + boolean success = true; + + while (true) { + int[] next = readBinding(); + + if (next == null) { + return null; + } + + int c = next[0]; + // int code = next[1]; + Operation code = Operation.valueOf(next[1]); + + if (c == -1) { + return null; + } + + // Search mode. + // + // Note that we have to do this first, because if there is a command + // not linked to a search command, we leave the search mode and fall + // through to the normal state. + if (state == SEARCH) { + int cursorDest = -1; + + switch (code) { + // This doesn't work right now, it seems CTRL-G is not passed + // down correctly. :( + case ABORT: + state = NORMAL; + break; + + case SEARCH_PREV: + if (searchTerm.length() == 0) { + searchTerm.append(previousSearchTerm); + } + + if (searchIndex == -1) { + searchIndex = searchBackwards(searchTerm.toString()); + } else { + searchIndex = searchBackwards(searchTerm.toString(), searchIndex); + } + break; + + case DELETE_PREV_CHAR: + if (searchTerm.length() > 0) { + searchTerm.deleteCharAt(searchTerm.length() - 1); + searchIndex = searchBackwards(searchTerm.toString()); + } + break; + + case UNKNOWN: + searchTerm.appendCodePoint(c); + searchIndex = searchBackwards(searchTerm.toString()); + break; + + default: + // Set buffer and cursor position to the found string. + if (searchIndex != -1) { + history.moveTo(searchIndex); + // set cursor position to the found string + cursorDest = history.current().toString().indexOf(searchTerm.toString()); + } + state = NORMAL; + break; + } + + // if we're still in search mode, print the search status + if (state == SEARCH) { + if (searchTerm.length() == 0) { + printSearchStatus("", ""); + searchIndex = -1; + } else { + if (searchIndex == -1) { + beep(); + } else { + printSearchStatus(searchTerm.toString(), history.get(searchIndex).toString()); + } + } + } + // otherwise, restore the line + else { + restoreLine(originalPrompt, cursorDest); + } + } + + if (state == NORMAL) { + switch (code) { + case EXIT: // ctrl-d + if (buf.buffer.length() == 0) { + return null; + } else { + deleteCurrentCharacter(); + } + break; + + case COMPLETE: // tab + success = complete(); + break; + + case MOVE_TO_BEG: + success = setCursorPosition(0); + break; + + case KILL_LINE: // CTRL-K + success = killLine(); + break; + + case CLEAR_SCREEN: // CTRL-L + success = clearScreen(); + break; + + case KILL_LINE_PREV: // CTRL-U + success = resetLine(); + break; + + case NEWLINE: // enter + moveToEnd(); + println(); // output newline + flush(); + return finishBuffer(); + + case DELETE_PREV_CHAR: // backspace + success = backspace(); + break; + + case DELETE_NEXT_CHAR: // delete + success = deleteCurrentCharacter(); + break; + + case MOVE_TO_END: + success = moveToEnd(); + break; + + case PREV_CHAR: + success = moveCursor(-1) != 0; + break; + + case NEXT_CHAR: + success = moveCursor(1) != 0; + break; + + case NEXT_HISTORY: + success = moveHistory(true); + break; + + case PREV_HISTORY: + success = moveHistory(false); + break; + + case ABORT: + case REDISPLAY: + break; + + case PASTE: + success = paste(); + break; + + case DELETE_PREV_WORD: + success = deletePreviousWord(); + break; + + case PREV_WORD: + success = previousWord(); + break; + + case NEXT_WORD: + success = nextWord(); + break; + + case START_OF_HISTORY: + success = history.moveToFirst(); + if (success) { + setBuffer(history.current()); + } + break; + + case END_OF_HISTORY: + success = history.moveToLast(); + if (success) { + setBuffer(history.current()); + } + break; + + case CLEAR_LINE: + moveInternal(-(buf.cursor)); + killLine(); + break; + + case INSERT: + buf.setOverTyping(!buf.isOverTyping()); + break; + + case SEARCH_PREV: // CTRL-R + if (searchTerm != null) { + previousSearchTerm = searchTerm.toString(); + } + searchTerm = new StringBuffer(buf.buffer); + state = SEARCH; + if (searchTerm.length() > 0) { + searchIndex = searchBackwards(searchTerm.toString()); + if (searchIndex == -1) { + beep(); + } + printSearchStatus(searchTerm.toString(), + searchIndex > -1 ? history.get(searchIndex).toString() : ""); + } else { + searchIndex = -1; + printSearchStatus("", ""); + } + break; + + case UNKNOWN: + default: + if (c != 0) { // ignore null chars + ActionListener action = triggeredActions.get((char) c); + if (action != null) { + action.actionPerformed(null); + } + else { + putChar(c, true); + } + } + else { + success = false; + } + } + + if (!success) { + beep(); + } + + flush(); + } + } + } + finally { + if (!terminal.isSupported()) { + afterReadLine(); + } + } + } + + /** + * Read a line for unsupported terminals. + */ + private String readLine(final InputStream in) throws IOException { + StringBuilder buff = new StringBuilder(); + + while (true) { + int i = in.read(); + + if (i == -1 || i == '\n' || i == '\r') { + return buff.toString(); + } + + buff.append((char) i); + } + + // return new BufferedReader (new InputStreamReader (in)).readLine (); + } + + // + // Completion + // + + private final List<Completer> completers = new LinkedList<Completer>(); + + private CompletionHandler completionHandler = new CandidateListCompletionHandler(); + + /** + * Add the specified {@link jline.console.completer.Completer} to the list of handlers for tab-completion. + * + * @param completer the {@link jline.console.completer.Completer} to add + * @return true if it was successfully added + */ + public boolean addCompleter(final Completer completer) { + return completers.add(completer); + } + + /** + * Remove the specified {@link jline.console.completer.Completer} from the list of handlers for tab-completion. + * + * @param completer The {@link Completer} to remove + * @return True if it was successfully removed + */ + public boolean removeCompleter(final Completer completer) { + return completers.remove(completer); + } + + /** + * Returns an unmodifiable list of all the completers. + */ + public Collection<Completer> getCompleters() { + return Collections.unmodifiableList(completers); + } + + public void setCompletionHandler(final CompletionHandler handler) { + assert handler != null; + this.completionHandler = handler; + } + + public CompletionHandler getCompletionHandler() { + return this.completionHandler; + } + + /** + * Use the completers to modify the buffer with the appropriate completions. + * + * @return true if successful + */ + private boolean complete() throws IOException { + // debug ("tab for (" + buf + ")"); + if (completers.size() == 0) { + return false; + } + + List<CharSequence> candidates = new LinkedList<CharSequence>(); + String bufstr = buf.buffer.toString(); + int cursor = buf.cursor; + + int position = -1; + + for (Completer comp : completers) { + if ((position = comp.complete(bufstr, cursor, candidates)) != -1) { + break; + } + } + + return candidates.size() != 0 && getCompletionHandler().complete(this, candidates, position); + } + + /** + * The number of tab-completion candidates above which a warning will be + * prompted before showing all the candidates. + */ + private int autoprintThreshold = Integer.getInteger(JLINE_COMPLETION_THRESHOLD, 100); // same default as bash + + /** + * @param threshold the number of candidates to print without issuing a warning. + */ + public void setAutoprintThreshold(final int threshold) { + this.autoprintThreshold = threshold; + } + + /** + * @return the number of candidates to print without issuing a warning. + */ + public int getAutoprintThreshold() { + return autoprintThreshold; + } + + private boolean paginationEnabled; + + /** + * Whether to use pagination when the number of rows of candidates exceeds the height of the terminal. + */ + public void setPaginationEnabled(final boolean enabled) { + this.paginationEnabled = enabled; + } + + /** + * Whether to use pagination when the number of rows of candidates exceeds the height of the terminal. + */ + public boolean isPaginationEnabled() { + return paginationEnabled; + } + + // + // History + // + + private History history = new MemoryHistory(); + + public void setHistory(final History history) { + this.history = history; + } + + public History getHistory() { + return history; + } + + private boolean historyEnabled = true; + + /** + * Whether or not to add new commands to the history buffer. + */ + public void setHistoryEnabled(final boolean enabled) { + this.historyEnabled = enabled; + } + + /** + * Whether or not to add new commands to the history buffer. + */ + public boolean isHistoryEnabled() { + return historyEnabled; + } + + /** + * Move up or down the history tree. + */ + private boolean moveHistory(final boolean next) throws IOException { + if (next && !history.next()) { + return false; + } + else if (!next && !history.previous()) { + return false; + } + + setBuffer(history.current()); + + return true; + } + + // + // Printing + // + + public static final String CR = System.getProperty("line.separator"); + + /** + * Output the specified character to the output stream without manipulating the current buffer. + */ + private void print(final int c) throws IOException { + if (c == '\t') { + char chars[] = new char[TAB_WIDTH]; + Arrays.fill(chars, ' '); + out.write(chars); + return; + } + + out.write(c); + } + + /** + * Output the specified characters to the output stream without manipulating the current buffer. + */ + private void print(final char... buff) throws IOException { + int len = 0; + for (char c : buff) { + if (c == '\t') { + len += TAB_WIDTH; + } + else { + len++; + } + } + + char chars[]; + if (len == buff.length) { + chars = buff; + } + else { + chars = new char[len]; + int pos = 0; + for (char c : buff) { + if (c == '\t') { + Arrays.fill(chars, pos, pos + TAB_WIDTH, ' '); + pos += TAB_WIDTH; + } + else { + chars[pos] = c; + pos++; + } + } + } + + out.write(chars); + } + + private void print(final char c, final int num) throws IOException { + if (num == 1) { + print(c); + } + else { + char[] chars = new char[num]; + Arrays.fill(chars, c); + print(chars); + } + } + + /** + * Output the specified string to the output stream (but not the buffer). + */ + public final void print(final CharSequence s) throws IOException { + assert s != null; + print(s.toString().toCharArray()); + } + + public final void println(final CharSequence s) throws IOException { + assert s != null; + print(s.toString().toCharArray()); + println(); + } + + /** + * Output a platform-dependant newline. + */ + public final void println() throws IOException { + print(CR); +// flush(); + } + + // + // Actions + // + + /** + * Issue a delete. + * + * @return true if successful + */ + public final boolean delete() throws IOException { + return delete(1) == 1; + } + + // FIXME: delete(int) only used by above + the return is always 1 and num is ignored + + /** + * Issue <em>num</em> deletes. + * + * @return the number of characters backed up + */ + private int delete(final int num) throws IOException { + // TODO: Try to use jansi for this + + /* Commented out because of DWA-2949: + if (buf.cursor == 0) { + return 0; + } + */ + + buf.buffer.delete(buf.cursor, buf.cursor + 1); + drawBuffer(1); + + return 1; + } + + /** + * Kill the buffer ahead of the current cursor position. + * + * @return true if successful + */ + public boolean killLine() throws IOException { + int cp = buf.cursor; + int len = buf.buffer.length(); + + if (cp >= len) { + return false; + } + + int num = buf.buffer.length() - cp; + clearAhead(num); + + 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; + } + + /** + * Issue an audible keyboard bell, if {@link #isBellEnabled} return true. + */ + public void beep() throws IOException { + if (isBellEnabled()) { + print(KEYBOARD_BELL); + // need to flush so the console actually beeps + flush(); + } + } + + /** + * Paste the contents of the clipboard into the console buffer + * + * @return true if clipboard contents pasted + */ + public boolean paste() throws IOException { + Clipboard clipboard; + try { // May throw ugly exception on system without X + clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); + } + catch (Exception e) { + return false; + } + + if (clipboard == null) { + return false; + } + + Transferable transferable = clipboard.getContents(null); + + if (transferable == null) { + return false; + } + + try { + Object content = transferable.getTransferData(DataFlavor.plainTextFlavor); + + // This fix was suggested in bug #1060649 at + // http://sourceforge.net/tracker/index.php?func=detail&aid=1060649&group_id=64033&atid=506056 + // to get around the deprecated DataFlavor.plainTextFlavor, but it + // raises a UnsupportedFlavorException on Mac OS X + + if (content == null) { + try { + content = new DataFlavor().getReaderForText(transferable); + } + catch (Exception e) { + // ignore + } + } + + if (content == null) { + return false; + } + + String value; + + if (content instanceof Reader) { + // TODO: we might want instead connect to the input stream + // so we can interpret individual lines + value = ""; + String line; + + BufferedReader read = new BufferedReader((Reader) content); + while ((line = read.readLine()) != null) { + if (value.length() > 0) { + value += "\n"; + } + + value += line; + } + } + else { + value = content.toString(); + } + + if (value == null) { + return true; + } + + putString(value); + + return true; + } + catch (UnsupportedFlavorException e) { + Log.error("Paste failed: ", e); + + return false; + } + } + + // + // Triggered Actions + // + + private final Map<Character, ActionListener> triggeredActions = new HashMap<Character, ActionListener>(); + + /** + * Adding a triggered Action allows to give another curse of action if a character passed the pre-processing. + * <p/> + * Say you want to close the application if the user enter q. + * addTriggerAction('q', new ActionListener(){ System.exit(0); }); would do the trick. + */ + public void addTriggeredAction(final char c, final ActionListener listener) { + triggeredActions.put(c, listener); + } + + // + // Formatted Output + // + + /** + * Output the specified {@link Collection} in proper columns. + */ + public void printColumns(final Collection<? extends CharSequence> items) throws IOException { + if (items == null || items.isEmpty()) { + return; + } + + int width = getTerminal().getWidth(); + int height = getTerminal().getHeight(); + + int maxWidth = 0; + for (CharSequence item : items) { + maxWidth = Math.max(maxWidth, item.length()); + } + Log.debug("Max width: ", maxWidth); + + int showLines; + if (isPaginationEnabled()) { + showLines = height - 1; // page limit + } + else { + showLines = Integer.MAX_VALUE; + } + + StringBuilder buff = new StringBuilder(); + for (CharSequence item : items) { + if ((buff.length() + maxWidth) > width) { + println(buff); + buff.setLength(0); + + if (--showLines == 0) { + // Overflow + print(resources.getString("display-more")); + flush(); + int c = readVirtualKey(); + if (c == '\r' || c == '\n') { + // one step forward + showLines = 1; + } + else if (c != 'q') { + // page forward + showLines = height - 1; + } + + back(resources.getString("display-more").length()); + if (c == 'q') { + // cancel + break; + } + } + } + + // NOTE: toString() is important here due to AnsiString being retarded + buff.append(item.toString()); + for (int i = 0; i < (maxWidth + 3 - item.length()); i++) { + buff.append(' '); + } + } + + if (buff.length() > 0) { + println(buff); + } + } + + // + // Non-supported Terminal Support + // + + private Thread maskThread; + + private void beforeReadLine(final String prompt, final Character mask) { + if (mask != null && maskThread == null) { + final String fullPrompt = "\r" + prompt + + " " + + " " + + " " + + "\r" + prompt; + + maskThread = new Thread() + { + public void run() { + while (!interrupted()) { + try { + Writer out = getOutput(); + out.write(fullPrompt); + out.flush(); + sleep(3); + } + catch (IOException e) { + return; + } + catch (InterruptedException e) { + return; + } + } + } + }; + + maskThread.setPriority(Thread.MAX_PRIORITY); + maskThread.setDaemon(true); + maskThread.start(); + } + } + + private void afterReadLine() { + if (maskThread != null && maskThread.isAlive()) { + maskThread.interrupt(); + } + + maskThread = null; + } + + /** + * Erases the current line with the existing prompt, then redraws the line + * with the provided prompt and buffer + * @param prompt + * the new prompt + * @param buffer + * the buffer to be drawn + * @param cursorDest + * where you want the cursor set when the line has been drawn. + * -1 for end of line. + * */ + public void resetPromptLine(String prompt, String buffer, int cursorDest) throws IOException { + // move cursor to end of line + moveToEnd(); + + // backspace all text, including prompt + buf.buffer.append(this.prompt); + buf.cursor += this.prompt.length(); + this.prompt = ""; + backspaceAll(); + + this.prompt = prompt; + redrawLine(); + setBuffer(buffer); + + // move cursor to destination (-1 will move to end of line) + if (cursorDest < 0) cursorDest = buffer.length(); + setCursorPosition(cursorDest); + + flush(); + } + + public void printSearchStatus(String searchTerm, String match) throws IOException { + String prompt = "(reverse-i-search)`" + searchTerm + "': "; + String buffer = match; + int cursorDest = match.indexOf(searchTerm); + resetPromptLine(prompt, buffer, cursorDest); + } + + public void restoreLine(String originalPrompt, int cursorDest) throws IOException { + // TODO move cursor to matched string + String prompt = lastLine(originalPrompt); + String buffer = buf.buffer.toString(); + resetPromptLine(prompt, buffer, cursorDest); + } + + // + // History search + // + /** + * Search backward in history from a given position. + * + * @param searchTerm substring to search for. + * @param startIndex the index from which on to search + * @return index where this substring has been found, or -1 else. + */ + public int searchBackwards(String searchTerm, int startIndex) { + return searchBackwards(searchTerm, startIndex, false); + } + + /** + * Search backwards in history from the current position. + * + * @param searchTerm substring to search for. + * @return index where the substring has been found, or -1 else. + */ + public int searchBackwards(String searchTerm) { + return searchBackwards(searchTerm, history.index()); + } + + + public int searchBackwards(String searchTerm, int startIndex, boolean startsWith) { + ListIterator<History.Entry> it = history.entries(startIndex); + while (it.hasPrevious()) { + History.Entry e = it.previous(); + if (startsWith) { + if (e.value().toString().startsWith(searchTerm)) { + return e.index(); + } + } else { + if (e.value().toString().contains(searchTerm)) { + return e.index(); + } + } + } + return -1; + } + + // + // Helpers + // + + /** + * Checks to see if the specified character is a delimiter. We consider a + * character a delimiter if it is anything but a letter or digit. + * + * @param c The character to test + * @return True if it is a delimiter + */ + private boolean isDelimiter(final char c) { + return !Character.isLetterOrDigit(c); + } + + private void printAnsiSequence(String sequence) throws IOException { + print(27); + print('['); + print(sequence); + flush(); // helps with step debugging + } + + // return column position, reported by the terminal + private int getCurrentPosition() { + // check for ByteArrayInputStream to disable for unit tests + if (terminal.isAnsiSupported() && !(in instanceof ByteArrayInputStream)) { + try { + printAnsiSequence("6n"); + flush(); + StringBuffer b = new StringBuffer(8); + // position is sent as <ESC>[{ROW};{COLUMN}R + int r; + while((r = in.read()) > -1 && r != 'R') { + if (r != 27 && r != '[') { + b.append((char) r); + } + } + String[] pos = b.toString().split(";"); + return Integer.parseInt(pos[1]); + } catch (Exception x) { + // no luck + } + } + + return -1; // TODO: throw exception instead? + } + +} diff --git a/src/jline/src/main/java/jline/CursorBuffer.java b/src/jline/src/main/java/jline/console/CursorBuffer.java index 3aec20af51..ef9932f43a 100644 --- a/src/jline/src/main/java/jline/CursorBuffer.java +++ b/src/jline/src/main/java/jline/console/CursorBuffer.java @@ -4,20 +4,31 @@ * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. */ -package jline; + +package jline.console; /** - * A CursorBuffer is a holder for a {@link StringBuffer} that also contains the - * current cursor position. + * A holder for a {@link StringBuilder} that also contains the current cursor position. * * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> + * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> + * @since 2.0 */ -public class CursorBuffer { +public class CursorBuffer +{ + private boolean overTyping = false; + public int cursor = 0; - StringBuffer buffer = new StringBuffer(); + public final StringBuilder buffer = new StringBuilder(); - private boolean overtyping = false; + public boolean isOverTyping() { + return overTyping; + } + + public void setOverTyping(final boolean b) { + overTyping = b; + } public int length() { return buffer.length(); @@ -31,74 +42,52 @@ public class CursorBuffer { 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(). + * of {@link #isOverTyping}. * - * @param c - * the character to insert + * @param c the character to insert */ public void write(final char c) { buffer.insert(cursor++, c); - if (isOvertyping() && cursor < buffer.length()) { + 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. + * Insert the specified chars into the buffer, setting the cursor to the end of the insertion point. */ - public void write(final String str) { + public void write(final CharSequence str) { + assert str != null; + if (buffer.length() == 0) { buffer.append(str); - } else { + } + else { buffer.insert(cursor, str); } cursor += str.length(); - if (isOvertyping() && cursor < buffer.length()) { + if (isOverTyping() && cursor < buffer.length()) { buffer.delete(cursor, (cursor + str.length())); } } - public String toString() { - return buffer.toString(); - } + public boolean clear() { + if (buffer.length() == 0) { + return false; + } - public boolean isOvertyping() { - return overtyping; + buffer.delete(0, buffer.length()); + cursor = 0; + return true; } - public void setOvertyping(boolean b) { - overtyping = b; + @Override + public String toString() { + return buffer.toString(); } - - 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/jline/src/main/java/jline/console/Key.java b/src/jline/src/main/java/jline/console/Key.java new file mode 100644 index 0000000000..8997985dcc --- /dev/null +++ b/src/jline/src/main/java/jline/console/Key.java @@ -0,0 +1,74 @@ +/* + * 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.console; + +import java.util.HashMap; +import java.util.Map; + +/** + * Map from key name to key codes. + * + * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> + * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> + * @see java.awt.event.KeyEvent + * @since 2.0 + */ +public enum Key +{ + CTRL_A(1), + + CTRL_B(2), + + CTRL_C(3), + + CTRL_D(4), + + CTRL_E(5), + + CTRL_F(6), + + CTRL_G(7), + + CTRL_K(11), + + CTRL_L(12), + + CTRL_N(14), + + CTRL_P(16), + + CTRL_OB(27), + + CTRL_QM(127), + + BACKSPACE('\b'), + + DELETE(127),; + + public final short code; + + Key(final int code) { + this.code = (short) code; + } + + private static final Map<Short, Key> codes; + + static { + Map<Short, Key> map = new HashMap<Short, Key>(); + + for (Key op : Key.values()) { + map.put(op.code, op); + } + + codes = map; + } + + public static Key valueOf(final int code) { + return codes.get((short) code); + } +}
\ No newline at end of file diff --git a/src/jline/src/main/java/jline/console/Operation.java b/src/jline/src/main/java/jline/console/Operation.java new file mode 100644 index 0000000000..e2d9ea564b --- /dev/null +++ b/src/jline/src/main/java/jline/console/Operation.java @@ -0,0 +1,285 @@ +/* + * 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.console; + +import java.util.HashMap; +import java.util.Map; + +/** + * Map for console operation to virtual key bindings. + * + * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> + * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> + * @see java.awt.event.KeyEvent + * @since 2.0 + */ +public enum Operation +{ + /** + * Unknown operation. + */ + UNKNOWN(-99), + + /** + * Operation that moves to the beginning of the buffer. + */ + MOVE_TO_BEG(-1), + + /** + * Operation that moves to the end of the buffer. + */ + MOVE_TO_END(-3), + + /** + * Operation that moved to the previous character in the buffer. + */ + PREV_CHAR(-4), + + /** + * Operation that issues a newline. + */ + NEWLINE(-6), + + /** + * Operation that deletes the buffer from the current character to the end. + */ + KILL_LINE(-7), + + /** + * Operation that clears the screen. + */ + CLEAR_SCREEN(-8), + + /** + * Operation that sets the buffer to the next history item. + */ + NEXT_HISTORY(-9), + + /** + * Operation that sets the buffer to the previous history item. + */ + PREV_HISTORY(-11), + + /** + * Operation that redisplays the current buffer. + */ + REDISPLAY(-13), + + /** + * Operation that deletes the buffer from the cursor to the beginning. + */ + KILL_LINE_PREV(-15), + + /** + * Operation that deletes the previous word in the buffer. + */ + DELETE_PREV_WORD(-16), + + /** + * Operation that moves to the next character in the buffer. + */ + NEXT_CHAR(-19), + + /** + * Operation that moves to the previous character in the buffer. + */ + REPEAT_PREV_CHAR(-20), + + /** + * Operation that searches backwards in the command history. + */ + SEARCH_PREV(-21), + + /** + * Operation that repeats the character. + */ + REPEAT_NEXT_CHAR(-24), + + /** + * Operation that searches forward in the command history. + */ + SEARCH_NEXT(-25), + + /** + * Operation that moved to the previous whitespace. + */ + PREV_SPACE_WORD(-27), + + /** + * Operation that moved to the end of the current word. + */ + TO_END_WORD(-29), + + /** + * Operation that + */ + REPEAT_SEARCH_PREV(-34), + + /** + * Operation that + */ + PASTE_PREV(-36), + + /** + * Operation that + */ + REPLACE_MODE(-37), + + /** + * Operation that + */ + SUBSTITUTE_LINE(-38), + + /** + * Operation that + */ + TO_PREV_CHAR(-39), + + /** + * Operation that + */ + NEXT_SPACE_WORD(-40), + + /** + * Operation that + */ + DELETE_PREV_CHAR(-41), + + /** + * Operation that + */ + ADD(-42), + + /** + * Operation that + */ + PREV_WORD(-43), + + /** + * Operation that + */ + CHANGE_META(-44), + + /** + * Operation that + */ + DELETE_META(-45), + + /** + * Operation that + */ + END_WORD(-46), + + /** + * Operation that toggles insert/overtype + */ + INSERT(-48), + + /** + * Operation that + */ + REPEAT_SEARCH_NEXT(-49), + + /** + * Operation that + */ + PASTE_NEXT(-50), + + /** + * Operation that + */ + REPLACE_CHAR(-51), + + /** + * Operation that + */ + SUBSTITUTE_CHAR(-52), + + /** + * Operation that + */ + TO_NEXT_CHAR(-53), + + /** + * Operation that undoes the previous operation. + */ + UNDO(-54), + + /** + * Operation that moved to the next word. + */ + NEXT_WORD(-55), + + /** + * Operation that deletes the previous character. + */ + DELETE_NEXT_CHAR(-56), + + /** + * Operation that toggles between uppercase and lowercase. + */ + CHANGE_CASE(-57), + + /** + * Operation that performs completion operation on the current word. + */ + COMPLETE(-58), + + /** + * Operation that exits the command prompt. + */ + EXIT(-59), + + /** + * Operation that pastes the contents of the clipboard into the line + */ + PASTE(-60), + + /** + * Operation that moves the current History to the beginning. + */ + START_OF_HISTORY(-61), + + /** + * Operation that moves the current History to the end. + */ + END_OF_HISTORY(-62), + + /** + * Operation that clears whatever text is on the current line. + */ + CLEAR_LINE(-63), + + /** + * Cancel search + */ + ABORT(-64), + ; + + public final short code; + + Operation(final int code) { + this.code = (short) code; + } + + private static final Map<Short, Operation> codes; + + static { + Map<Short, Operation> map = new HashMap<Short, Operation>(); + + for (Operation op : Operation.values()) { + map.put(op.code, op); + } + + codes = map; + } + + public static Operation valueOf(final int code) { + return codes.get((short) code); + } +}
\ No newline at end of file diff --git a/src/jline/src/main/java/jline/console/completer/AggregateCompleter.java b/src/jline/src/main/java/jline/console/completer/AggregateCompleter.java new file mode 100644 index 0000000000..621ac5f6d8 --- /dev/null +++ b/src/jline/src/main/java/jline/console/completer/AggregateCompleter.java @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package jline.console.completer; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; + +/** + * Completer which contains multiple completers and aggregates them together. + * + * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> + * @since 2.3 + */ +public class AggregateCompleter + implements Completer +{ + private final List<Completer> completers = new ArrayList<Completer>(); + + public AggregateCompleter() { + // empty + } + + public AggregateCompleter(final Collection<Completer> completers) { + assert completers != null; + this.completers.addAll(completers); + } + + public AggregateCompleter(final Completer... completers) { + this(Arrays.asList(completers)); + } + + public Collection<Completer> getCompleters() { + return completers; + } + + public int complete(final String buffer, final int cursor, final List<CharSequence> candidates) { + // buffer could be null + assert candidates != null; + + List<Completion> completions = new ArrayList<Completion>(completers.size()); + + // Run each completer, saving its completion results + int max = -1; + for (Completer completer : completers) { + Completion completion = new Completion(candidates); + completion.complete(completer, buffer, cursor); + + // Compute the max cursor position + max = Math.max(max, completion.cursor); + + completions.add(completion); + } + + // Append candidates from completions which have the same cursor position as max + for (Completion completion : completions) { + if (completion.cursor == max) { + candidates.addAll(completion.candidates); + } + } + + return max; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "completers=" + completers + + '}'; + } + + private class Completion + { + public final List<CharSequence> candidates; + + public int cursor; + + public Completion(final List<CharSequence> candidates) { + assert candidates != null; + this.candidates = new LinkedList<CharSequence>(candidates); + } + + public void complete(final Completer completer, final String buffer, final int cursor) { + assert completer != null; + + this.cursor = completer.complete(buffer, cursor, candidates); + } + } +} diff --git a/src/jline/src/main/java/jline/console/completer/ArgumentCompleter.java b/src/jline/src/main/java/jline/console/completer/ArgumentCompleter.java new file mode 100644 index 0000000000..542d4b4424 --- /dev/null +++ b/src/jline/src/main/java/jline/console/completer/ArgumentCompleter.java @@ -0,0 +1,398 @@ +/* + * 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.console.completer; + +import jline.internal.Log; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; + +/** + * A {@link Completer} implementation that invokes a child completer using the appropriate <i>separator</i> argument. + * This can be used instead of the individual completers having to know about argument parsing semantics. + * + * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> + * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> + * @since 2.3 + */ +public class ArgumentCompleter + implements Completer +{ + private final ArgumentDelimiter delimiter; + + private final List<Completer> completers = new ArrayList<Completer>(); + + private boolean strict = true; + + /** + * Create a new completer with the specified argument delimiter. + * + * @param delimiter The delimiter for parsing arguments + * @param completers The embedded completers + */ + public ArgumentCompleter(final ArgumentDelimiter delimiter, final Collection<Completer> completers) { + assert delimiter != null; + this.delimiter = delimiter; + assert completers != null; + this.completers.addAll(completers); + } + + /** + * Create a new completer with the specified argument delimiter. + * + * @param delimiter The delimiter for parsing arguments + * @param completers The embedded completers + */ + public ArgumentCompleter(final ArgumentDelimiter delimiter, final Completer... completers) { + this(delimiter, Arrays.asList(completers)); + } + + /** + * Create a new completer with the default {@link WhitespaceArgumentDelimiter}. + * + * @param completers The embedded completers + */ + public ArgumentCompleter(final Completer... completers) { + this(new WhitespaceArgumentDelimiter(), completers); + } + + /** + * Create a new completer with the default {@link WhitespaceArgumentDelimiter}. + * + * @param completers The embedded completers + */ + public ArgumentCompleter(final List<Completer> completers) { + this(new WhitespaceArgumentDelimiter(), completers); + } + + /** + * If true, a completion at argument index N will only succeed + * if all the completions from 0-(N-1) also succeed. + */ + public void setStrict(final boolean strict) { + this.strict = strict; + } + + /** + * Returns whether a completion at argument index N will success + * if all the completions from arguments 0-(N-1) also succeed. + * + * @return True if strict. + * @since 2.3 + */ + public boolean isStrict() { + return this.strict; + } + + /** + * @since 2.3 + */ + public ArgumentDelimiter getDelimiter() { + return delimiter; + } + + /** + * @since 2.3 + */ + public List<Completer> getCompleters() { + return completers; + } + + public int complete(final String buffer, final int cursor, final List<CharSequence> candidates) { + // buffer can be null + assert candidates != null; + + ArgumentDelimiter delim = getDelimiter(); + ArgumentList list = delim.delimit(buffer, cursor); + int argpos = list.getArgumentPosition(); + int argIndex = list.getCursorArgumentIndex(); + + if (argIndex < 0) { + return -1; + } + + List<Completer> completers = getCompleters(); + Completer completer; + + // if we are beyond the end of the completers, just use the last one + if (argIndex >= completers.size()) { + completer = completers.get(completers.size() - 1); + } + else { + completer = completers.get(argIndex); + } + + // ensure that all the previous completers are successful before allowing this completer to pass (only if strict). + for (int i = 0; isStrict() && (i < argIndex); i++) { + Completer sub = completers.get(i >= completers.size() ? (completers.size() - 1) : i); + String[] args = list.getArguments(); + String arg = (args == null || i >= args.length) ? "" : args[i]; + + List<CharSequence> subCandidates = new LinkedList<CharSequence>(); + + if (sub.complete(arg, arg.length(), subCandidates) == -1) { + return -1; + } + + if (subCandidates.size() == 0) { + return -1; + } + } + + int ret = completer.complete(list.getCursorArgument(), argpos, candidates); + + if (ret == -1) { + return -1; + } + + int pos = ret + list.getBufferPosition() - argpos; + + // Special case: when completing in the middle of a line, and the area under the cursor is a delimiter, + // then trim any delimiters from the candidates, since we do not need to have an extra delimiter. + // + // E.g., if we have a completion for "foo", and we enter "f bar" into the buffer, and move to after the "f" + // and hit TAB, we want "foo bar" instead of "foo bar". + + if ((cursor != buffer.length()) && delim.isDelimiter(buffer, cursor)) { + for (int i = 0; i < candidates.size(); i++) { + CharSequence val = candidates.get(i); + + while (val.length() > 0 && delim.isDelimiter(val, val.length() - 1)) { + val = val.subSequence(0, val.length() - 1); + } + + candidates.set(i, val); + } + } + + Log.trace("Completing ", buffer, " (pos=", cursor, ") with: ", candidates, ": offset=", pos); + + return pos; + } + + /** + * The {@link ArgumentCompleter.ArgumentDelimiter} allows custom breaking up of a {@link String} into individual + * arguments in order to dispatch the arguments to the nested {@link Completer}. + * + * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> + */ + public static interface ArgumentDelimiter + { + /** + * Break the specified buffer into individual tokens that can be completed on their own. + * + * @param buffer The buffer to split + * @param pos The current position of the cursor in the buffer + * @return The tokens + */ + ArgumentList delimit(CharSequence buffer, int pos); + + /** + * Returns true if the specified character is a whitespace parameter. + * + * @param buffer The complete command buffer + * @param pos The index of the character in the buffer + * @return True if the character should be a delimiter + */ + boolean isDelimiter(CharSequence buffer, int pos); + } + + /** + * Abstract implementation of a delimiter that uses the {@link #isDelimiter} method to determine if a particular + * character should be used as a delimiter. + * + * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> + */ + public abstract static class AbstractArgumentDelimiter + implements ArgumentDelimiter + { + // TODO: handle argument quoting and escape characters + + private char[] quoteChars = {'\'', '"'}; + + private char[] escapeChars = {'\\'}; + + public void setQuoteChars(final char[] chars) { + this.quoteChars = chars; + } + + public char[] getQuoteChars() { + return this.quoteChars; + } + + public void setEscapeChars(final char[] chars) { + this.escapeChars = chars; + } + + public char[] getEscapeChars() { + return this.escapeChars; + } + + public ArgumentList delimit(final CharSequence buffer, final int cursor) { + List<String> args = new LinkedList<String>(); + StringBuilder arg = new StringBuilder(); + int argpos = -1; + int bindex = -1; + + for (int i = 0; (buffer != null) && (i <= buffer.length()); i++) { + // once we reach the cursor, set the + // position of the selected index + if (i == cursor) { + bindex = args.size(); + // the position in the current argument is just the + // length of the current argument + argpos = arg.length(); + } + + if ((i == buffer.length()) || isDelimiter(buffer, i)) { + if (arg.length() > 0) { + args.add(arg.toString()); + arg.setLength(0); // reset the arg + } + } + else { + arg.append(buffer.charAt(i)); + } + } + + return new ArgumentList(args.toArray(new String[args.size()]), bindex, argpos, cursor); + } + + /** + * Returns true if the specified character is a whitespace parameter. Check to ensure that the character is not + * escaped by any of {@link #getQuoteChars}, and is not escaped by ant of the {@link #getEscapeChars}, and + * returns true from {@link #isDelimiterChar}. + * + * @param buffer The complete command buffer + * @param pos The index of the character in the buffer + * @return True if the character should be a delimiter + */ + public boolean isDelimiter(final CharSequence buffer, final int pos) { + return !isQuoted(buffer, pos) && !isEscaped(buffer, pos) && isDelimiterChar(buffer, pos); + } + + public boolean isQuoted(final CharSequence buffer, final int pos) { + return false; + } + + public boolean isEscaped(final CharSequence buffer, final int pos) { + if (pos <= 0) { + return false; + } + + for (int i = 0; (escapeChars != null) && (i < escapeChars.length); + i++) { + if (buffer.charAt(pos) == escapeChars[i]) { + return !isEscaped(buffer, pos - 1); // escape escape + } + } + + return false; + } + + /** + * Returns true if the character at the specified position if a delimiter. This method will only be called if + * the character is not enclosed in any of the {@link #getQuoteChars}, and is not escaped by ant of the + * {@link #getEscapeChars}. To perform escaping manually, override {@link #isDelimiter} instead. + */ + public abstract boolean isDelimiterChar(CharSequence buffer, int pos); + } + + /** + * {@link ArgumentCompleter.ArgumentDelimiter} implementation that counts all whitespace (as reported by + * {@link Character#isWhitespace}) as being a delimiter. + * + * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> + */ + public static class WhitespaceArgumentDelimiter + extends AbstractArgumentDelimiter + { + /** + * The character is a delimiter if it is whitespace, and the + * preceding character is not an escape character. + */ + @Override + public boolean isDelimiterChar(final CharSequence buffer, final int pos) { + return Character.isWhitespace(buffer.charAt(pos)); + } + } + + /** + * The result of a delimited buffer. + * + * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> + */ + public static class ArgumentList + { + private String[] arguments; + + private int cursorArgumentIndex; + + private int argumentPosition; + + private int bufferPosition; + + /** + * @param arguments The array of tokens + * @param cursorArgumentIndex The token index of the cursor + * @param argumentPosition The position of the cursor in the current token + * @param bufferPosition The position of the cursor in the whole buffer + */ + public ArgumentList(final String[] arguments, final int cursorArgumentIndex, final int argumentPosition, final int bufferPosition) { + assert arguments != null; + + this.arguments = arguments; + this.cursorArgumentIndex = cursorArgumentIndex; + this.argumentPosition = argumentPosition; + this.bufferPosition = bufferPosition; + } + + public void setCursorArgumentIndex(final int i) { + this.cursorArgumentIndex = i; + } + + public int getCursorArgumentIndex() { + return this.cursorArgumentIndex; + } + + public String getCursorArgument() { + if ((cursorArgumentIndex < 0) || (cursorArgumentIndex >= arguments.length)) { + return null; + } + + return arguments[cursorArgumentIndex]; + } + + public void setArgumentPosition(final int pos) { + this.argumentPosition = pos; + } + + public int getArgumentPosition() { + return this.argumentPosition; + } + + public void setArguments(final String[] arguments) { + this.arguments = arguments; + } + + public String[] getArguments() { + return this.arguments; + } + + public void setBufferPosition(final int pos) { + this.bufferPosition = pos; + } + + public int getBufferPosition() { + return this.bufferPosition; + } + } +} diff --git a/src/jline/src/main/java/jline/console/completer/CandidateListCompletionHandler.java b/src/jline/src/main/java/jline/console/completer/CandidateListCompletionHandler.java new file mode 100644 index 0000000000..3f6ede6fc4 --- /dev/null +++ b/src/jline/src/main/java/jline/console/completer/CandidateListCompletionHandler.java @@ -0,0 +1,192 @@ +/* + * 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.console.completer; + +import jline.console.ConsoleReader; +import jline.console.CursorBuffer; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.ResourceBundle; +import java.util.Set; + +/** + * A {@link CompletionHandler} that deals with multiple distinct completions + * by outputting the complete list of possibilities to the console. This + * mimics the behavior of the + * <a href="http://www.gnu.org/directory/readline.html">readline</a> library. + * + * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> + * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> + * @since 2.3 + */ +public class CandidateListCompletionHandler + implements CompletionHandler +{ + // TODO: handle quotes and escaped quotes && enable automatic escaping of whitespace + + public boolean complete(final ConsoleReader reader, final List<CharSequence> candidates, final int pos) throws + IOException + { + CursorBuffer buf = reader.getCursorBuffer(); + + // if there is only one completion, then fill in the buffer + if (candidates.size() == 1) { + CharSequence value = candidates.get(0); + + // fail if the only candidate is the same as the current buffer + if (value.equals(buf.toString())) { + return false; + } + + setBuffer(reader, value, pos); + + return true; + } + else if (candidates.size() > 1) { + String value = getUnambiguousCompletions(candidates); + setBuffer(reader, value, pos); + } + + printCandidates(reader, candidates); + + // redraw the current console buffer + reader.drawLine(); + + return true; + } + + public static void setBuffer(final ConsoleReader reader, final CharSequence value, final int offset) throws + IOException + { + while ((reader.getCursorBuffer().cursor > offset) && reader.backspace()) { + // empty + } + + reader.putString(value); + reader.setCursorPosition(offset + value.length()); + } + + /** + * Print out the candidates. If the size of the candidates is greater than the + * {@link ConsoleReader#getAutoprintThreshold}, they prompt with a warning. + * + * @param candidates the list of candidates to print + */ + public static void printCandidates(final ConsoleReader reader, Collection<CharSequence> candidates) throws + IOException + { + Set<CharSequence> distinct = new HashSet<CharSequence>(candidates); + + if (distinct.size() > reader.getAutoprintThreshold()) { + //noinspection StringConcatenation + reader.print(Messages.DISPLAY_CANDIDATES.format(candidates.size())); + reader.flush(); + + int c; + + String noOpt = Messages.DISPLAY_CANDIDATES_NO.format(); + String yesOpt = Messages.DISPLAY_CANDIDATES_YES.format(); + char[] allowed = {yesOpt.charAt(0), noOpt.charAt(0)}; + + while ((c = reader.readCharacter(allowed)) != -1) { + String tmp = new String(new char[]{(char) c}); + + if (noOpt.startsWith(tmp)) { + reader.println(); + return; + } + else if (yesOpt.startsWith(tmp)) { + break; + } + else { + reader.beep(); + } + } + } + + // copy the values and make them distinct, without otherwise affecting the ordering. Only do it if the sizes differ. + if (distinct.size() != candidates.size()) { + Collection<CharSequence> copy = new ArrayList<CharSequence>(); + + for (CharSequence next : candidates) { + if (!copy.contains(next)) { + copy.add(next); + } + } + + candidates = copy; + } + + reader.println(); + reader.printColumns(candidates); + } + + /** + * Returns a root that matches all the {@link String} elements of the specified {@link List}, + * or null if there are no commonalities. For example, if the list contains + * <i>foobar</i>, <i>foobaz</i>, <i>foobuz</i>, the method will return <i>foob</i>. + */ + private String getUnambiguousCompletions(final List<CharSequence> candidates) { + if (candidates == null || candidates.isEmpty()) { + return null; + } + + // convert to an array for speed + String[] strings = candidates.toArray(new String[candidates.size()]); + + String first = strings[0]; + StringBuilder candidate = new StringBuilder(); + + for (int i = 0; i < first.length(); i++) { + if (startsWith(first.substring(0, i + 1), strings)) { + candidate.append(first.charAt(i)); + } + else { + break; + } + } + + return candidate.toString(); + } + + /** + * @return true is all the elements of <i>candidates</i> start with <i>starts</i> + */ + private boolean startsWith(final String starts, final String[] candidates) { + for (String candidate : candidates) { + if (!candidate.startsWith(starts)) { + return false; + } + } + + return true; + } + + private static enum Messages + { + DISPLAY_CANDIDATES, + DISPLAY_CANDIDATES_YES, + DISPLAY_CANDIDATES_NO,; + + private static final + ResourceBundle + bundle = + ResourceBundle.getBundle(CandidateListCompletionHandler.class.getName(), + Locale.getDefault(), + CandidateListCompletionHandler.class.getClassLoader()); + + public String format(final Object... args) { + return String.format(bundle.getString(name()), args); + } + } +} diff --git a/src/jline/src/main/java/jline/console/completer/Completer.java b/src/jline/src/main/java/jline/console/completer/Completer.java new file mode 100644 index 0000000000..ff5d22b108 --- /dev/null +++ b/src/jline/src/main/java/jline/console/completer/Completer.java @@ -0,0 +1,37 @@ +/* + * 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.console.completer; + +import java.util.List; + +/** + * A completer is the mechanism by which tab-completion candidates will be resolved. + * + * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> + * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> + * @since 2.3 + */ +public interface Completer +{ + // + // FIXME: Check if we can use CharSequece for buffer? + // + + /** + * Populates <i>candidates</i> with a list of possible completions for the <i>buffer</i>. + * + * The <i>candidates</i> list will not be sorted before being displayed to the user: thus, the + * complete method should sort the {@link List} before returning. + * + * @param buffer The buffer + * @param cursor The current position of the cursor in the <i>buffer</i> + * @param candidates The {@link List} of candidates to populate + * @return The index of the <i>buffer</i> for which the completion will be relative + */ + int complete(String buffer, int cursor, List<CharSequence> candidates); +} diff --git a/src/jline/src/main/java/jline/console/completer/CompletionHandler.java b/src/jline/src/main/java/jline/console/completer/CompletionHandler.java new file mode 100644 index 0000000000..8f32f2c7f6 --- /dev/null +++ b/src/jline/src/main/java/jline/console/completer/CompletionHandler.java @@ -0,0 +1,25 @@ +/* + * 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.console.completer; + +import jline.console.ConsoleReader; + +import java.io.IOException; +import java.util.List; + +/** + * Handler for dealing with candidates for tab-completion. + * + * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> + * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> + * @since 2.3 + */ +public interface CompletionHandler +{ + boolean complete(ConsoleReader reader, List<CharSequence> candidates, int position) throws IOException; +} diff --git a/src/jline/src/main/java/jline/console/completer/EnumCompleter.java b/src/jline/src/main/java/jline/console/completer/EnumCompleter.java new file mode 100644 index 0000000000..738689e8a6 --- /dev/null +++ b/src/jline/src/main/java/jline/console/completer/EnumCompleter.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2009 the original author(s). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package jline.console.completer; + +/** + * {@link Completer} for {@link Enum} names. + * + * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> + * @since 2.3 + */ +public class EnumCompleter + extends StringsCompleter +{ + public EnumCompleter(Class<? extends Enum> source) { + assert source != null; + + for (Enum<?> n : source.getEnumConstants()) { + this.getStrings().add(n.name().toLowerCase()); + } + } +}
\ No newline at end of file diff --git a/src/jline/src/main/java/jline/console/completer/FileNameCompleter.java b/src/jline/src/main/java/jline/console/completer/FileNameCompleter.java new file mode 100644 index 0000000000..69667aa9bf --- /dev/null +++ b/src/jline/src/main/java/jline/console/completer/FileNameCompleter.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.console.completer; + +import jline.internal.Configuration; + +import java.io.File; +import java.util.List; + +/** + * A file name completer takes the buffer and issues a list of + * potential completions. + * <p/> + * This completer tries to behave as similar as possible to + * <i>bash</i>'s file name completion (using GNU readline) + * with the following exceptions: + * <p/> + * <ul> + * <li>Candidates that are directories will end with "/"</li> + * <li>Wildcard regular expressions are not evaluated or replaced</li> + * <li>The "~" character can be used to represent the user's home, + * but it cannot complete to other users' homes, since java does + * not provide any way of determining that easily</li> + * </ul> + * + * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> + * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> + * @since 2.3 + */ +public class FileNameCompleter + implements Completer +{ + // TODO: Handle files with spaces in them + + private static final boolean OS_IS_WINDOWS; + + static { + String os = Configuration.getOsName(); + OS_IS_WINDOWS = os.contains("windows"); + } + + public int complete(String buffer, final int cursor, final List<CharSequence> candidates) { + // buffer can be null + assert candidates != null; + + if (buffer == null) { + buffer = ""; + } + + if (OS_IS_WINDOWS) { + buffer = buffer.replace('/', '\\'); + } + + String translated = buffer; + + File homeDir = getUserHome(); + + // Special character: ~ maps to the user's home directory + if (translated.startsWith("~" + separator())) { + translated = homeDir.getPath() + translated.substring(1); + } + else if (translated.startsWith("~")) { + translated = homeDir.getParentFile().getAbsolutePath(); + } + else if (!(translated.startsWith(separator()))) { + String cwd = getUserDir().getAbsolutePath(); + translated = cwd + separator() + translated; + } + + File file = new File(translated); + final File dir; + + if (translated.endsWith(separator())) { + dir = file; + } + else { + dir = file.getParentFile(); + } + + File[] entries = dir == null ? new File[0] : dir.listFiles(); + + return matchFiles(buffer, translated, entries, candidates); + } + + protected String separator() { + return File.separator; + } + + protected File getUserHome() { + return Configuration.getUserHome(); + } + + protected File getUserDir() { + return new File("."); + } + + protected int matchFiles(final String buffer, final String translated, final File[] files, final List<CharSequence> candidates) { + if (files == null) { + return -1; + } + + int matches = 0; + + // first pass: just count the matches + for (File file : files) { + if (file.getAbsolutePath().startsWith(translated)) { + matches++; + } + } + for (File file : files) { + if (file.getAbsolutePath().startsWith(translated)) { + CharSequence name = file.getName() + (matches == 1 && file.isDirectory() ? separator() : " "); + candidates.add(render(file, name).toString()); + } + } + + final int index = buffer.lastIndexOf(separator()); + + return index + separator().length(); + } + + protected CharSequence render(final File file, final CharSequence name) { + assert file != null; + assert name != null; + + return name; + } +} diff --git a/src/jline/src/main/java/jline/console/completer/NullCompleter.java b/src/jline/src/main/java/jline/console/completer/NullCompleter.java new file mode 100644 index 0000000000..872aee7c84 --- /dev/null +++ b/src/jline/src/main/java/jline/console/completer/NullCompleter.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package jline.console.completer; + +import java.util.List; + +/** + * Null completer. + * + * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> + * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> + * @since 2.3 + */ +public final class NullCompleter + implements Completer +{ + public static final NullCompleter INSTANCE = new NullCompleter(); + + public int complete(final String buffer, final int cursor, final List<CharSequence> candidates) { + return -1; + } +}
\ No newline at end of file diff --git a/src/jline/src/main/java/jline/console/completer/StringsCompleter.java b/src/jline/src/main/java/jline/console/completer/StringsCompleter.java new file mode 100644 index 0000000000..afd2abbcd1 --- /dev/null +++ b/src/jline/src/main/java/jline/console/completer/StringsCompleter.java @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package jline.console.completer; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.SortedSet; +import java.util.TreeSet; + +/** + * Completer for a set of strings. + * + * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> + * @since 2.3 + */ +public class StringsCompleter + implements Completer +{ + private final SortedSet<String> strings = new TreeSet<String>(); + + public StringsCompleter() { + // empty + } + + public StringsCompleter(final Collection<String> strings) { + assert strings != null; + getStrings().addAll(strings); + } + + public StringsCompleter(final String... strings) { + this(Arrays.asList(strings)); + } + + public Collection<String> getStrings() { + return strings; + } + + public int complete(final String buffer, final int cursor, final List<CharSequence> candidates) { + // buffer could be null + assert candidates != null; + + if (buffer == null) { + candidates.addAll(strings); + } + else { + for (String match : strings.tailSet(buffer)) { + if (!match.startsWith(buffer)) { + break; + } + + candidates.add(match); + } + } + + if (candidates.size() == 1) { + candidates.set(0, candidates.get(0) + " "); + } + + return candidates.isEmpty() ? -1 : 0; + } +}
\ No newline at end of file diff --git a/src/jline/src/main/java/jline/console/completer/package-info.java b/src/jline/src/main/java/jline/console/completer/package-info.java new file mode 100644 index 0000000000..4f8fc08a76 --- /dev/null +++ b/src/jline/src/main/java/jline/console/completer/package-info.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2010 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Console completer support. + * + * @since 2.3 + */ +package jline.console.completer;
\ No newline at end of file diff --git a/src/jline/src/main/java/jline/console/history/FileHistory.java b/src/jline/src/main/java/jline/console/history/FileHistory.java new file mode 100644 index 0000000000..a32e05ab75 --- /dev/null +++ b/src/jline/src/main/java/jline/console/history/FileHistory.java @@ -0,0 +1,106 @@ +/* + * 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.console.history; + +import jline.internal.Log; + +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.Flushable; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.io.Reader; +import java.util.ListIterator; + +/** + * {@link History} using a file for persistent backing. + * <p/> + * Implementers should install shutdown hook to call {@link FileHistory#flush} + * to save history to disk. + * + * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> + * @since 2.0 + */ +public class FileHistory + extends MemoryHistory + implements PersistentHistory, Flushable +{ + private final File file; + + public FileHistory(final File file) throws IOException { + assert file != null; + this.file = file; + load(file); + } + + public File getFile() { + return file; + } + + public void load(final File file) throws IOException { + assert file != null; + if (file.exists()) { + Log.trace("Loading history from: ", file); + load(new FileReader(file)); + } + } + + public void load(final InputStream input) throws IOException { + assert input != null; + load(new InputStreamReader(input)); + } + + public void load(final Reader reader) throws IOException { + assert reader != null; + BufferedReader input = new BufferedReader(reader); + + String item; + while ((item = input.readLine()) != null) { + add(item); + } + } + + public void flush() throws IOException { + Log.trace("Flushing history"); + + if (!file.exists()) { + File dir = file.getParentFile(); + if (!dir.exists() && !dir.mkdirs()) { + Log.warn("Failed to create directory: ", dir); + } + if (!file.createNewFile()) { + Log.warn("Failed to create file: ", file); + } + } + + PrintStream out = new PrintStream(new BufferedOutputStream(new FileOutputStream(file))); + try { + for (Entry entry : this) { + out.println(entry.value()); + } + } + finally { + out.close(); + } + } + + public void purge() throws IOException { + Log.trace("Purging history"); + + clear(); + + if (!file.delete()) { + Log.warn("Failed to delete history file: ", file); + } + } +}
\ No newline at end of file diff --git a/src/jline/src/main/java/jline/console/history/History.java b/src/jline/src/main/java/jline/console/history/History.java new file mode 100644 index 0000000000..9e098c9985 --- /dev/null +++ b/src/jline/src/main/java/jline/console/history/History.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.console.history; + +import java.util.Iterator; +import java.util.ListIterator; + +/** + * Console history. + * + * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> + * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> + * @since 2.3 + */ +public interface History + extends Iterable<History.Entry> +{ + int size(); + + boolean isEmpty(); + + int index(); + + void clear(); + + CharSequence get(int index); + + void add(CharSequence line); + + void replace(CharSequence item); + + // + // Entries + // + + interface Entry + { + int index(); + + CharSequence value(); + } + + ListIterator<Entry> entries(int index); + + ListIterator<Entry> entries(); + + Iterator<Entry> iterator(); + + // + // Navigation + // + + CharSequence current(); + + boolean previous(); + + boolean next(); + + boolean moveToFirst(); + + boolean moveToLast(); + + boolean moveTo(int index); + + void moveToEnd(); +} diff --git a/src/jline/src/main/java/jline/console/history/MemoryHistory.java b/src/jline/src/main/java/jline/console/history/MemoryHistory.java new file mode 100644 index 0000000000..5b67d8801b --- /dev/null +++ b/src/jline/src/main/java/jline/console/history/MemoryHistory.java @@ -0,0 +1,318 @@ +/* + * 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.console.history; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.ListIterator; +import java.util.NoSuchElementException; + +/** + * Non-persistent {@link History}. + * + * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> + * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> + * @since 2.3 + */ +public class MemoryHistory + implements History +{ + public static final int DEFAULT_MAX_SIZE = 500; + + private final LinkedList<CharSequence> items = new LinkedList<CharSequence>(); + + private int maxSize = DEFAULT_MAX_SIZE; + + private boolean ignoreDuplicates = true; + + private boolean autoTrim = false; + + // NOTE: These are all ideas from looking at the Bash man page: + + // TODO: Add ignore space? (lines starting with a space are ignored) + + // TODO: Add ignore patterns? + + // TODO: Add history timestamp? + + // TODO: Add erase dups? + + private int offset = 0; + + private int index = 0; + + public void setMaxSize(final int maxSize) { + this.maxSize = maxSize; + maybeResize(); + } + + public int getMaxSize() { + return maxSize; + } + + public boolean isIgnoreDuplicates() { + return ignoreDuplicates; + } + + public void setIgnoreDuplicates(final boolean flag) { + this.ignoreDuplicates = flag; + } + + public boolean isAutoTrim() { + return autoTrim; + } + + public void setAutoTrim(final boolean flag) { + this.autoTrim = flag; + } + + public int size() { + return items.size(); + } + + public boolean isEmpty() { + return items.isEmpty(); + } + + public int index() { + return offset + index; + } + + public void clear() { + items.clear(); + offset = 0; + index = 0; + } + + public CharSequence get(final int index) { + return items.get(index - offset); + } + + public void add(CharSequence item) { + assert item != null; + + if (isAutoTrim()) { + item = String.valueOf(item).trim(); + } + + if (isIgnoreDuplicates()) { + if (!items.isEmpty() && item.equals(items.getLast())) { + return; + } + } + + items.add(item); + + maybeResize(); + } + + public void replace(final CharSequence item) { + items.removeLast(); + add(item); + } + + private void maybeResize() { + while (size() > getMaxSize()) { + items.removeFirst(); + offset++; + } + + index = size(); + } + + public ListIterator<Entry> entries(final int index) { + return new EntriesIterator(index - offset); + } + + public ListIterator<Entry> entries() { + return entries(offset); + } + + public Iterator<Entry> iterator() { + return entries(); + } + + private static class EntryImpl + implements Entry + { + private final int index; + + private final CharSequence value; + + public EntryImpl(int index, CharSequence value) { + this.index = index; + this.value = value; + } + + public int index() { + return index; + } + + public CharSequence value() { + return value; + } + + @Override + public String toString() { + return String.format("%d: %s", index, value); + } + } + + private class EntriesIterator + implements ListIterator<Entry> + { + private final ListIterator<CharSequence> source; + + private EntriesIterator(final int index) { + source = items.listIterator(index); + } + + public Entry next() { + if (!source.hasNext()) { + throw new NoSuchElementException(); + } + return new EntryImpl(offset + source.nextIndex(), source.next()); + } + + public Entry previous() { + if (!source.hasPrevious()) { + throw new NoSuchElementException(); + } + return new EntryImpl(offset + source.previousIndex(), source.previous()); + } + + public int nextIndex() { + return offset + source.nextIndex(); + } + + public int previousIndex() { + return offset + source.previousIndex(); + } + + public boolean hasNext() { + return source.hasNext(); + } + + public boolean hasPrevious() { + return source.hasPrevious(); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + + public void set(final Entry entry) { + throw new UnsupportedOperationException(); + } + + public void add(final Entry entry) { + throw new UnsupportedOperationException(); + } + } + + // + // Navigation + // + + /** + * This moves the history to the last entry. This entry is one position + * before the moveToEnd() position. + * + * @return Returns false if there were no history entries or the history + * index was already at the last entry. + */ + public boolean moveToLast() { + int lastEntry = size() - 1; + if (lastEntry >= 0 && lastEntry != index) { + index = size() - 1; + return true; + } + + return false; + } + + /** + * Move to the specified index in the history + * @param index + * @return + */ + public boolean moveTo(int index) { + index -= offset; + if (index >= 0 && index < size() ) { + this.index = index; + return true; + } + return false; + } + + /** + * Moves the history index to the first entry. + * + * @return Return false if there are no entries in the history or if the + * history is already at the beginning. + */ + public boolean moveToFirst() { + if (size() > 0 && index != 0) { + index = 0; + return true; + } + + return false; + } + + /** + * Move to the end of the history buffer. This will be a blank entry, after + * all of the other entries. + */ + public void moveToEnd() { + index = size(); + } + + /** + * Return the content of the current buffer. + */ + public CharSequence current() { + if (index >= size()) { + return ""; + } + + return items.get(index); + } + + /** + * Move the pointer to the previous element in the buffer. + * + * @return true if we successfully went to the previous element + */ + public boolean previous() { + if (index <= 0) { + return false; + } + + index--; + + return true; + } + + /** + * Move the pointer to the next element in the buffer. + * + * @return true if we successfully went to the next element + */ + public boolean next() { + if (index >= size()) { + return false; + } + + index++; + + return true; + } + + +}
\ No newline at end of file diff --git a/src/jline/src/main/java/jline/console/history/PersistentHistory.java b/src/jline/src/main/java/jline/console/history/PersistentHistory.java new file mode 100644 index 0000000000..b4cff792e3 --- /dev/null +++ b/src/jline/src/main/java/jline/console/history/PersistentHistory.java @@ -0,0 +1,34 @@ +/* + * 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.console.history; + +import java.io.IOException; + +/** + * Persistent {@link History}. + * + * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> + * @since 2.3 + */ +public interface PersistentHistory + extends History +{ + /** + * Flush all items to persistent storage. + * + * @throws IOException Flush failed + */ + void flush() throws IOException; + + /** + * Purge persistent storage and {@link #clear}. + * + * @throws IOException Purge failed + */ + void purge() throws IOException; +}
\ No newline at end of file diff --git a/src/jline/src/main/java/jline/console/history/package-info.java b/src/jline/src/main/java/jline/console/history/package-info.java new file mode 100644 index 0000000000..d001133efb --- /dev/null +++ b/src/jline/src/main/java/jline/console/history/package-info.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2009 the original author(s). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Console history support. + * + * @since 2.0 + */ +package jline.console.history;
\ No newline at end of file diff --git a/src/jline/src/main/java/jline/console/package-info.java b/src/jline/src/main/java/jline/console/package-info.java new file mode 100644 index 0000000000..ea2c05651d --- /dev/null +++ b/src/jline/src/main/java/jline/console/package-info.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2009 the original author(s). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Console support. + * + * @since 2.0 + */ +package jline.console;
\ No newline at end of file diff --git a/src/jline/src/main/java/jline/internal/Configuration.java b/src/jline/src/main/java/jline/internal/Configuration.java new file mode 100644 index 0000000000..6c6e0eb843 --- /dev/null +++ b/src/jline/src/main/java/jline/internal/Configuration.java @@ -0,0 +1,127 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package jline.internal; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +/** + * Provides access to configuration values. + * + * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> + * @since 2.4 + */ +public final class Configuration +{ + public static final String JLINE_RC = ".jline.rc"; + + private static final Properties userprops; + + static { + Properties props = new Properties(); + + File file = new File(getUserHome(), JLINE_RC); + if (file.exists() && file.canRead()) { + try { + InputStream input = new BufferedInputStream(new FileInputStream(file)); + try { + props.load(input); + Log.debug("Loaded user configuration: ", file); + } + finally { + input.close(); + } + } + catch (IOException e) { + Log.warn("Unable to read user configuration: ", file, e); + } + } + else { + Log.trace("User configuration file missing or unreadable: ", file); + } + + userprops = props; + } + + private static boolean isEmpty(final String value) { + return value == null || value.trim().length() == 0; + } + + public static String getString(final String name, final String defaultValue) { + assert name != null; + + String value; + + // Check sysprops first, it always wins + value = System.getProperty(name); + + if (isEmpty(value)) { + // Next try userprops + value = userprops.getProperty(name); + + if (isEmpty(value)) { + // else use the default + value = defaultValue; + } + } + + return value; + } + + public static String getString(final String name) { + return getString(name, null); + } + + public static Boolean getBoolean(final String name, final Boolean defaultValue) { + String value = getString(name); + if (isEmpty(value)) { + return defaultValue; + } + return Boolean.valueOf(value); + } + + public static Boolean getBoolean(final String name) { + return getBoolean(name, null); + } + + // + // System property helpers + // + + public static File getUserHome() { + return new File(System.getProperty("user.home")); + } + + public static String getOsName() { + return System.getProperty("os.name").toLowerCase(); + } + + public static String getFileEncoding() { + return System.getProperty("file.encoding"); + } + + public static String getInputEncoding() { + return System.getProperty("input.encoding", "UTF-8"); + } +}
\ No newline at end of file diff --git a/src/jline/src/main/java/jline/internal/Log.java b/src/jline/src/main/java/jline/internal/Log.java new file mode 100644 index 0000000000..deb34c89ae --- /dev/null +++ b/src/jline/src/main/java/jline/internal/Log.java @@ -0,0 +1,112 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package jline.internal; + +import java.io.PrintStream; + +/** + * Internal logger. + * + * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> + * @since 2.0 + */ +public final class Log +{ + ///CLOVER:OFF + + public static enum Level + { + TRACE, + DEBUG, + INFO, + WARN, + ERROR + } + + @SuppressWarnings({"StringConcatenation"}) + public static final boolean DEBUG = Boolean.getBoolean(Log.class.getName() + ".debug"); + + @SuppressWarnings({"StringConcatenation"}) + public static final boolean TRACE = Boolean.getBoolean(Log.class.getName() + ".trace"); + + private static PrintStream output = System.err; + + public static PrintStream getOutput() { + return output; + } + + public static void setOutput(final PrintStream out) { + assert out != null; + output = out; + } + + private static void print(final Object message) { + if (message instanceof Throwable) { + ((Throwable) message).printStackTrace(); + } + else if (message.getClass().isArray()) { + Object[] array = (Object[]) message; + + for (int i = 0; i < array.length; i++) { + output.print(array[i]); + if (i + 1 < array.length) { + output.print(","); + } + } + } + else { + output.print(message); + } + } + + private static void log(final Level level, final Object[] messages) { + //noinspection SynchronizeOnNonFinalField + synchronized (output) { + output.format("[%s] ", level); + + for (Object message : messages) { + print(message); + } + + output.println(); + output.flush(); + } + } + + public static void trace(final Object... messages) { + if (TRACE) { + log(Level.TRACE, messages); + } + } + + public static void debug(final Object... messages) { + if (TRACE || DEBUG) { + log(Level.DEBUG, messages); + } + } + + public static void warn(final Object... messages) { + log(Level.WARN, messages); + } + + public static void error(final Object... messages) { + log(Level.ERROR, messages); + } +}
\ No newline at end of file diff --git a/src/jline/src/main/java/jline/internal/ReplayPrefixOneCharInputStream.java b/src/jline/src/main/java/jline/internal/ReplayPrefixOneCharInputStream.java new file mode 100644 index 0000000000..ae54fbb329 --- /dev/null +++ b/src/jline/src/main/java/jline/internal/ReplayPrefixOneCharInputStream.java @@ -0,0 +1,95 @@ +package jline.internal; + +import java.io.IOException; +import java.io.InputStream; +import java.text.MessageFormat; + +/** + * This is awkward and inefficient, but probably the minimal way to add UTF-8 support to JLine + * + * @author <a href="mailto:Marc.Herbert@continuent.com">Marc Herbert</a> + * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> + * @since 2.0 + */ +public final class ReplayPrefixOneCharInputStream + extends InputStream +{ + private byte firstByte; + + private int byteLength; + + private InputStream wrappedStream; + + private int byteRead; + + private final String encoding; + + public ReplayPrefixOneCharInputStream(final String encoding) { + assert encoding != null; + this.encoding = encoding; + } + + public String getEncoding() { + return encoding; + } + + public void setInput(final int recorded, final InputStream wrapped) throws IOException { + this.byteRead = 0; + this.firstByte = (byte) recorded; + this.wrappedStream = wrapped; + + byteLength = 1; + if (encoding.equalsIgnoreCase("UTF-8")) { + setInputUTF8(recorded, wrapped); + } + else if (encoding.equalsIgnoreCase("UTF-16")) { + byteLength = 2; + } + else if (encoding.equalsIgnoreCase("UTF-32")) { + byteLength = 4; + } + } + + + public void setInputUTF8(final int recorded, final InputStream wrapped) throws IOException { + // 110yyyyy 10zzzzzz + if ((firstByte & (byte) 0xE0) == (byte) 0xC0) { + this.byteLength = 2; + } + // 1110xxxx 10yyyyyy 10zzzzzz + else if ((firstByte & (byte) 0xF0) == (byte) 0xE0) { + this.byteLength = 3; + } + // 11110www 10xxxxxx 10yyyyyy 10zzzzzz + else if ((firstByte & (byte) 0xF8) == (byte) 0xF0) { + this.byteLength = 4; + } + else { + throw new IOException(MessageFormat.format("Invalid UTF-8 first byte: {0}", firstByte)); + } + } + + public int read() throws IOException { + if (available() == 0) { + return -1; + } + + byteRead++; + + if (byteRead == 1) { + return firstByte; + } + + return wrappedStream.read(); + } + + /** + * InputStreamReader is greedy and will try to read bytes in advance. We + * do NOT want this to happen since we use a temporary/"losing bytes" + * InputStreamReader above, that's why we hide the real + * wrappedStream.available() here. + */ + public int available() { + return byteLength - byteRead; + } +}
\ No newline at end of file diff --git a/src/jline/src/main/java/jline/internal/TerminalLineSettings.java b/src/jline/src/main/java/jline/internal/TerminalLineSettings.java new file mode 100644 index 0000000000..4b4b957649 --- /dev/null +++ b/src/jline/src/main/java/jline/internal/TerminalLineSettings.java @@ -0,0 +1,217 @@ +/* + * 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.internal; + +import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.text.MessageFormat; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Provides access to terminal line settings via <tt>stty</tt>. + * + * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> + * @author <a href="mailto:dwkemp@gmail.com">Dale Kemp</a> + * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> + * @author <a href="mailto:jbonofre@apache.org">Jean-Baptiste Onofré</a> + * @since 2.0 + */ +public final class TerminalLineSettings +{ + public static final String JLINE_STTY = "jline.stty"; + + public static final String DEFAULT_STTY = "stty"; + + public static final String JLINE_SH = "jline.sh"; + + public static final String DEFAULT_SH = "sh"; + + private static String sttyCommand = Configuration.getString(JLINE_STTY, DEFAULT_STTY); + + private static String shCommand = Configuration.getString(JLINE_SH, DEFAULT_SH); + + private String config; + + private long configLastFetched; + + public TerminalLineSettings() throws IOException, InterruptedException { + config = get("-a"); + configLastFetched = System.currentTimeMillis(); + + Log.debug("Config: ", config); + + // sanity check + if (config.length() == 0) { + throw new IOException(MessageFormat.format("Unrecognized stty code: {0}", config)); + } + } + + public String getConfig() { + return config; + } + + public void restore() throws IOException, InterruptedException { + set("sane"); + } + + public String get(final String args) throws IOException, InterruptedException { + return stty(args); + } + + public void set(final String args) throws IOException, InterruptedException { + stty(args); + } + + /** + * <p> + * Get the value of a stty property, including the management of a cache. + * </p> + * + * @param name the stty property. + * @return the stty property value. + */ + public int getProperty(String name) { + assert name != null; + try { + // tty properties are cached so we don't have to worry too much about getting term widht/height + if (config == null || System.currentTimeMillis() - configLastFetched > 1000 ) { + config = get("-a"); + configLastFetched = System.currentTimeMillis(); + } + return this.getProperty(name, config); + } catch (Exception e) { + Log.warn("Failed to query stty ", name, e); + return -1; + } + } + + /** + * <p> + * Parses a stty output (provided by stty -a) and return the value of a given property. + * </p> + * + * @param name property name. + * @param stty string resulting of stty -a execution. + * @return value of the given property. + */ + protected int getProperty(String name, String stty) { + // try the first kind of regex + Pattern pattern = Pattern.compile(name + "\\s+=\\s+([^;]*)[;\\n\\r]"); + Matcher matcher = pattern.matcher(stty); + if (!matcher.find()) { + // try a second kind of regex + pattern = Pattern.compile(name + "\\s+([^;]*)[;\\n\\r]"); + matcher = pattern.matcher(stty); + if (!matcher.find()) { + // try a second try of regex + pattern = Pattern.compile("(\\S*)\\s+" + name); + matcher = pattern.matcher(stty); + if (!matcher.find()) { + return -1; + } + } + } + return parseControlChar(matcher.group(1)); + } + + private int parseControlChar(String str) { + // under + if ("<undef>".equals(str)) { + return -1; + } + // octal + if (str.charAt(0) == '0') { + return Integer.parseInt(str, 8); + } + // decimal + if (str.charAt(0) >= '1' && str.charAt(0) <= '9') { + return Integer.parseInt(str, 10); + } + // control char + if (str.charAt(0) == '^') { + if (str.charAt(1) == '?') { + return 127; + } else { + return str.charAt(1) - 64; + } + } else if (str.charAt(0) == 'M' && str.charAt(1) == '-') { + if (str.charAt(2) == '^') { + if (str.charAt(3) == '?') { + return 127 + 128; + } else { + return str.charAt(3) - 64 + 128; + } + } else { + return str.charAt(2) + 128; + } + } else { + return str.charAt(0); + } + } + + private static String stty(final String args) throws IOException, InterruptedException { + assert args != null; + return exec(String.format("%s %s < /dev/tty", sttyCommand, args)); + } + + private static String exec(final String cmd) throws IOException, InterruptedException { + assert cmd != null; + return exec(shCommand, "-c", cmd); + } + + private static String exec(final String... cmd) throws IOException, InterruptedException { + assert cmd != null; + + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + + Log.trace("Running: ", cmd); + + Process p = Runtime.getRuntime().exec(cmd); + + InputStream in = null; + InputStream err = null; + OutputStream out = null; + try { + int c; + in = p.getInputStream(); + while ((c = in.read()) != -1) { + bout.write(c); + } + err = p.getErrorStream(); + while ((c = err.read()) != -1) { + bout.write(c); + } + out = p.getOutputStream(); + p.waitFor(); + } + finally { + close(in, out, err); + } + + String result = bout.toString(); + + Log.trace("Result: ", result); + + return result; + } + + private static void close(final Closeable... closeables) { + for (Closeable c : closeables) { + try { + c.close(); + } + catch (Exception e) { + // Ignore + } + } + } +}
\ No newline at end of file diff --git a/src/jline/src/main/java/jline/internal/package-info.java b/src/jline/src/main/java/jline/internal/package-info.java new file mode 100644 index 0000000000..e15ddfb25c --- /dev/null +++ b/src/jline/src/main/java/jline/internal/package-info.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2009 the original author(s). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Internal support. + * + * @since 2.0 + */ +package jline.internal;
\ No newline at end of file diff --git a/src/jline/src/main/java/jline/package-info.java b/src/jline/src/main/java/jline/package-info.java new file mode 100644 index 0000000000..78924e262a --- /dev/null +++ b/src/jline/src/main/java/jline/package-info.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2009 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * JLine 2. + * + * @since 2.0 + */ +package jline;
\ No newline at end of file diff --git a/src/jline/src/main/java/jline/package.html b/src/jline/src/main/java/jline/package.html deleted file mode 100644 index c80743165a..0000000000 --- a/src/jline/src/main/java/jline/package.html +++ /dev/null @@ -1,9 +0,0 @@ -<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/jline/src/main/native/Makefile b/src/jline/src/main/native/Makefile deleted file mode 100644 index c620814825..0000000000 --- a/src/jline/src/main/native/Makefile +++ /dev/null @@ -1,8 +0,0 @@ - - -#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/jline/src/main/native/jline_WindowsTerminal.c b/src/jline/src/main/native/jline_WindowsTerminal.c deleted file mode 100644 index f020cae52b..0000000000 --- a/src/jline/src/main/native/jline_WindowsTerminal.c +++ /dev/null @@ -1,57 +0,0 @@ -#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/jline/src/main/native/jline_WindowsTerminal.h b/src/jline/src/main/native/jline_WindowsTerminal.h deleted file mode 100644 index 5078b939cb..0000000000 --- a/src/jline/src/main/native/jline_WindowsTerminal.h +++ /dev/null @@ -1,68 +0,0 @@ -/* 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/jline/src/main/resources/jline/CandidateListCompletionHandler.properties b/src/jline/src/main/resources/jline/CandidateListCompletionHandler.properties deleted file mode 100644 index 18ee221cc9..0000000000 --- a/src/jline/src/main/resources/jline/CandidateListCompletionHandler.properties +++ /dev/null @@ -1,5 +0,0 @@ -display-candidates: Display all {0} possibilities? (y or n) -display-candidates-yes: y -display-candidates-no: n -display-more: --More-- - diff --git a/src/jline/src/main/resources/jline/console/completer/CandidateListCompletionHandler.properties b/src/jline/src/main/resources/jline/console/completer/CandidateListCompletionHandler.properties new file mode 100644 index 0000000000..fd097efb8a --- /dev/null +++ b/src/jline/src/main/resources/jline/console/completer/CandidateListCompletionHandler.properties @@ -0,0 +1,4 @@ +DISPLAY_CANDIDATES=Display all %d possibilities? (y or n) +DISPLAY_CANDIDATES_YES=y +DISPLAY_CANDIDATES_NO=n +DISPLAY_MORE=--More-- diff --git a/src/jline/src/main/resources/jline/jline32.dll b/src/jline/src/main/resources/jline/jline32.dll Binary files differdeleted file mode 100644 index a0d3b117ce..0000000000 --- a/src/jline/src/main/resources/jline/jline32.dll +++ /dev/null diff --git a/src/jline/src/main/resources/jline/jline64.dll b/src/jline/src/main/resources/jline/jline64.dll Binary files differdeleted file mode 100644 index 922d6b01c5..0000000000 --- a/src/jline/src/main/resources/jline/jline64.dll +++ /dev/null diff --git a/src/jline/src/main/resources/jline/keybindings.properties b/src/jline/src/main/resources/jline/keybindings.properties index 6f13615d84..610a1626aa 100644 --- a/src/jline/src/main/resources/jline/keybindings.properties +++ b/src/jline/src/main/resources/jline/keybindings.properties @@ -1,62 +1,65 @@ # 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 +# [key code]=[logical operation] # CTRL-A: move to the beginning of the line -1: MOVE_TO_BEG +1=MOVE_TO_BEG + +# CTRL-B: move to the previous character +2=PREV_CHAR # CTRL-D: close out the input stream -4: EXIT +4=EXIT # CTRL-E: move the cursor to the end of the line -5: MOVE_TO_END +5=MOVE_TO_END + +# CTRL-F: move to the next character +6=NEXT_CHAR + +# CTRL-G: abort +7=ABORT # BACKSPACE, CTRL-H: delete the previous character # 8 is the ASCII code for backspace and therefor # deleting the previous character -8: DELETE_PREV_CHAR +8=DELETE_PREV_CHAR # TAB, CTRL-I: signal that console completion should be attempted -9: COMPLETE +9=COMPLETE # CTRL-J, CTRL-M: newline -10: NEWLINE +10=NEWLINE # CTRL-K: erase the current line -11: KILL_LINE - -# ENTER: newline -13: NEWLINE +11=KILL_LINE # CTRL-L: clear screen -12: CLEAR_SCREEN +12=CLEAR_SCREEN + +# ENTER: newline +13=NEWLINE # CTRL-N: scroll to the next element in the history buffer -14: NEXT_HISTORY +14=NEXT_HISTORY + +# CTRL-O: move to the previous word +15=PREV_WORD # CTRL-P: scroll to the previous element in the history buffer -16: PREV_HISTORY +16=PREV_HISTORY # CTRL-R: redraw the current line -18: REDISPLAY +18=SEARCH_PREV # CTRL-U: delete all the characters before the cursor position -21: KILL_LINE_PREV +21=KILL_LINE_PREV # CTRL-V: paste the contents of the clipboard (useful for Windows terminal) -22: PASTE +22=PASTE # CTRL-W: delete the word directly before the cursor -23: DELETE_PREV_WORD +23=DELETE_PREV_WORD -# DELETE, CTRL-?: delete the previous character +# DELETE, CTRL-?: delete the next character # 127 is the ASCII code for delete -127: DELETE_PREV_CHAR +127=DELETE_NEXT_CHAR diff --git a/src/jline/src/main/resources/jline/windowsbindings.properties b/src/jline/src/main/resources/jline/windowsbindings.properties index 51ba4919c9..340b5aa5b9 100644 --- a/src/jline/src/main/resources/jline/windowsbindings.properties +++ b/src/jline/src/main/resources/jline/windowsbindings.properties @@ -1,65 +1,71 @@ # Keybinding mapping for JLine. The format is: -# [key code]: [logical operation] +# [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 +2=PREV_CHAR # CTRL-C: toggle overtype mode (frankly, I wasn't sure where to bind this) -3: INSERT - -# 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 +3=INSERT # CTRL-D: close out the input stream -4: EXIT +4=EXIT # CTRL-E: move the cursor to the end of the line -5: MOVE_TO_END +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 +8=DELETE_PREV_CHAR # TAB, CTRL-I: signal that console completion should be attempted -9: COMPLETE +9=COMPLETE # CTRL-J, CTRL-M: newline -10: NEWLINE +10=NEWLINE -# CTRL-K: Vertical tab - on windows we'll move to the start of the history -11: START_OF_HISTORY +# CTRL-K: erase the current line +11=KILL_LINE -# ENTER: newline -13: NEWLINE +# CTRL-L: clear screen +12=CLEAR_SCREEN -# 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 +14=NEXT_HISTORY + +# CTRL-O: move to the previous word +15=PREV_WORD # CTRL-P: scroll to the previous element in the history buffer -16: PREV_HISTORY +16=PREV_HISTORY + +# CTRL-R: search backwards in history +18=SEARCH_PREV -# CTRL-R: redraw the current line -18: REDISPLAY +# CTRL-S: Move to the end of the history +19=END_OF_HISTORY # CTRL-U: delete all the characters before the cursor position -21: KILL_LINE_PREV +21=KILL_LINE_PREV # CTRL-V: paste the contents of the clipboard (useful for Windows terminal) -22: PASTE +22=PASTE # CTRL-W: delete the word directly before the cursor -23: DELETE_PREV_WORD +23=DELETE_PREV_WORD # CTRL-[: escape - clear the current line. -27: CLEAR_LINE +27=CLEAR_LINE # CTRL-?: delete the previous character -127: DELETE_NEXT_CHAR +127=DELETE_NEXT_CHAR diff --git a/src/jline/src/site/apt/building.apt b/src/jline/src/site/apt/building.apt deleted file mode 100644 index a09137873a..0000000000 --- a/src/jline/src/site/apt/building.apt +++ /dev/null @@ -1,39 +0,0 @@ - ------ - 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/jline/src/site/apt/downloads.apt b/src/jline/src/site/apt/downloads.apt deleted file mode 100644 index de90db9595..0000000000 --- a/src/jline/src/site/apt/downloads.apt +++ /dev/null @@ -1,39 +0,0 @@ - ------ - 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/jline/src/site/docbook/index.xml b/src/jline/src/site/docbook/index.xml deleted file mode 100644 index c68b42f1e9..0000000000 --- a/src/jline/src/site/docbook/index.xml +++ /dev/null @@ -1,492 +0,0 @@ -<!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/jline/src/site/fml/faq.fml b/src/jline/src/site/fml/faq.fml deleted file mode 100644 index b9902b0fa7..0000000000 --- a/src/jline/src/site/fml/faq.fml +++ /dev/null @@ -1,26 +0,0 @@ - <?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/jline/src/site/resources/css/site.css b/src/jline/src/site/resources/css/site.css deleted file mode 100755 index 5d71608c15..0000000000 --- a/src/jline/src/site/resources/css/site.css +++ /dev/null @@ -1,311 +0,0 @@ -
-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/jline/src/site/resources/images/collapsed.png b/src/jline/src/site/resources/images/collapsed.png Binary files differdeleted file mode 100755 index a02c1e67c9..0000000000 --- a/src/jline/src/site/resources/images/collapsed.png +++ /dev/null diff --git a/src/jline/src/site/resources/images/dotted.png b/src/jline/src/site/resources/images/dotted.png Binary files differdeleted file mode 100755 index 8a4d443a4a..0000000000 --- a/src/jline/src/site/resources/images/dotted.png +++ /dev/null diff --git a/src/jline/src/site/resources/images/expanded.png b/src/jline/src/site/resources/images/expanded.png Binary files differdeleted file mode 100755 index 8a19dbf103..0000000000 --- a/src/jline/src/site/resources/images/expanded.png +++ /dev/null diff --git a/src/jline/src/site/resources/images/external.png b/src/jline/src/site/resources/images/external.png Binary files differdeleted file mode 100755 index 19f28955ca..0000000000 --- a/src/jline/src/site/resources/images/external.png +++ /dev/null diff --git a/src/jline/src/site/resources/images/ico_file_pdf.png b/src/jline/src/site/resources/images/ico_file_pdf.png Binary files differdeleted file mode 100644 index 9ceb00f2dd..0000000000 --- a/src/jline/src/site/resources/images/ico_file_pdf.png +++ /dev/null diff --git a/src/jline/src/site/resources/images/logo.jpg b/src/jline/src/site/resources/images/logo.jpg Binary files differdeleted file mode 100644 index 1f1da5c8f2..0000000000 --- a/src/jline/src/site/resources/images/logo.jpg +++ /dev/null diff --git a/src/jline/src/site/resources/images/newwindow.png b/src/jline/src/site/resources/images/newwindow.png Binary files differdeleted file mode 100755 index 1374c228af..0000000000 --- a/src/jline/src/site/resources/images/newwindow.png +++ /dev/null diff --git a/src/jline/src/site/site.xml b/src/jline/src/site/site.xml deleted file mode 100644 index 274ec83ee4..0000000000 --- a/src/jline/src/site/site.xml +++ /dev/null @@ -1,40 +0,0 @@ -<?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/jline/src/test/java/jline/ConsoleReaderTest.java b/src/jline/src/test/java/jline/ConsoleReaderTest.java deleted file mode 100644 index 4a25783c58..0000000000 --- a/src/jline/src/test/java/jline/ConsoleReaderTest.java +++ /dev/null @@ -1,162 +0,0 @@ -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/jline/src/test/java/jline/JLineTestCase.java b/src/jline/src/test/java/jline/JLineTestCase.java deleted file mode 100644 index 92e09d485d..0000000000 --- a/src/jline/src/test/java/jline/JLineTestCase.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ -package 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/jline/src/test/java/jline/TerminalFactoryTest.java b/src/jline/src/test/java/jline/TerminalFactoryTest.java new file mode 100644 index 0000000000..3b38fbec95 --- /dev/null +++ b/src/jline/src/test/java/jline/TerminalFactoryTest.java @@ -0,0 +1,34 @@ +package jline; + +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * Tests for the {@link TerminalFactory}. + */ +public class TerminalFactoryTest +{ + @Before + public void setUp() throws Exception { + TerminalFactory.reset(); + } + + @Test + public void testConfigureNone() { + TerminalFactory.configure(TerminalFactory.NONE); + Terminal t = TerminalFactory.get(); + assertNotNull(t); + assertEquals(UnsupportedTerminal.class.getName(), t.getClass().getName()); + } + + @Test + public void testConfigureUnsupportedTerminal() { + TerminalFactory.configure(UnsupportedTerminal.class.getName()); + Terminal t = TerminalFactory.get(); + assertNotNull(t); + assertEquals(UnsupportedTerminal.class.getName(), t.getClass().getName()); + } +}
\ No newline at end of file diff --git a/src/jline/src/test/java/jline/TestCompletion.java b/src/jline/src/test/java/jline/TestCompletion.java deleted file mode 100644 index a481e2625b..0000000000 --- a/src/jline/src/test/java/jline/TestCompletion.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ -package 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/jline/src/test/java/jline/TestEditLine.java b/src/jline/src/test/java/jline/TestEditLine.java deleted file mode 100644 index 88b45240ed..0000000000 --- a/src/jline/src/test/java/jline/TestEditLine.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ -package 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/jline/src/test/java/jline/TestHistory.java b/src/jline/src/test/java/jline/TestHistory.java deleted file mode 100644 index a39afa5c19..0000000000 --- a/src/jline/src/test/java/jline/TestHistory.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ -package 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 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/jline/src/test/java/jline/console/ConsoleReaderTest.java b/src/jline/src/test/java/jline/console/ConsoleReaderTest.java new file mode 100644 index 0000000000..45ba4775e1 --- /dev/null +++ b/src/jline/src/test/java/jline/console/ConsoleReaderTest.java @@ -0,0 +1,261 @@ +package jline.console; + +import jline.TerminalFactory; +import jline.WindowsTerminal; +import jline.console.history.History; +import jline.console.history.MemoryHistory; +import org.junit.Before; +import org.junit.Test; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.StringWriter; +import java.io.Writer; + +import static jline.WindowsTerminal.WindowsKey.DELETE_KEY; +import static jline.WindowsTerminal.WindowsKey.END_KEY; +import static jline.WindowsTerminal.WindowsKey.ESCAPE_KEY; +import static jline.WindowsTerminal.WindowsKey.HOME_KEY; +import static jline.WindowsTerminal.WindowsKey.INSERT_KEY; +import static jline.WindowsTerminal.WindowsKey.LEFT_ARROW_KEY; +import static jline.WindowsTerminal.WindowsKey.NUMPAD_KEY_INDICATOR; +import static jline.WindowsTerminal.WindowsKey.PAGE_DOWN_KEY; +import static jline.WindowsTerminal.WindowsKey.PAGE_UP_KEY; +import static jline.WindowsTerminal.WindowsKey.SPECIAL_KEY_INDICATOR; +import static jline.console.Operation.DELETE_NEXT_CHAR; +import static jline.console.Operation.DELETE_PREV_CHAR; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * Tests for the {@link ConsoleReader}. + */ +public class ConsoleReaderTest +{ + @Before + public void setUp() throws Exception { + System.setProperty(WindowsTerminal.JLINE_WINDOWS_TERMINAL_DIRECT_CONSOLE, "false"); + } + + private void assertWindowsKeyBehavior(String expected, char[] input) throws Exception { + StringBuilder buffer = new StringBuilder(); + buffer.append(input); + ConsoleReader reader = createConsole(buffer.toString().getBytes()); + assertNotNull(reader); + String line = reader.readLine(); + assertEquals(expected, line); + } + + private ConsoleReader createConsole(byte[] bytes) throws Exception { + InputStream in = new ByteArrayInputStream(bytes); + Writer writer = new StringWriter(); + ConsoleReader reader = new ConsoleReader(in, writer); + reader.setHistory(createSeededHistory()); + return reader; + } + + private History createSeededHistory() { + History history = new MemoryHistory(); + history.add("dir"); + history.add("cd c:\\"); + history.add("mkdir monkey"); + return history; + } + + @Test + public void testDeleteAndBackspaceKeymappings() throws Exception { + // test only works on Windows + if (!(TerminalFactory.get() instanceof WindowsTerminal)) { + return; + } + + ConsoleReader consoleReader = new ConsoleReader(); + assertNotNull(consoleReader); + assertEquals(127, consoleReader.getKeyForAction(DELETE_NEXT_CHAR)); + assertEquals(8, consoleReader.getKeyForAction(DELETE_PREV_CHAR)); + } + + @Test + public void testReadline() throws Exception { + ConsoleReader consoleReader = createConsole("Sample String\r\n".getBytes()); + assertNotNull(consoleReader); + String line = consoleReader.readLine(); + assertEquals("Sample String", line); + } + + @Test + public void testDeleteOnWindowsTerminal() throws Exception { + // test only works on Windows + if (!(TerminalFactory.get() instanceof WindowsTerminal)) { + return; + } + + char[] characters = new char[]{ + 'S', 's', + (char) SPECIAL_KEY_INDICATOR.code, + (char) LEFT_ARROW_KEY.code, + (char) SPECIAL_KEY_INDICATOR.code, + (char) DELETE_KEY.code, '\r', 'n' + }; + assertWindowsKeyBehavior("S", characters); + } + + @Test + public void testNumpadDeleteOnWindowsTerminal() throws Exception { + // test only works on Windows + if (!(TerminalFactory.get() instanceof WindowsTerminal)) { + return; + } + + char[] characters = new char[]{ + 'S', 's', + (char) NUMPAD_KEY_INDICATOR.code, + (char) LEFT_ARROW_KEY.code, + (char) NUMPAD_KEY_INDICATOR.code, + (char) DELETE_KEY.code, '\r', 'n' + }; + assertWindowsKeyBehavior("S", characters); + } + + @Test + public void testHomeKeyOnWindowsTerminal() throws Exception { + // test only works on Windows + if (!(TerminalFactory.get() instanceof WindowsTerminal)) { + return; + } + + char[] characters = new char[]{ + 'S', 's', + (char) SPECIAL_KEY_INDICATOR.code, + (char) HOME_KEY.code, 'x', '\r', '\n' + }; + assertWindowsKeyBehavior("xSs", characters); + + } + + @Test + public void testEndKeyOnWindowsTerminal() throws Exception { + // test only works on Windows + if (!(TerminalFactory.get() instanceof WindowsTerminal)) { + return; + } + + char[] characters = new char[]{ + 'S', 's', + (char) SPECIAL_KEY_INDICATOR.code, + (char) HOME_KEY.code, 'x', + (char) SPECIAL_KEY_INDICATOR.code, (char) END_KEY.code, + 'j', '\r', '\n' + }; + assertWindowsKeyBehavior("xSsj", characters); + } + + @Test + public void testPageUpOnWindowsTerminal() throws Exception { + // test only works on Windows + if (!(TerminalFactory.get() instanceof WindowsTerminal)) { + return; + } + + char[] characters = new char[]{ + (char) SPECIAL_KEY_INDICATOR.code, + (char) PAGE_UP_KEY.code, '\r', '\n' + }; + assertWindowsKeyBehavior("dir", characters); + } + + @Test + public void testPageDownOnWindowsTerminal() throws Exception { + // test only works on Windows + if (!(TerminalFactory.get() instanceof WindowsTerminal)) { + return; + } + + char[] characters = new char[]{ + (char) SPECIAL_KEY_INDICATOR.code, + (char) PAGE_DOWN_KEY.code, '\r', '\n' + }; + assertWindowsKeyBehavior("mkdir monkey", characters); + } + + @Test + public void testEscapeOnWindowsTerminal() throws Exception { + // test only works on Windows + if (!(TerminalFactory.get() instanceof WindowsTerminal)) { + return; + } + + char[] characters = new char[]{ + 's', 's', 's', + (char) SPECIAL_KEY_INDICATOR.code, + (char) ESCAPE_KEY.code, '\r', '\n' + }; + assertWindowsKeyBehavior("", characters); + } + + @Test + public void testInsertOnWindowsTerminal() throws Exception { + // test only works on Windows + if (!(TerminalFactory.get() instanceof WindowsTerminal)) { + return; + } + + char[] characters = new char[]{ + 'o', 'p', 's', + (char) SPECIAL_KEY_INDICATOR.code, + (char) HOME_KEY.code, + (char) SPECIAL_KEY_INDICATOR.code, + (char) INSERT_KEY.code, 'o', 'o', 'p', 's', '\r', '\n' + }; + assertWindowsKeyBehavior("oops", characters); + } + + @Test + public void testExpansion() throws Exception { + ConsoleReader reader = new ConsoleReader(); + MemoryHistory history = new MemoryHistory(); + history.setMaxSize(3); + history.add("foo"); + history.add("dir"); + history.add("cd c:\\"); + history.add("mkdir monkey"); + reader.setHistory(history); + + assertEquals("echo a!", reader.expandEvents("echo a!")); + assertEquals("mkdir monkey ; echo a!", reader.expandEvents("!! ; echo a!")); + assertEquals("echo ! a", reader.expandEvents("echo ! a")); + assertEquals("echo !\ta", reader.expandEvents("echo !\ta")); + + assertEquals("mkdir barey", reader.expandEvents("^monk^bar^")); + assertEquals("mkdir barey", reader.expandEvents("^monk^bar")); + assertEquals("a^monk^bar", reader.expandEvents("a^monk^bar")); + + assertEquals("mkdir monkey", reader.expandEvents("!!")); + assertEquals("echo echo a", reader.expandEvents("echo !#a")); + + assertEquals("mkdir monkey", reader.expandEvents("!mk")); + try { + reader.expandEvents("!mz"); + } catch (IllegalArgumentException e) { + assertEquals("!mz: event not found", e.getMessage()); + } + + assertEquals("mkdir monkey", reader.expandEvents("!?mo")); + assertEquals("mkdir monkey", reader.expandEvents("!?mo?")); + + assertEquals("mkdir monkey", reader.expandEvents("!-1")); + assertEquals("cd c:\\", reader.expandEvents("!-2")); + assertEquals("cd c:\\", reader.expandEvents("!2")); + assertEquals("mkdir monkey", reader.expandEvents("!3")); + try { + reader.expandEvents("!20"); + } catch (IllegalArgumentException e) { + assertEquals("!20: event not found", e.getMessage()); + } + try { + reader.expandEvents("!-20"); + } catch (IllegalArgumentException e) { + assertEquals("!-20: event not found", e.getMessage()); + } + } +} diff --git a/src/jline/src/test/java/jline/console/ConsoleReaderTestSupport.java b/src/jline/src/test/java/jline/console/ConsoleReaderTestSupport.java new file mode 100644 index 0000000000..7bb65fe3ca --- /dev/null +++ b/src/jline/src/test/java/jline/console/ConsoleReaderTestSupport.java @@ -0,0 +1,142 @@ +/* + * 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.console; + +import jline.UnixTerminal; +import org.junit.Before; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +import static jline.UnixTerminal.UnixKey.ARROW_DOWN; +import static jline.UnixTerminal.UnixKey.ARROW_LEFT; +import static jline.UnixTerminal.UnixKey.ARROW_PREFIX; +import static jline.UnixTerminal.UnixKey.ARROW_RIGHT; +import static jline.UnixTerminal.UnixKey.ARROW_START; +import static jline.UnixTerminal.UnixKey.ARROW_UP; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +/** + * Provides support for console reader tests. + */ +public abstract class ConsoleReaderTestSupport +{ + protected ConsoleReader console; + + @Before + public void setUp() throws Exception { + console = new ConsoleReader(null, new PrintWriter(new OutputStreamWriter(new ByteArrayOutputStream())), new UnixTerminal()); + } + + protected void assertBuffer(final String expected, final Buffer buffer) throws IOException { + assertBuffer(expected, buffer, true); + } + + protected void assertBuffer(final String expected, final Buffer buffer, final boolean clear) throws IOException { + // clear current buffer, if any + if (clear) { + console.finishBuffer(); + console.getHistory().clear(); + } + + console.setInput(new ByteArrayInputStream(buffer.getBytes())); + + // run it through the reader + while (console.readLine((String) null) != null) { + // ignore + } + + assertEquals(expected, console.getCursorBuffer().toString()); + } + + private int getKeyForAction(final Operation key) { + return getKeyForAction(key.code); + } + + private int getKeyForAction(final short logicalAction) { + int action = console.getKeyForAction(logicalAction); + + if (action == -1) { + fail("Keystroke for logical action " + logicalAction + " was not bound in the console"); + } + + return action; + } + + protected class Buffer + { + private final ByteArrayOutputStream out = new ByteArrayOutputStream(); + + public Buffer() { + // nothing + } + + public Buffer(final String str) { + append(str); + } + + public byte[] getBytes() { + return out.toByteArray(); + } + + public Buffer op(final short operation) { + return append(getKeyForAction(operation)); + } + + public Buffer op(final Operation op) { + return op(op.code); + } + + public Buffer ctrlA() { + return append(getKeyForAction(Operation.MOVE_TO_BEG)); + } + + public Buffer ctrlU() { + return append(getKeyForAction(Operation.KILL_LINE_PREV)); + } + + public Buffer tab() { + return append(getKeyForAction(Operation.COMPLETE)); + } + + public Buffer back() { + return append(getKeyForAction(Operation.DELETE_PREV_CHAR)); + } + + public Buffer left() { + return append(ARROW_START.code).append(ARROW_PREFIX.code).append(ARROW_LEFT.code); + } + + public Buffer right() { + return append(ARROW_START.code).append(ARROW_PREFIX.code).append(ARROW_RIGHT.code); + } + + public Buffer up() { + return append(ARROW_START.code).append(ARROW_PREFIX.code).append(ARROW_UP.code); + } + + public Buffer down() { + return append(ARROW_START.code).append(ARROW_PREFIX.code).append(ARROW_DOWN.code); + } + + public Buffer append(final String str) { + for (byte b : str.getBytes()) { + append(b); + } + return this; + } + + public Buffer append(final int i) { + out.write((byte) i); + return this; + } + } +} diff --git a/src/jline/src/test/java/jline/console/EditLineTest.java b/src/jline/src/test/java/jline/console/EditLineTest.java new file mode 100644 index 0000000000..3471ab4276 --- /dev/null +++ b/src/jline/src/test/java/jline/console/EditLineTest.java @@ -0,0 +1,172 @@ +/* + * 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.console; + +import org.junit.Test; + +import static jline.console.Operation.DELETE_PREV_WORD; +import static jline.console.Operation.MOVE_TO_END; +import static jline.console.Operation.PREV_WORD; + +/** + * Tests various features of editing lines. + * + * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> + */ +public class EditLineTest + extends ConsoleReaderTestSupport +{ + @Test + public void testDeletePreviousWord() throws Exception { + Buffer b = new Buffer("This is a test"); + + assertBuffer("This is a ", b = b.op(DELETE_PREV_WORD)); + assertBuffer("This is ", b = b.op(DELETE_PREV_WORD)); + assertBuffer("This ", b = b.op(DELETE_PREV_WORD)); + assertBuffer("", b = b.op(DELETE_PREV_WORD)); + assertBuffer("", b = b.op(DELETE_PREV_WORD)); + assertBuffer("", b = b.op(DELETE_PREV_WORD)); + } + + @Test + public void testMoveToEnd() throws Exception { + Buffer b = new Buffer("This is a test"); + + assertBuffer("This is a XtestX", + new Buffer("This is a test").op(PREV_WORD) + .append('X') + .op(MOVE_TO_END) + .append('X')); + + assertBuffer("This is Xa testX", + new Buffer("This is a test").op(PREV_WORD) + .op(PREV_WORD) + .append('X') + .op(MOVE_TO_END) + .append('X')); + + assertBuffer("This Xis a testX", + new Buffer("This is a test").op(PREV_WORD) + .op(PREV_WORD) + .op(PREV_WORD) + .append('X') + .op(MOVE_TO_END) + .append('X')); + } + + @Test + public void testPreviousWord() throws Exception { + assertBuffer("This is a Xtest", + new Buffer("This is a test").op(PREV_WORD) + .append('X')); + assertBuffer("This is Xa test", + new Buffer("This is a test").op(PREV_WORD) + .op(PREV_WORD) + .append('X')); + assertBuffer("This Xis a test", + new Buffer("This is a test").op(PREV_WORD) + .op(PREV_WORD) + .op(PREV_WORD) + .append('X')); + assertBuffer("XThis is a test", + new Buffer("This is a test").op(PREV_WORD) + .op(PREV_WORD) + .op(PREV_WORD) + .op(PREV_WORD) + .append('X')); + assertBuffer("XThis is a test", + new Buffer("This is a test").op(PREV_WORD) + .op(PREV_WORD) + .op(PREV_WORD) + .op(PREV_WORD) + .op(PREV_WORD) + .append('X')); + assertBuffer("XThis is a test", + new Buffer("This is a test").op(PREV_WORD) + .op(PREV_WORD) + .op(PREV_WORD) + .op(PREV_WORD) + .op(PREV_WORD) + .op(PREV_WORD) + .append('X')); + } + + @Test + public void testLineStart() throws Exception { + assertBuffer("XThis is a test", + new Buffer("This is a test").ctrlA().append('X')); + assertBuffer("TXhis is a test", + new Buffer("This is a test").ctrlA().right().append('X')); + } + + @Test + public void testClearLine() throws Exception { + assertBuffer("", new Buffer("This is a test").ctrlU()); + assertBuffer("t", new Buffer("This is a test").left().ctrlU()); + assertBuffer("st", new Buffer("This is a test").left().left().ctrlU()); + } + + @Test + public void testRight() throws Exception { + Buffer b = new Buffer("This is a test"); + b = b.left().right().back(); + assertBuffer("This is a tes", b); + b = b.left().left().left().right().left().back(); + assertBuffer("This is ates", b); + b.append('X'); + assertBuffer("This is aXtes", b); + } + + @Test + public void testLeft() throws Exception { + Buffer b = new Buffer("This is a test"); + b = b.left().left().left(); + assertBuffer("This is a est", b = b.back()); + assertBuffer("This is aest", b = b.back()); + assertBuffer("This is est", b = b.back()); + assertBuffer("This isest", b = b.back()); + assertBuffer("This iest", b = b.back()); + assertBuffer("This est", b = b.back()); + assertBuffer("Thisest", b = b.back()); + assertBuffer("Thiest", b = b.back()); + assertBuffer("Thest", b = b.back()); + assertBuffer("Test", b = b.back()); + assertBuffer("est", b = b.back()); + assertBuffer("est", b = b.back()); + assertBuffer("est", b = b.back()); + assertBuffer("est", b = b.back()); + assertBuffer("est", b = b.back()); + } + + @Test + public void testBackspace() throws Exception { + Buffer b = new Buffer("This is a test"); + assertBuffer("This is a tes", b = b.back()); + assertBuffer("This is a te", b = b.back()); + assertBuffer("This is a t", b = b.back()); + assertBuffer("This is a ", b = b.back()); + assertBuffer("This is a", b = b.back()); + assertBuffer("This is ", b = b.back()); + assertBuffer("This is", b = b.back()); + assertBuffer("This i", b = b.back()); + assertBuffer("This ", b = b.back()); + assertBuffer("This", b = b.back()); + assertBuffer("Thi", b = b.back()); + assertBuffer("Th", b = b.back()); + assertBuffer("T", b = b.back()); + assertBuffer("", b = b.back()); + assertBuffer("", b = b.back()); + assertBuffer("", b = b.back()); + assertBuffer("", b = b.back()); + assertBuffer("", b = b.back()); + } + + @Test + public void testBuffer() throws Exception { + assertBuffer("This is a test", new Buffer("This is a test")); + } +} diff --git a/src/jline/src/test/java/jline/console/completer/ArgumentCompleterTest.java b/src/jline/src/test/java/jline/console/completer/ArgumentCompleterTest.java new file mode 100644 index 0000000000..a74acf4adb --- /dev/null +++ b/src/jline/src/test/java/jline/console/completer/ArgumentCompleterTest.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2010 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package jline.console.completer; + +import jline.console.ConsoleReaderTestSupport; +import jline.console.completer.ArgumentCompleter; +import jline.console.completer.StringsCompleter; +import org.junit.Test; + +/** + * Tests for {@link jline.console.completer.ArgumentCompleter}. + * + * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> + */ +public class ArgumentCompleterTest + extends ConsoleReaderTestSupport +{ + @Test + public void test1() throws Exception { + console.addCompleter(new ArgumentCompleter(new StringsCompleter("foo", "bar", "baz"))); + + assertBuffer("foo foo ", new Buffer("foo f").tab()); + assertBuffer("foo ba", new Buffer("foo b").tab()); + assertBuffer("foo ba", new Buffer("foo ba").tab()); + assertBuffer("foo baz ", new Buffer("foo baz").tab()); + + // test completion in the mid range + assertBuffer("foo baz", new Buffer("f baz").left().left().left().left().tab()); + assertBuffer("ba foo", new Buffer("b foo").left().left().left().left().tab()); + assertBuffer("foo ba baz", new Buffer("foo b baz").left().left().left().left().tab()); + assertBuffer("foo foo baz", new Buffer("foo f baz").left().left().left().left().tab()); + } +}
\ No newline at end of file diff --git a/src/jline/src/test/java/jline/console/completer/NullCompleterTest.java b/src/jline/src/test/java/jline/console/completer/NullCompleterTest.java new file mode 100644 index 0000000000..accdd801ac --- /dev/null +++ b/src/jline/src/test/java/jline/console/completer/NullCompleterTest.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2010 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package jline.console.completer; + +import jline.console.ConsoleReaderTestSupport; +import jline.console.completer.NullCompleter; +import org.junit.Test; + +/** + * Tests for {@link NullCompleter}. + * + * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> + */ +public class NullCompleterTest + extends ConsoleReaderTestSupport +{ + @Test + public void test1() throws Exception { + console.addCompleter(NullCompleter.INSTANCE); + + assertBuffer("f", new Buffer("f").tab()); + assertBuffer("ba", new Buffer("ba").tab()); + assertBuffer("baz", new Buffer("baz").tab()); + } +}
\ No newline at end of file diff --git a/src/jline/src/test/java/jline/console/completer/StringsCompleterTest.java b/src/jline/src/test/java/jline/console/completer/StringsCompleterTest.java new file mode 100644 index 0000000000..6c7c982a25 --- /dev/null +++ b/src/jline/src/test/java/jline/console/completer/StringsCompleterTest.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2010 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package jline.console.completer; + +import jline.console.ConsoleReaderTestSupport; +import jline.console.completer.StringsCompleter; +import org.junit.Test; + +/** + * Tests for {@link jline.console.completer.StringsCompleter}. + * + * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> + */ +public class StringsCompleterTest + extends ConsoleReaderTestSupport +{ + @Test + public void test1() throws Exception { + console.addCompleter(new StringsCompleter("foo", "bar", "baz")); + + assertBuffer("foo ", new Buffer("f").tab()); + // single tab completes to unambiguous "ba" + assertBuffer("ba", new Buffer("b").tab()); + assertBuffer("ba", new Buffer("ba").tab()); + assertBuffer("baz ", new Buffer("baz").tab()); + } +}
\ No newline at end of file diff --git a/src/jline/src/test/java/jline/console/history/HistoryTest.java b/src/jline/src/test/java/jline/console/history/HistoryTest.java new file mode 100644 index 0000000000..c53107d984 --- /dev/null +++ b/src/jline/src/test/java/jline/console/history/HistoryTest.java @@ -0,0 +1,79 @@ +/* + * 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.console.history; + +import jline.console.ConsoleReaderTestSupport; +import org.junit.Test; + +import static jline.console.Operation.MOVE_TO_BEG; +import static jline.console.Operation.NEWLINE; +import static jline.console.Operation.NEXT_HISTORY; +import static jline.console.Operation.PREV_HISTORY; +import static jline.console.Operation.PREV_CHAR; + +/** + * Tests command history. + * + * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> + */ +public class HistoryTest + extends ConsoleReaderTestSupport +{ + @Test + public void testSingleHistory() throws Exception { + Buffer b = new Buffer(). + append("test line 1").op(NEWLINE). + append("test line 2").op(NEWLINE). + append("test line 3").op(NEWLINE). + append("test line 4").op(NEWLINE). + append("test line 5").op(NEWLINE). + append(""); + + assertBuffer("", b); + + assertBuffer("test line 5", b = b.op(PREV_HISTORY)); + assertBuffer("test line 5", b = b.op(PREV_CHAR)); + assertBuffer("test line 4", b = b.op(PREV_HISTORY)); + assertBuffer("test line 5", b = b.op(NEXT_HISTORY)); + assertBuffer("test line 4", b = b.op(PREV_HISTORY)); + assertBuffer("test line 3", b = b.op(PREV_HISTORY)); + assertBuffer("test line 2", b = b.op(PREV_HISTORY)); + assertBuffer("test line 1", b = b.op(PREV_HISTORY)); + + // beginning of history + assertBuffer("test line 1", b = b.op(PREV_HISTORY)); + assertBuffer("test line 1", b = b.op(PREV_HISTORY)); + assertBuffer("test line 1", b = b.op(PREV_HISTORY)); + assertBuffer("test line 1", b = b.op(PREV_HISTORY)); + + assertBuffer("test line 2", b = b.op(NEXT_HISTORY)); + assertBuffer("test line 3", b = b.op(NEXT_HISTORY)); + assertBuffer("test line 4", b = b.op(NEXT_HISTORY)); + assertBuffer("test line 5", b = b.op(NEXT_HISTORY)); + + // end of history + assertBuffer("", b = b.op(NEXT_HISTORY)); + assertBuffer("", b = b.op(NEXT_HISTORY)); + assertBuffer("", b = b.op(NEXT_HISTORY)); + + assertBuffer("test line 5", b = b.op(PREV_HISTORY)); + assertBuffer("test line 4", b = b.op(PREV_HISTORY)); + b = b.op(MOVE_TO_BEG).append("XXX").op(NEWLINE); + assertBuffer("XXXtest line 4", b = b.op(PREV_HISTORY)); + assertBuffer("test line 5", b = b.op(PREV_HISTORY)); + assertBuffer("test line 4", b = b.op(PREV_HISTORY)); + assertBuffer("test line 5", b = b.op(NEXT_HISTORY)); + assertBuffer("XXXtest line 4", b = b.op(NEXT_HISTORY)); + assertBuffer("", b = b.op(NEXT_HISTORY)); + + assertBuffer("XXXtest line 4", b = b.op(PREV_HISTORY)); + assertBuffer("XXXtest line 4", b = b.op(NEWLINE).op(PREV_HISTORY)); + assertBuffer("XXXtest line 4", b = b.op(NEWLINE).op(PREV_HISTORY)); + assertBuffer("XXXtest line 4", b = b.op(NEWLINE).op(PREV_HISTORY)); + assertBuffer("XXXtest line 4", b = b.op(NEWLINE).op(PREV_HISTORY)); + } +} diff --git a/src/jline/src/test/java/jline/console/history/MemoryHistoryTest.java b/src/jline/src/test/java/jline/console/history/MemoryHistoryTest.java new file mode 100644 index 0000000000..6e5c731a80 --- /dev/null +++ b/src/jline/src/test/java/jline/console/history/MemoryHistoryTest.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2010 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package jline.console.history; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import static junit.framework.Assert.*; + +/** + * Tests for {@link MemoryHistory}. + * + * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> + */ +public class MemoryHistoryTest +{ + private MemoryHistory history; + + @Before + public void setUp() { + history = new MemoryHistory(); + } + + @After + public void tearDown() { + history = null; + } + + @Test + public void testAdd() { + assertEquals(0, history.size()); + + history.add("test"); + + assertEquals(1, history.size()); + assertEquals("test", history.get(0)); + assertEquals(1, history.index()); + } + + private void assertHistoryContains(final int offset, final String... items) { + assertEquals(items.length, history.size()); + int i=0; + for (History.Entry entry : history) { + assertEquals(offset + i, entry.index()); + assertEquals(items[i++], entry.value()); + } + } + + @Test + public void testOffset() { + history.setMaxSize(5); + + assertEquals(0, history.size()); + assertEquals(0, history.index()); + + history.add("a"); + history.add("b"); + history.add("c"); + history.add("d"); + history.add("e"); + + assertEquals(5, history.size()); + assertEquals(5, history.index()); + assertHistoryContains(0, "a", "b", "c", "d", "e"); + + history.add("f"); + + assertEquals(5, history.size()); + assertEquals(6, history.index()); + + assertHistoryContains(1, "b", "c", "d", "e", "f"); + assertEquals("f", history.get(5)); + } + + @Test + public void testReplace() { + assertEquals(0, history.size()); + + history.add("a"); + history.add("b"); + history.replace("c"); + + assertHistoryContains(0, "a", "c"); + } +}
\ No newline at end of file diff --git a/src/jline/src/test/java/jline/example/Example.java b/src/jline/src/test/java/jline/example/Example.java index 80a8d99e33..0da3799cd3 100644 --- a/src/jline/src/test/java/jline/example/Example.java +++ b/src/jline/src/test/java/jline/example/Example.java @@ -6,33 +6,34 @@ */ package jline.example; -import jline.*; +import jline.console.completer.*; +import jline.console.ConsoleReader; import java.io.*; import java.util.*; -import java.util.zip.*; -public class Example { +public class Example +{ public static void usage() { System.out.println("Usage: java " + Example.class.getName() - + " [none/simple/files/dictionary [trigger mask]]"); + + " [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\""); + + "\"foo\", \"bar\", and \"baz\""); System.out - .println(" files - a completor that comples " + "file names"); + .println(" files - a completor that comples " + "file names"); System.out.println(" dictionary - a completor that comples " - + "english dictionary words"); + + "english dictionary words"); System.out.println(" classes - a completor that comples " - + "java class names"); + + "java class names"); System.out - .println(" trigger - a special word which causes it to assume " - + "the next line is a password"); + .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"); + + "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."); + + "will use the simple compleator with 'su' triggering\n" + + "the use of '*' as a password mask."); } public static void main(String[] args) throws IOException { @@ -40,8 +41,8 @@ public class Example { 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(); @@ -49,21 +50,18 @@ public class Example { return; } - List<Completor> completors = new LinkedList<Completor>(); + List<Completer> completors = new LinkedList<Completer>(); 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 { + } + else if (args[0].equals("files")) { + completors.add(new FileNameCompleter()); + } + else if (args[0].equals("simple")) { + completors.add(new StringsCompleter("foo", "bar", "baz")); + } + else { usage(); return; @@ -71,11 +69,13 @@ public class Example { } if (args.length == 3) { - mask = new Character(args[2].charAt(0)); + mask = args[2].charAt(0); trigger = args[1]; } - reader.addCompletor(new ArgumentCompletor(completors)); + for (Completer c : completors) { + reader.addCompleter(c); + } String line; PrintWriter out = new PrintWriter(System.out); diff --git a/src/jline/src/test/java/jline/example/PasswordReader.java b/src/jline/src/test/java/jline/example/PasswordReader.java deleted file mode 100644 index 133185540a..0000000000 --- a/src/jline/src/test/java/jline/example/PasswordReader.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - */ -package 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/jline/src/test/java/jline/internal/TerminalLineSettingsTest.java b/src/jline/src/test/java/jline/internal/TerminalLineSettingsTest.java new file mode 100644 index 0000000000..80b7f0be0b --- /dev/null +++ b/src/jline/src/test/java/jline/internal/TerminalLineSettingsTest.java @@ -0,0 +1,146 @@ +package jline.internal; + +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * Tests for the {@link TerminalLineSettings}. + * + * @author <a href="mailto:jbonofre@apache.org">Jean-Baptiste Onofré</a> + */ +public class TerminalLineSettingsTest +{ + private TerminalLineSettings settings; + + private final String linuxSttySample = "speed 38400 baud; rows 85; columns 244; line = 0;\n" + + "intr = ^C; quit = ^\\; erase = ^?; kill = ^U; eof = ^D; eol = M-^?; eol2 = M-^?; swtch = M-^?; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0;\n" + + "-parenb -parodd cs8 hupcl -cstopb cread -clocal -crtscts\n" + + "-ignbrk brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc ixany imaxbel iutf8\n" + + "opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0\n" + + "isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke"; + + private final String solarisSttySample = "speed 38400 baud; \n" + + "rows = 85; columns = 244; ypixels = 0; xpixels = 0;\n" + + "csdata ?\n" + + "eucw 1:0:0:0, scrw 1:0:0:0\n" + + "intr = ^c; quit = ^\\; erase = ^?; kill = ^u;\n" + + "eof = ^d; eol = -^?; eol2 = -^?; swtch = <undef>;\n" + + "start = ^q; stop = ^s; susp = ^z; dsusp = ^y;\n" + + "rprnt = ^r; flush = ^o; werase = ^w; lnext = ^v;\n" + + "-parenb -parodd cs8 -cstopb -hupcl cread -clocal -loblk -crtscts -crtsxoff -parext \n" + + "-ignbrk brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl -iuclc \n" + + "ixon ixany -ixoff imaxbel \n" + + "isig icanon -xcase echo echoe echok -echonl -noflsh \n" + + "-tostop echoctl -echoprt echoke -defecho -flusho -pendin iexten \n" + + "opost -olcuc onlcr -ocrnl -onocr -onlret -ofill -ofdel tab3"; + + private final String aixSttySample = "speed 38400 baud; 85 rows; 244 columns;\n" + + "eucw 1:1:0:0, scrw 1:1:0:0:\n" + + "intr = ^C; quit = ^\\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>\n" + + "eol2 = <undef>; start = ^Q; stop = ^S; susp = ^Z; dsusp = ^Y; reprint = ^R\n" + + "discard = ^O; werase = ^W; lnext = ^V\n" + + "-parenb -parodd cs8 -cstopb -hupcl cread -clocal -parext \n" + + "-ignbrk brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl -iuclc \n" + + "ixon ixany -ixoff imaxbel \n" + + "isig icanon -xcase echo echoe echok -echonl -noflsh \n" + + "-tostop echoctl -echoprt echoke -flusho -pending iexten \n" + + "opost -olcuc onlcr -ocrnl -onocr -onlret -ofill -ofdel tab3"; + + private final String macOsSttySample = "speed 9600 baud; 47 rows; 155 columns;\n" + + "lflags: icanon isig iexten echo echoe -echok echoke -echonl echoctl\n" + + "-echoprt -altwerase -noflsh -tostop -flusho pendin -nokerninfo\n" + + "-extproc\n" + + "iflags: -istrip icrnl -inlcr -igncr ixon -ixoff ixany imaxbel iutf8\n" + + "-ignbrk brkint -inpck -ignpar -parmrk\n" + + "oflags: opost onlcr -oxtabs -onocr -onlret\n" + + "cflags: cread cs8 -parenb -parodd hupcl -clocal -cstopb -crtscts -dsrflow\n" + + "-dtrflow -mdmbuf\n" + + "cchars: discard = ^O; dsusp = ^Y; eof = ^D; eol = <undef>;\n" + + "eol2 = <undef>; erase = ^?; intr = ^C; kill = ^U; lnext = ^V;\n" + + "min = 1; quit = ^\\; reprint = ^R; start = ^Q; status = ^T;\n" + + "stop = ^S; susp = ^Z; time = 0; werase = ^W;"; + + private final String netBsdSttySample = "speed 38400 baud; 85 rows; 244 columns;\n" + + "lflags: icanon isig iexten echo echoe echok echoke -echonl echoctl\n" + + " -echoprt -altwerase -noflsh -tostop -flusho pendin -nokerninfo\n" + + " -extproc\n" + + "iflags: -istrip icrnl -inlcr -igncr ixon -ixoff ixany imaxbel -ignbrk\n" + + " brkint -inpck -ignpar -parmrk\n" + + "oflags: opost onlcr -ocrnl oxtabs onocr onlret\n" + + "cflags: cread cs8 -parenb -parodd hupcl -clocal -cstopb -crtscts -mdmbuf\n" + + " -cdtrcts\n" + + "cchars: discard = ^O; dsusp = ^Y; eof = ^D; eol = <undef>;\n" + + " eol2 = <undef>; erase = ^?; intr = ^C; kill = ^U; lnext = ^V;\n" + + " min = 1; quit = ^\\; reprint = ^R; start = ^Q; status = ^T;\n" + + " stop = ^S; susp = ^Z; time = 0; werase = ^W;"; + + private final String freeBsdSttySample = "speed 9600 baud; 32 rows; 199 columns;\n" + + "lflags: icanon isig iexten echo echoe echok echoke -echonl echoctl\n" + + " -echoprt -altwerase -noflsh -tostop -flusho -pendin -nokerninfo\n" + + " -extproc\n" + + "iflags: -istrip icrnl -inlcr -igncr ixon -ixoff ixany imaxbel -ignbrk\n" + + " brkint -inpck -ignpar -parmrk\n" + + "oflags: opost onlcr -ocrnl tab0 -onocr -onlret\n" + + "cflags: cread cs8 -parenb -parodd hupcl -clocal -cstopb -crtscts -dsrflow\n" + + " -dtrflow -mdmbuf\n" + + "cchars: discard = ^O; dsusp = ^Y; eof = ^D; eol = <undef>;\n" + + " eol2 = <undef>; erase = ^?; erase2 = ^H; intr = ^C; kill = ^U;\n" + + " lnext = ^V; min = 1; quit = ^\\; reprint = ^R; start = ^Q;\n" + + " status = ^T; stop = ^S; susp = ^Z; time = 0; werase = ^W;"; + + @Before + public void setUp() throws Exception { + settings = new TerminalLineSettings(); + } + + @Test + public void testGetConfig() { + String config = settings.getConfig(); + System.out.println(config); + } + + @Test + public void testLinuxSttyParsing() { + assertEquals(0x7f, settings.getProperty("erase", linuxSttySample)); + assertEquals(244, settings.getProperty("columns", linuxSttySample)); + assertEquals(85, settings.getProperty("rows", linuxSttySample)); + } + + @Test + public void testSolarisSttyParsing() { + assertEquals(0x7f, settings.getProperty("erase", solarisSttySample)); + assertEquals(244, settings.getProperty("columns", solarisSttySample)); + assertEquals(85, settings.getProperty("rows", solarisSttySample)); + } + + @Test + public void testAixSttyParsing() { + assertEquals(0x7f, settings.getProperty("erase", aixSttySample)); + assertEquals(244, settings.getProperty("columns", aixSttySample)); + assertEquals(85, settings.getProperty("rows", aixSttySample)); + } + + @Test + public void testMacOsSttyParsing() { + assertEquals(0x7f, settings.getProperty("erase", macOsSttySample)); + assertEquals(155, settings.getProperty("columns", macOsSttySample)); + assertEquals(47, settings.getProperty("rows", macOsSttySample)); + } + + @Test + public void testNetBsdSttyParsing() { + assertEquals(0x7f, settings.getProperty("erase", netBsdSttySample)); + assertEquals(244, settings.getProperty("columns", netBsdSttySample)); + assertEquals(85, settings.getProperty("rows", netBsdSttySample)); + } + + @Test + public void testFreeBsdSttyParsing() { + assertEquals(0x7f, settings.getProperty("erase", freeBsdSttySample)); + assertEquals(199, settings.getProperty("columns", freeBsdSttySample)); + assertEquals(32, settings.getProperty("rows", freeBsdSttySample)); + } + +}
\ No newline at end of file diff --git a/src/jline/src/test/resources/jline/example/english.gz b/src/jline/src/test/resources/jline/example/english.gz Binary files differdeleted file mode 100644 index f0a85c08d9..0000000000 --- a/src/jline/src/test/resources/jline/example/english.gz +++ /dev/null |