Skip to content

Commit

Permalink
Merge branch 'cursor-move' of https://github.com/romge/jansi into rom…
Browse files Browse the repository at this point in the history
…ge-cursor-move

gnodet: Fixed conflict and added the missing junit-jupiter-params dependency
# Conflicts:
#	src/test/java/org/fusesource/jansi/AnsiTest.java
  • Loading branch information
gnodet committed Jan 20, 2021
2 parents 0a06ceb + f8fa335 commit f057957
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 16 deletions.
8 changes: 7 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,13 @@
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<artifactId>junit-jupiter</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>
Expand Down
96 changes: 81 additions & 15 deletions src/main/java/org/fusesource/jansi/Ansi.java
Original file line number Diff line number Diff line change
Expand Up @@ -554,52 +554,118 @@ public Ansi a(Attribute attribute) {
}

/**
* Moves the cursor to row n, column m.
* The values are 1-based, and default to 1 (top left corner) if omitted.
* A sequence such as CSI ;5H is a synonym for CSI 1;5H as well as CSI 17;H is the same as CSI 17H and CSI 17;1H
* Moves the cursor to row n, column m. The values are 1-based.
* Any values less than 1 are mapped to 1.
*
* @param row row (1-based) from top
* @param row row (1-based) from top
* @param column column (1 based) from left
* @return Ansi
* @return this Ansi instance
*/
public Ansi cursor(final int row, final int column) {
return appendEscapeSequence('H', row, column);
return appendEscapeSequence('H', Math.max(1, row), Math.max(1, column));
}

/**
* Moves the cursor to column n. The parameter n is 1-based.
* If n is less than 1 it is moved to the first column.
*
* @param x the index (1-based) of the column to move to
* @return this Ansi instance
*/
public Ansi cursorToColumn(final int x) {
return x >= 0 ? appendEscapeSequence('G', x) : this;
return appendEscapeSequence('G', Math.max(1, x));
}

/**
* Moves the cursor up. If the parameter y is negative it moves the cursor down.
*
* @param y the number of lines to move up
* @return this Ansi instance
*/
public Ansi cursorUp(final int y) {
return appendEscapeSequence('A', y);
return y > 0 ? appendEscapeSequence('A', y) : y < 0 ? cursorDown(-y) : this;
}

/**
* Moves the cursor down. If the parameter y is negative it moves the cursor up.
*
* @param y the number of lines to move down
* @return this Ansi instance
*/
public Ansi cursorDown(final int y) {
return y > 0 ? appendEscapeSequence('B', y) : this;
return y > 0 ? appendEscapeSequence('B', y) : y < 0 ? cursorUp(-y) : this;
}

/**
* Moves the cursor right. If the parameter x is negative it moves the cursor left.
*
* @param x the number of characters to move right
* @return this Ansi instance
*/
public Ansi cursorRight(final int x) {
return x > 0 ? appendEscapeSequence('C', x) : this;
return x > 0 ? appendEscapeSequence('C', x) : x < 0 ? cursorLeft(-x) : this;
}

/**
* Moves the cursor left. If the parameter x is negative it moves the cursor right.
*
* @param x the number of characters to move left
* @return this Ansi instance
*/
public Ansi cursorLeft(final int x) {
return x > 0 ? appendEscapeSequence('D', x) : this;
return x > 0 ? appendEscapeSequence('D', x) : x < 0 ? cursorRight(-x) : this;
}

/**
* Moves the cursor relative to the current position. The cursor is moved right if x is
* positive, left if negative and down if y is positive and up if negative.
*
* @param x the number of characters to move horizontally
* @param y the number of lines to move vertically
* @return this Ansi instance
*/
public Ansi cursorMove(final int x, final int y) {
return cursorRight(x).cursorDown(y);
}

/**
* Moves the cursor to the beginning of the line below.
*
* @return this Ansi instance
*/
public Ansi cursorDownLine() {
return appendEscapeSequence('E');
}

/**
* Moves the cursor to the beginning of the n-th line below. If the parameter n is negative it
* moves the cursor to the beginning of the n-th line above.
*
* @param n the number of lines to move the cursor
* @return this Ansi instance
*/
public Ansi cursorDownLine(final int n) {
return n > 0 ? appendEscapeSequence('E', n) : this;
return n < 0 ? cursorUpLine(-n) : appendEscapeSequence('E', n);
}

/**
* Moves the cursor to the beginning of the line above.
*
* @return this Ansi instance
*/
public Ansi cursorUpLine() {
return appendEscapeSequence('F');
}

/**
* Moves the cursor to the beginning of the n-th line above. If the parameter n is negative it
* moves the cursor to the beginning of the n-th line below.
*
* @param n the number of lines to move the cursor
* @return this Ansi instance
*/
public Ansi cursorUpLine(final int n) {
return n > 0 ? appendEscapeSequence('F', n) : this;
return n < 0 ? cursorDownLine(-n) : appendEscapeSequence('F', n);
}

