org.crsh.console.EditorAction Maven / Gradle / Ivy
The newest version!
/*
* Copyright (C) 2012 eXo Platform SAS.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.crsh.console;
import org.crsh.cli.impl.Delimiter;
import org.crsh.cli.impl.completion.CompletionMatch;
import org.crsh.cli.impl.line.LineParser;
import org.crsh.cli.impl.line.MultiLineVisitor;
import org.crsh.cli.spi.Completion;
import org.crsh.util.Utils;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* An action on the editor.
*/
class EditorAction {
static class InsertKey extends EditorAction {
private final int[] sequence;
public InsertKey(int[] sequence) {
this.sequence = sequence;
}
void perform(Editor editor, EditorBuffer buffer) throws IOException {
StringBuilder sb = new StringBuilder(sequence.length);
for (int c : sequence) {
sb.appendCodePoint(c);
}
buffer.append(sb);
}
}
static EditorAction COMPLETE = new EditorAction() {
@Override
String execute(Editor editor, EditorBuffer buffer, int[] sequence, boolean flush) throws IOException {
// Compute prefix
MultiLineVisitor visitor = new MultiLineVisitor();
LineParser parser = new LineParser(visitor);
List lines = buffer.getLines();
for (int i = 0;i < lines.size();i++) {
if (i > 0) {
parser.crlf();
}
parser.append(lines.get(i));
}
String prefix = visitor.getRaw();
// log.log(Level.FINE, "About to get completions for " + prefix);
CompletionMatch completion = editor.console.shell.complete(prefix);
// log.log(Level.FINE, "Completions for " + prefix + " are " + completions);
//
if (completion != null) {
Completion completions = completion.getValue();
//
Delimiter delimiter = completion.getDelimiter();
try {
// Try to find the greatest prefix among all the results
if (completions.getSize() == 0) {
// Do nothing
} else if (completions.getSize() == 1) {
Map.Entry entry = completions.iterator().next();
String insert = entry.getKey();
StringBuilder sb = new StringBuilder();
sb.append(delimiter.escape(insert));
if (entry.getValue()) {
sb.append(completion.getDelimiter().getValue());
}
buffer.append(sb);
editor.console.driver.flush();
} else {
String commonCompletion = Utils.findLongestCommonPrefix(completions.getValues());
// Format stuff
int width = editor.console.driver.getWidth();
//
String completionPrefix = completions.getPrefix();
// Get the max length
int max = 0;
for (String suffix : completions.getValues()) {
max = Math.max(max, completionPrefix.length() + suffix.length());
}
// Separator : use two whitespace like in BASH
max += 2;
//
StringBuilder sb = new StringBuilder().append('\n');
if (max < width) {
int columns = width / max;
int index = 0;
for (String suffix : completions.getValues()) {
sb.append(completionPrefix).append(suffix);
for (int l = completionPrefix.length() + suffix.length();l < max;l++) {
sb.append(' ');
}
if (++index >= columns) {
index = 0;
sb.append('\n');
}
}
if (index > 0) {
sb.append('\n');
}
} else {
for (Iterator i = completions.getValues().iterator();i.hasNext();) {
String suffix = i.next();
sb.append(commonCompletion).append(suffix);
if (i.hasNext()) {
sb.append('\n');
}
}
sb.append('\n');
}
// Add current buffer
int index = 0;
for (String line : lines) {
if (index == 0) {
String prompt = editor.console.shell.getPrompt();
sb.append(prompt == null ? "" : prompt);
} else {
sb.append("\n> ");
}
sb.append(line);
index++;
}
// Redraw everything
editor.console.driver.write(sb.toString());
// If we have common completion we append it now in the buffer
if (commonCompletion.length() > 0) {
buffer.append(delimiter.escape(commonCompletion));
}
// Flush
buffer.flush(true);
}
}
catch (IOException e) {
// log.log(Level.SEVERE, "Could not write completion", e);
}
}
//
return null;
}
};
static EditorAction INTERRUPT = new EditorAction() {
@Override
String execute(Editor editor, EditorBuffer buffer, int[] sequence, boolean flush) throws IOException {
editor.lineParser.reset();
buffer.reset();
editor.console.driver.writeCRLF();
String prompt = editor.console.shell.getPrompt();
if (prompt != null) {
editor.console.driver.write(prompt);
}
if (flush) {
editor.console.driver.flush();
}
return null;
}
};
static EditorAction EOF_MAYBE = new EditorAction() {
@Override
String execute(Editor editor, EditorBuffer buffer, int[] sequence, boolean flush) throws IOException {
if (editor.isEmpty()) {
editor.console.status = Console.CLOSING;
return null;
} else {
if (editor.console.getMode() == Mode.EMACS) {
return EditorAction.DELETE_PREV_CHAR.execute(editor, buffer, sequence, true);
} else {
return EditorAction.ENTER.execute(editor, buffer, sequence, true);
}
}
}
};
public abstract static class History extends EditorAction {
protected abstract int getNext(Editor editor);
@Override
void perform(Editor editor, EditorBuffer buffer) throws IOException {
int nextHistoryCursor = getNext(editor);
if (nextHistoryCursor >= -1 && nextHistoryCursor < editor.history.size()) {
String s = nextHistoryCursor == -1 ? editor.historyBuffer : editor.history.get(nextHistoryCursor);
while (buffer.moveRight()) {
// Do nothing
}
String t = buffer.replace(s);
if (editor.historyCursor == -1) {
editor.historyBuffer = t;
} else {
editor.history.set(editor.historyCursor, t);
}
editor.historyCursor = nextHistoryCursor;
}
}
}
static EditorAction HISTORY_FIRST = new History() {
@Override
protected int getNext(Editor editor) {
return editor.history.size() - 1;
}
};
static EditorAction HISTORY_LAST = new History() {
@Override
protected int getNext(Editor editor) {
return 0;
}
};
static EditorAction HISTORY_PREV = new History() {
@Override
protected int getNext(Editor editor) {
return editor.historyCursor + 1;
}
};
static EditorAction HISTORY_NEXT = new History() {
@Override
protected int getNext(Editor editor) {
return editor.historyCursor - 1;
}
};
static EditorAction LEFT = new EditorAction() {
@Override
void perform(Editor editor, EditorBuffer buffer) throws IOException {
buffer.moveLeft();
}
};
static EditorAction RIGHT = new EditorAction() {
@Override
void perform(Editor editor, EditorBuffer buffer) throws IOException {
if (buffer.getCursor() < editor.getCursorBound()) {
buffer.moveRight();
}
}
};
static EditorAction MOVE_BEGINNING = new EditorAction() {
@Override
void perform(Editor editor, EditorBuffer buffer) throws IOException {
int cursor = buffer.getCursor();
if (cursor > 0) {
buffer.moveLeftBy(cursor);
}
}
};
static class MovePrevWord extends EditorAction {
final boolean atBeginning /* otherwise at end */;
public MovePrevWord(boolean atBeginning) {
this.atBeginning = atBeginning;
}
@Override
void perform(Editor editor, EditorBuffer buffer) throws IOException {
int cursor = buffer.getCursor();
int pos = cursor;
while (pos > 0) {
char c = buffer.charAt(pos - 1);
if ((atBeginning && Character.isLetterOrDigit(c)) || (!atBeginning && !Character.isLetterOrDigit(c))) {
break;
} else {
pos--;
}
}
while (pos > 0) {
char c = buffer.charAt(pos - 1);
if ((atBeginning && !Character.isLetterOrDigit(c)) || (!atBeginning && Character.isLetterOrDigit(c))) {
break;
} else {
pos--;
}
}
if (pos < cursor) {
buffer.moveLeftBy(cursor - pos);
}
}
}
static EditorAction MOVE_PREV_WORD_AT_BEGINNING = new MovePrevWord(true);
static EditorAction MOVE_PREV_WORD_AT_END = new MovePrevWord(false);
static class MoveNextWord extends EditorAction {
final At at;
public MoveNextWord(At at) {
this.at = at;
}
@Override
void perform(Editor editor, EditorBuffer buffer) throws IOException {
int to = editor.getCursorBound();
int from = buffer.getCursor();
int pos = from;
while (true) {
int look = at == At.BEFORE_END ? pos + 1 : pos;
if (look < to) {
char c = buffer.charAt(look);
if ((at != At.BEGINNING && Character.isLetterOrDigit(c)) || (at == At.BEGINNING && !Character.isLetterOrDigit(c))) {
break;
} else {
pos++;
}
} else {
break;
}
}
while (true) {
int look = at == At.BEFORE_END ? pos + 1 : pos;
if (look < to) {
char c = buffer.charAt(look);
if ((at != At.BEGINNING && !Character.isLetterOrDigit(c)) || (at == At.BEGINNING && Character.isLetterOrDigit(c))) {
break;
} else {
pos++;
}
} else {
break;
}
}
if (pos > from) {
buffer.moveRightBy(pos - from);
}
}
}
static EditorAction MOVE_NEXT_WORD_AT_BEGINNING = new MoveNextWord(At.BEGINNING);
static EditorAction MOVE_NEXT_WORD_AFTER_END = new MoveNextWord(At.AFTER_END);
static EditorAction MOVE_NEXT_WORD_BEFORE_END = new MoveNextWord(At.BEFORE_END);
static EditorAction DELETE_PREV_WORD = new EditorAction() {
@Override
void perform(Editor editor, EditorBuffer buffer) throws IOException {
editor.killBuffer.setLength(0);
boolean chars = false;
while (true) {
int cursor = buffer.getCursor();
if (cursor > 0) {
if (buffer.charAt(cursor - 1) == ' ') {
if (!chars) {
editor.killBuffer.appendCodePoint(buffer.del());
} else {
break;
}
} else {
editor.killBuffer.appendCodePoint(buffer.del());
chars = true;
}
} else {
break;
}
}
editor.killBuffer.reverse();
}
};
static EditorAction DELETE_NEXT_WORD = new EditorAction() {
@Override
void perform(Editor editor, EditorBuffer buffer) throws IOException {
int count = 0;
boolean chars = false;
while (true) {
if (buffer.getCursor() < buffer.getSize()) {
char c = buffer.charAt(buffer.getCursor());
if (!Character.isLetterOrDigit(c)) {
if (!chars) {
count++;
buffer.moveRight();
} else {
break;
}
} else {
chars = true;
count++;
buffer.moveRight();
}
} else {
break;
}
}
editor.killBuffer.setLength(0);
while (count-- > 0) {
editor.killBuffer.appendCodePoint(buffer.del());
}
editor.killBuffer.reverse();
}
};
static EditorAction DELETE_UNTIL_NEXT_WORD = new EditorAction() {
@Override
void perform(Editor editor, EditorBuffer buffer) throws IOException {
int pos = buffer.getCursor();
EditorAction.MOVE_NEXT_WORD_AT_BEGINNING.perform(editor, buffer);
while (buffer.getCursor() > pos) {
buffer.del();
}
}
};
static EditorAction DELETE_END = new EditorAction() {
@Override
void perform(Editor editor, EditorBuffer buffer) throws IOException {
int count = 0;
while (buffer.moveRight()) {
count++;
}
editor.killBuffer.setLength(0);
while (count-- > 0) {
editor.killBuffer.appendCodePoint(buffer.del());
}
editor.killBuffer.reverse();
if (buffer.getCursor() > editor.getCursorBound()) {
buffer.moveLeft();
}
}
};
static EditorAction DELETE_BEGINNING = new EditorAction() {
@Override
void perform(Editor editor, EditorBuffer buffer) throws IOException {
editor.killBuffer.setLength(0);
while (editor.buffer.getCursor() > 0) {
editor.killBuffer.appendCodePoint(buffer.del());
}
editor.killBuffer.reverse();
}
};
static EditorAction UNIX_LINE_DISCARD = new EditorAction() {
@Override
void perform(Editor editor, EditorBuffer buffer) throws IOException {
// Not really efficient
if (buffer.getCursor() > 0) {
editor.killBuffer.setLength(0);
while (buffer.getCursor() > 0) {
int c = buffer.del();
editor.killBuffer.appendCodePoint(c);
}
editor.killBuffer.reverse();
}
}
};
static EditorAction DELETE_LINE = new EditorAction() {
@Override
String execute(Editor editor, EditorBuffer buffer, int[] sequence, boolean flush) throws IOException {
buffer.moveRightBy(buffer.getSize() - buffer.getCursor());
buffer.replace("");
return null;
}
};
static EditorAction PASTE_AFTER = new EditorAction() {
@Override
void perform(Editor editor, EditorBuffer buffer) throws IOException {
if (editor.killBuffer.length() > 0) {
for (int i = 0;i < editor.killBuffer.length();i++) {
char c = editor.killBuffer.charAt(i);
buffer.append(c);
}
}
}
};
static EditorAction MOVE_END = new EditorAction() {
@Override
void perform(Editor editor, EditorBuffer buffer) throws IOException {
int cursor = editor.getCursorBound() - buffer.getCursor();
if (cursor > 0) {
buffer.moveRightBy(cursor);
}
}
};
static abstract class Copy extends EditorAction {
protected abstract int getFrom(EditorBuffer buffer);
protected abstract int getTo(EditorBuffer buffer);
@Override
void perform(Editor editor, EditorBuffer buffer) throws IOException {
int from = getFrom(buffer);
int to = getTo(buffer);
editor.killBuffer.setLength(0);
for (int i = from;i < to;i++) {
editor.killBuffer.append(editor.buffer.charAt(i));
}
}
}
static EditorAction COPY = new Copy() {
@Override
protected int getFrom(EditorBuffer buffer) {
return 0;
}
@Override
protected int getTo(EditorBuffer buffer) {
return buffer.getSize();
}
};
static EditorAction COPY_END_OF_LINE = new Copy() {
@Override
protected int getFrom(EditorBuffer buffer) {
return buffer.getCursor();
}
@Override
protected int getTo(EditorBuffer buffer) {
return buffer.getSize();
}
};
static EditorAction COPY_BEGINNING_OF_LINE = new Copy() {
@Override
protected int getFrom(EditorBuffer buffer) {
return 0;
}
@Override
protected int getTo(EditorBuffer buffer) {
return buffer.getCursor();
}
};
static EditorAction COPY_NEXT_WORD = new EditorAction() {
@Override
void perform(Editor editor, EditorBuffer buffer) throws IOException {
int size = editor.buffer.getSize();
int cursor = editor.buffer.getCursor();
editor.killBuffer.setLength(0);
while (cursor < size && editor.buffer.charAt(cursor) != ' ') {
editor.killBuffer.append(editor.buffer.charAt(cursor++));
}
while (cursor < size && editor.buffer.charAt(cursor) == ' ') {
editor.killBuffer.append(editor.buffer.charAt(cursor++));
}
}
};
static EditorAction COPY_PREV_WORD = new EditorAction() {
@Override
void perform(Editor editor, EditorBuffer buffer) throws IOException {
int cursor = buffer.getCursor() - 1;
editor.killBuffer.setLength(0);
while (cursor > 0 && buffer.charAt(cursor) != ' ') {
editor.killBuffer.append(buffer.charAt(cursor--));
}
while (cursor > 0 && editor.buffer.charAt(cursor) == ' ') {
editor.killBuffer.append(buffer.charAt(cursor--));
}
editor.killBuffer.reverse();
}
};
static class ChangeChars extends EditorAction {
/** . */
public final int count;
/** . */
public final int c;
public ChangeChars(int count, int c) {
this.count = count;
this.c = c;
}
@Override
void perform(Editor editor, EditorBuffer buffer) throws IOException {
int a = Math.min(count, buffer.getSize() - buffer.getCursor());
while (a-- > 0) {
buffer.moveRight((char)c);
}
buffer.moveLeft();
}
}
static EditorAction DELETE_PREV_CHAR = new EditorAction() {
@Override
void perform(Editor editor, EditorBuffer buffer) throws IOException {
buffer.del();
}
};
static class DeleteNextChars extends EditorAction {
/** . */
public final int count;
public DeleteNextChars(int count) {
this.count = count;
}
@Override
void perform(Editor editor, EditorBuffer buffer) throws IOException {
int tmp = count;
while (tmp > 0 && buffer.moveRight()) {
tmp--;
}
while (tmp++ < count) {
buffer.del();
}
if (buffer.getCursor() > editor.getCursorBound()) {
buffer.moveLeft();
}
}
}
static EditorAction DELETE_NEXT_CHAR = ((EditorAction)new DeleteNextChars(1));
static EditorAction CHANGE_CASE = new EditorAction() {
@Override
void perform(Editor editor, EditorBuffer buffer) throws IOException {
if (buffer.getCursor() < buffer.getSize()) {
char c = buffer.charAt(buffer.getCursor());
if (Character.isUpperCase(c)) {
c = Character.toLowerCase(c);
}
else if (Character.isLowerCase(c)) {
c = Character.toUpperCase(c);
}
buffer.moveRight(c);
if (buffer.getCursor() > editor.getCursorBound()) {
buffer.moveLeft();
}
}
}
};
static EditorAction TRANSPOSE_CHARS = new EditorAction() {
@Override
void perform(Editor editor, EditorBuffer buffer) throws IOException {
if (buffer.getSize() > 2) {
int pos = buffer.getCursor();
if (pos > 0) {
if (pos < buffer.getSize()) {
if (buffer.moveLeft()) {
char a = buffer.charAt(pos - 1);
char b = buffer.charAt(pos);
buffer.moveRight(b); // Should be assertion
buffer.moveRight(a); // Should be assertion
// A bit not great : need to find a better way to do that...
if (editor.console.getMode() == Mode.VI_MOVE && buffer.getCursor() > editor.getCursorBound()) {
buffer.moveLeft();
}
}
} else {
if (buffer.moveLeft() && buffer.moveLeft()) {
char a = buffer.charAt(pos - 2);
char b = buffer.charAt(pos - 1);
buffer.moveRight(b); // Should be assertion
buffer.moveRight(a); // Should be assertion
}
}
}
}
}
};
static EditorAction INSERT_COMMENT = new EditorAction() {
@Override
String execute(Editor editor, EditorBuffer buffer, int[] sequence, boolean flush) throws IOException {
EditorAction.MOVE_BEGINNING.perform(editor, buffer);
buffer.append("#");
return EditorAction.ENTER.execute(editor, buffer, sequence, flush);
}
};
static EditorAction CLS = new EditorAction() {
@Override
void perform(Editor editor, EditorBuffer buffer) throws IOException {
editor.console.driver.cls();
StringBuilder sb = new StringBuilder();
int index = 0;
List lines = buffer.getLines();
for (String line : lines) {
if (index == 0) {
String prompt = editor.console.shell.getPrompt();
sb.append(prompt == null ? "" : prompt);
} else {
sb.append("\n> ");
}
sb.append(line);
index++;
}
editor.console.driver.write(sb.toString());
editor.console.driver.flush();
}
};
static EditorAction ENTER = new EditorAction() {
@Override
String execute(Editor editor, EditorBuffer buffer, int[] sequence, boolean flush) throws IOException {
editor.historyCursor = -1;
editor.historyBuffer = null;
String line = buffer.getLine();
editor.lineParser.append(line);
if (editor.console.getMode() == Mode.VI_MOVE) {
editor.console.setMode(Mode.VI_INSERT);
}
if (editor.lineParser.crlf()) {
editor.console.driver.writeCRLF();
editor.console.driver.flush();
String request = editor.visitor.getRaw();
if (request.length() > 0) {
editor.addToHistory(request);
}
return request;
} else {
buffer.append('\n');
editor.console.driver.write("> ");
if (flush) {
buffer.flush();
}
return null;
}
}
};
String execute(Editor editor, EditorBuffer buffer, int[] sequence, boolean flush) throws IOException {
perform(editor, buffer);
if (flush) {
buffer.flush();
}
return null;
}
void perform(Editor editor, EditorBuffer buffer) throws IOException {
throw new UnsupportedOperationException("Implement the edition logic");
}
public EditorAction then(final EditorAction action) {
return new EditorAction() {
@Override
String execute(Editor editor, EditorBuffer buffer, int[] sequence, boolean flush) throws IOException {
EditorAction.this.execute(editor, buffer, sequence, flush);
return action.execute(editor, buffer, sequence, flush);
}
};
}
public EditorAction repeat(final int count) {
return new EditorAction() {
@Override
void perform(Editor editor, EditorBuffer buffer) throws IOException {
for (int i = 0;i < count;i++) {
EditorAction.this.perform(editor, buffer);
}
}
};
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy