Skip to content

Commit

Permalink
Fix for AESH-433. API for cursor events
Browse files Browse the repository at this point in the history
  • Loading branch information
jfdenise committed Apr 24, 2017
1 parent 4a7bd9b commit 45bb337
Show file tree
Hide file tree
Showing 10 changed files with 771 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.aesh.readline;

import org.aesh.readline.cursor.Line;
import org.aesh.readline.cursor.CursorListener;
import org.aesh.readline.history.InMemoryHistory;
import org.aesh.readline.paste.PasteManager;
import org.aesh.readline.undo.UndoAction;
Expand Down Expand Up @@ -34,12 +36,13 @@ public class AeshConsoleBuffer implements ConsoleBuffer {
private final boolean ansiMode;

private static final Logger LOGGER = LoggerUtil.getLogger(AeshConsoleBuffer.class.getName());
private final CursorListener cursorListener;

public AeshConsoleBuffer(Connection connection, Prompt prompt,
EditMode editMode, History history,
CompletionHandler completionHandler,
Size size,
boolean ansi) {
boolean ansi, CursorListener listener) {
this.connection = connection;
this.ansiMode = ansi;
this.buffer = new Buffer(prompt);
Expand All @@ -58,6 +61,7 @@ public AeshConsoleBuffer(Connection connection, Prompt prompt,
this.size = size;

this.editMode = editMode;
this.cursorListener = listener;
}
@Override
public History history() {
Expand Down Expand Up @@ -104,6 +108,9 @@ public PasteManager pasteManager() {
public void moveCursor(int where) {
buffer.move(connection.stdoutHandler(), where,
size().getWidth(), isViMode());
if (cursorListener != null) {
cursorListener.moved(new Line(buffer, connection, size.getWidth()));
}
}

@Override
Expand Down
34 changes: 31 additions & 3 deletions readline/src/main/java/org/aesh/readline/Buffer.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import java.util.Arrays;
import java.util.function.Consumer;
import java.util.logging.Logger;
import org.aesh.readline.cursor.CursorLocator;

/**
* Buffer to keep track of text and cursor position in the console.
Expand All @@ -54,10 +55,12 @@ public class Buffer {
private boolean isPromptDisplayed = false;
private boolean deletingBackward = true;

private final CursorLocator locator;

Buffer() {
line = new int[1024];
prompt = new Prompt("");
locator = new CursorLocator(this);
}

Buffer(Prompt prompt) {
Expand All @@ -66,13 +69,19 @@ public class Buffer {
this.prompt = prompt;
else
this.prompt = new Prompt("");
locator = new CursorLocator(this);
}

public Buffer(Buffer buf) {
line = buf.line.clone();
cursor = buf.cursor;
size = buf.size;
prompt = buf.prompt.copy();
locator = new CursorLocator(this);
}

public CursorLocator getCursorLocator() {
return locator;
}

public int get(int pos) {
Expand Down Expand Up @@ -113,6 +122,7 @@ public void reset() {
isPromptDisplayed = false;
if(multiLine)
multiLineBuffer = new int[0];
locator.clear();
}

public void setIsPromptDisplayed(boolean isPromptDisplayed) {
Expand Down Expand Up @@ -160,16 +170,31 @@ public void setMultiLine(boolean multi) {
multiLine = multi;
}

/**
* Some completion occured, do not try to compute character index location.
* This could be revisited to implement a strategy.
*/
public void invalidateCursorLocation() {
if (isMultiLine()) {
locator.invalidateCursorLocation();
}
}

public void updateMultiLineBuffer() {
int originalSize = multiLineBuffer.length;
if(lineEndsWithBackslash()) {
// Store the size of each line.
int cmdSize;
if (lineEndsWithBackslash()) {
cmdSize = size - 1;
multiLineBuffer = Arrays.copyOf(multiLineBuffer, originalSize + size-1);
System.arraycopy(line, 0, multiLineBuffer, originalSize, size-1);
}
else {
cmdSize = size;
multiLineBuffer = Arrays.copyOf(multiLineBuffer, originalSize + size);
System.arraycopy(line, 0, multiLineBuffer, originalSize, size);
}
locator.addLine(cmdSize, prompt.getLength());
clear();
prompt = new Prompt("> ");
cursor = 0;
Expand Down Expand Up @@ -341,7 +366,7 @@ private int[] syncCursorWhenBufferIsAtTerminalEdge(int currentPos, int newPos, i
return builder.toArray();
}

private int[] moveNumberOfColumns(int column, char direction) {
public int[] moveNumberOfColumns(int column, char direction) {
if(column < 10) {
int[] out = new int[4];
out[0] = 27; // esc
Expand Down Expand Up @@ -732,14 +757,17 @@ public void delete(Consumer<int[]> out, int delta, int width, boolean viMode) {
deletingBackward = false;
}
else if (delta < 0) {
delta = - Math.min(- delta, cursor);
delta = -Math.min(-delta, cursor);
System.arraycopy(line, cursor, line, cursor + delta, size - cursor);
size += delta;
cursor += delta;
this.delta =+ delta;
deletingBackward = true;
}

// Erase the remaining.
Arrays.fill(line, size, line.length, 0);

if(viMode) {
//if(!deletingBackward)
// cursor--;
Expand Down
11 changes: 6 additions & 5 deletions readline/src/main/java/org/aesh/readline/Readline.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
*/
package org.aesh.readline;

import org.aesh.readline.cursor.CursorListener;
import org.aesh.readline.action.Action;
import org.aesh.readline.action.ActionDecoder;
import org.aesh.readline.action.KeyAction;
Expand Down Expand Up @@ -118,18 +119,18 @@ public void readline(Connection conn, Prompt prompt, Consumer<String> requestHan
public void readline(Connection conn, Prompt prompt, Consumer<String> requestHandler,
List<Completion> completions,
List<Function<String,Optional<String>>> preProcessors ) {
readline(conn, prompt, requestHandler, completions, preProcessors, null);
readline(conn, prompt, requestHandler, completions, preProcessors, null, null);
}

public void readline(Connection conn, Prompt prompt, Consumer<String> requestHandler,
List<Completion> completions,
List<Function<String,Optional<String>>> preProcessors,
History history) {
History history, CursorListener listener) {
synchronized(this) {
if (inputProcessor != null) {
throw new IllegalStateException("Already reading a line");
}
inputProcessor = new AeshInputProcessor(conn, prompt, requestHandler, completions, preProcessors, history);
inputProcessor = new AeshInputProcessor(conn, prompt, requestHandler, completions, preProcessors, history, listener);
}
inputProcessor.start();
processInput();
Expand Down Expand Up @@ -170,15 +171,15 @@ private AeshInputProcessor(
Consumer<String> requestHandler,
List<Completion> completions,
List<Function<String,Optional<String>>> preProcessors,
History newHistory) {
History newHistory, CursorListener listener) {

completionHandler.clear();
completionHandler.addCompletions(completions);
consoleBuffer =
new AeshConsoleBuffer(conn, prompt, editMode,
//use newHistory if its not null
newHistory != null ? newHistory : history,
completionHandler, size, true);
completionHandler, size, true, listener);

this.conn = conn;
this.requestHandler = requestHandler;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ private void displayCompletions(List<TerminalString> completions, Buffer buffer,
inputProcessor.buffer().size().getHeight(), inputProcessor.buffer().size().getWidth()));

buffer.setIsPromptDisplayed(false);
buffer.invalidateCursorLocation();
inputProcessor.buffer().drawLine();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* JBoss, Home of Professional Open Source
* Copyright 2017 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @authors tag. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* 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 org.aesh.readline.cursor;

/**
*
* @author [email protected]
*/
public interface CursorListener {
void moved(Line line);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* JBoss, Home of Professional Open Source
* Copyright 2017 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @authors tag. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* 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 org.aesh.readline.cursor;

/**
*
* @author [email protected]
*/
public class CursorLocation {

private final int column;
private final int row;

public CursorLocation(int row, int column) {
this.row = row;
this.column = column;
}

/**
* @return the column
*/
public int getColumn() {
return column;
}

/**
* @return the row
*/
public int getRow() {
return row;
}
}
105 changes: 105 additions & 0 deletions readline/src/main/java/org/aesh/readline/cursor/CursorLocator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* JBoss, Home of Professional Open Source
* Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @authors tag. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* 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 org.aesh.readline.cursor;

import java.util.ArrayList;
import java.util.List;
import org.aesh.readline.Buffer;

/**
* Map a command character index onto a cursor COL/ROW.
*
* @author [email protected]
*/
public class CursorLocator {

private final List<Integer> linesSize = new ArrayList<>();
private boolean invalidatedLines;

private final Buffer buffer;

public CursorLocator(Buffer buffer) {
this.buffer = buffer;
}

public void addLine(int size, int promptSize) {
linesSize.add(size);
linesSize.add(promptSize);
}

public boolean isLocationInvalidated() {
return invalidatedLines;
}

public void invalidateCursorLocation() {
invalidatedLines = true;
}

/**
* The core logic of the locator. Map a command index onto an absolute
* COL/ROW cursor location.
*
* @param index The commnd index.
* @param width The terminal width.
* @return
*/
public CursorLocation locate(int index, int width) {
// Upper lines location has been lost.
if (isLocationInvalidated()) {
return null;
}
int cumulated = 0;

List<Integer> allLines = new ArrayList<>();
allLines.addAll(linesSize);
allLines.add(buffer.length());
allLines.add(buffer.prompt().getLength());
int lineIndex = 0;
for (int i = 0; i < allLines.size(); i++) {
int cmdSize = allLines.get(i++);
int promptSize = allLines.get(i);
lineIndex += 1;
if (cumulated + cmdSize > index) {
int part = index - cumulated;
int col = (part + promptSize) % width;
// if the part + prompt is longer than width, then
// the row is in a lower line.
lineIndex += (promptSize + part) / width;
return new CursorLocation(lineIndex - 1, col);
}
cumulated += cmdSize;
// Each line could be wrapped if longer than width.
lineIndex += (cmdSize + promptSize) / width;
}
// we are on the last line at the last character.
if (cumulated == index) {
int cmdSize = allLines.get(allLines.size() - 2);
int promptSize = allLines.get(allLines.size() - 1);
int col = (cmdSize + promptSize) % width;
return new CursorLocation(lineIndex - 1, col);
} else {
return null;
}
}

public void clear() {
linesSize.clear();
}
}
Loading

0 comments on commit 45bb337

Please sign in to comment.