public Ansi eraseScreen() {
Expand All @@ -619,11 +685,11 @@ public Ansi eraseLine(final Erase kind) {
}

public Ansi scrollUp(final int rows) {
return rows > 0 ? appendEscapeSequence('S', rows) : this;
return rows > 0 ? appendEscapeSequence('S', rows) : rows < 0 ? scrollDown(-rows) : this;
}

public Ansi scrollDown(final int rows) {
return rows > 0 ? appendEscapeSequence('T', rows) : this;
return rows > 0 ? appendEscapeSequence('T', rows) : rows < 0 ? scrollUp(-rows) : this;
}

public Ansi saveCursorPosition() {
Expand Down
81 changes: 81 additions & 0 deletions src/test/java/org/fusesource/jansi/AnsiTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

import org.fusesource.jansi.Ansi.Color;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;

import static org.junit.jupiter.api.Assertions.assertEquals;

Expand Down Expand Up @@ -61,4 +63,83 @@ public void apply(Ansi ansi) {
}
}).toString());
}

@ParameterizedTest
@CsvSource({
"-1,-1,ESC[1;1H", "-1,0,ESC[1;1H", "-1,1,ESC[1;1H", "-1,2,ESC[1;2H",
"0,-1,ESC[1;1H", "0,0,ESC[1;1H", "0,1,ESC[1;1H", "0,2,ESC[1;2H",
"1,-1,ESC[1;1H", "1,0,ESC[1;1H", "1,1,ESC[1;1H", "1,2,ESC[1;2H",
"2,-1,ESC[2;1H", "2,0,ESC[2;1H", "2,1,ESC[2;1H", "2,2,ESC[2;2H"
})
public void testCursor(int x, int y, String expected) {
assertAnsi(expected, new Ansi().cursor(x, y));
}

@ParameterizedTest
@CsvSource({"-1,ESC[1G", "0,ESC[1G", "1,ESC[1G", "2,ESC[2G"})
public void testCursorToColumn(int x, String expected) {
assertAnsi(expected, new Ansi().cursorToColumn(x));
}

@ParameterizedTest
@CsvSource({"-2,ESC[2B", "-1,ESC[1B", "0,''", "1,ESC[1A", "2,ESC[2A"})
public void testCursorUp(int y, String expected) {
assertAnsi(expected, new Ansi().cursorUp(y));
}

@ParameterizedTest
@CsvSource({"-2,ESC[2A", "-1,ESC[1A", "0,''", "1,ESC[1B", "2,ESC[2B"})
public void testCursorDown(int y, String expected) {
assertAnsi(expected, new Ansi().cursorDown(y));
}

@ParameterizedTest
@CsvSource({"-2,ESC[2D", "-1,ESC[1D", "0,''", "1,ESC[1C", "2,ESC[2C"})
public void testCursorRight(int x, String expected) {
assertAnsi(expected, new Ansi().cursorRight(x));
}

@ParameterizedTest
@CsvSource({"-2,ESC[2C", "-1,ESC[1C", "0,''", "1,ESC[1D", "2,ESC[2D"})
public void testCursorLeft(int x, String expected) {
assertAnsi(expected, new Ansi().cursorLeft(x));
}

@ParameterizedTest
@CsvSource({
"-2,-2,ESC[2DESC[2A", "-2,-1,ESC[2DESC[1A", "-2,0,ESC[2D", "-2,1,ESC[2DESC[1B", "-2,2,ESC[2DESC[2B",
"-1,-2,ESC[1DESC[2A", "-1,-1,ESC[1DESC[1A", "-1,0,ESC[1D", "-1,1,ESC[1DESC[1B", "-1,2,ESC[1DESC[2B",
"0,-2,ESC[2A", "0,-1,ESC[1A", "0,0,''", "0,1,ESC[1B", "0,2,ESC[2B",
"1,-2,ESC[1CESC[2A", "1,-1,ESC[1CESC[1A", "1,0,ESC[1C", "1,1,ESC[1CESC[1B", "1,2,ESC[1CESC[2B",
"2,-2,ESC[2CESC[2A", "2,-1,ESC[2CESC[1A", "2,0,ESC[2C", "2,1,ESC[2CESC[1B", "2,2,ESC[2CESC[2B"
})
public void testCursorMove(int x, int y, String expected) {
assertAnsi(expected, new Ansi().cursorMove(x, y));
}

@Test
public void testCursorDownLine() {
assertAnsi("ESC[E", new Ansi().cursorDownLine());
}

@ParameterizedTest
@CsvSource({"-2,ESC[2F", "-1,ESC[1F", "0,ESC[0E", "1,ESC[1E", "2,ESC[2E"})
public void testCursorDownLine(int n, String expected) {
assertAnsi(expected, new Ansi().cursorDownLine(n));
}

@Test
public void testCursorUpLine() {
assertAnsi("ESC[F", new Ansi().cursorUpLine());
}

@ParameterizedTest
@CsvSource({"-2,ESC[2E", "-1,ESC[1E", "0,ESC[0F", "1,ESC[1F", "2,ESC[2F"})
public void testCursorUpLine(int n, String expected) {
assertAnsi(expected, new Ansi().cursorUpLine(n));
}

private static void assertAnsi(String expected, Ansi actual) {
assertEquals(expected.replace("ESC", "\033"), actual.toString());
}
}

0 comments on commit f057957

Please sign in to comment.