Package org.jboss.aesh.console

Source Code of org.jboss.aesh.console.AeshConsoleBuffer

/*
* Copyright 2012 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Eclipse Public License version 1.0, available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.jboss.aesh.console;

import org.jboss.aesh.edit.EditMode;
import org.jboss.aesh.edit.Mode;
import org.jboss.aesh.edit.PasteManager;
import org.jboss.aesh.edit.ViEditMode;
import org.jboss.aesh.edit.actions.Action;
import org.jboss.aesh.edit.actions.EditAction;
import org.jboss.aesh.parser.Parser;
import org.jboss.aesh.terminal.Shell;
import org.jboss.aesh.undo.UndoAction;
import org.jboss.aesh.undo.UndoManager;
import org.jboss.aesh.util.ANSI;
import org.jboss.aesh.util.LoggerUtil;

import java.io.IOException;
import java.io.PrintStream;
import java.util.logging.Logger;

/**
* @author <a href="mailto:stale.pedersen@jboss.org">Ståle W. Pedersen</a>
*/
public class AeshConsoleBuffer implements ConsoleBuffer {

    private EditMode editMode;
    private final PrintStream err;
    private final PrintStream out;

    private final Buffer buffer;
    private final Shell shell;

    private final UndoManager undoManager;
    private final PasteManager pasteManager;

    private Action currentAction = Action.EDIT;

    private final boolean isLogging = true;

    //used to optimize text deletion
    private static final char[] resetLineAndSetCursorToStart =
            (ANSI.saveCursor()+ANSI.getStart()+"0G"+ANSI.getStart()+"2K").toCharArray();

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

    AeshConsoleBuffer(Prompt prompt, Shell shell, EditMode editMode) {
        this.out = shell.out();
        this.err = shell.err();
        this.buffer = new Buffer(prompt);
        this.shell = shell;
        pasteManager = new PasteManager();
        undoManager = new UndoManager();
        this.editMode = editMode;

        LOGGER.info("prompt: "+this.buffer.getPrompt().getPromptAsString());
    }

    @Override
    public Buffer getBuffer() {
        return this.buffer;
    }

    @Override
    public PrintStream out() {
        return out;
    }

    @Override
    public PrintStream err() {
        return err;
    }

    @Override
    public void setEditMode(EditMode editMode) {
        this.editMode = editMode;
    }

    @Override
    public UndoManager getUndoManager() {
        return undoManager;
    }

    @Override
    public PasteManager getPasteManager() {
        return pasteManager;
    }

    @Override
    public EditMode getEditMode() {
        return editMode;
    }

    @Override
    public void moveCursor(int where) {
        if(editMode.getMode() == Mode.VI &&
                (editMode.getCurrentAction() == Action.MOVE ||
                        editMode.getCurrentAction() == Action.DELETE)) {
            out.print(buffer.move(where, shell.getSize().getWidth(), true));
        }
        else {
            out.print(buffer.move(where, shell.getSize().getWidth()));
        }
        out.flush();
    }

    @Override
    public void drawLine() {
        drawLine(true);
    }

    @Override
    public void drawLine(boolean keepCursorPosition) {
        if(isLogging)
            LOGGER.info("drawing: "+buffer.getPrompt().getPromptAsString() + buffer.getLine());
        //need to clear more than one line
        String line = buffer.getPrompt().getPromptAsString() + buffer.getLine();

        if(line.length() > shell.getSize().getWidth() ||
                (buffer.getDelta() < 0 && line.length()+ Math.abs(buffer.getDelta()) > shell.getSize().getWidth())) {
            if(buffer.getDelta() == -1 && buffer.getCursor() >= buffer.length() && Config.isOSPOSIXCompatible())
                redrawMultipleLinesBackspace();
            else
                redrawMultipleLines(keepCursorPosition);
        }
        // only clear the current line
        else {
            //most deletions are backspace from the end of the line so we've
            //optimize that like this.
            //NOTE: this doesnt work with history, need to find a better solution
            if(buffer.getDelta() == -1 && buffer.getCursor() >= buffer.length()
                    && currentAction != Action.HISTORY) {
                out.print(Parser.SPACE_CHAR + ANSI.getStart() + "1D"); //move cursor to left
            }
            else {
                //save cursor, move the cursor to the beginning, reset line
                if(keepCursorPosition)
                    out.print(resetLineAndSetCursorToStart);
                if(!buffer.isPromptDisabled())
                    displayPrompt();
                //write line and restore cursor
                if(keepCursorPosition)
                    out.print(buffer.getLine() + ANSI.restoreCursor());
                else {
                    out.print(buffer.getLine());
                    buffer.setCursor(buffer.getLine().length());
                }
            }
        }
        out.flush();
    }

    @Override
    public void updateCurrentAction(Action action) {
        this.currentAction = action;
    }

    private void redrawMultipleLines(boolean keepCursorPosition) {
        int currentRow = 0;
        if(buffer.getCursorWithPrompt() > 0)
            currentRow = buffer.getCursorWithPrompt() / shell.getSize().getWidth();
        if(currentRow > 0 && buffer.getCursorWithPrompt() % shell.getSize().getWidth() == 0)
            currentRow--;

        if(isLogging) {
            LOGGER.info("actual position: " + shell.getCursor());
            LOGGER.info("currentRow:" + currentRow + ", cursorWithPrompt:" + buffer.getCursorWithPrompt()
                    + ", width:" + shell.getSize().getWidth() + ", height:" + shell.getSize().getHeight()
                    + ", delta:" + buffer.getDelta() + ", buffer:" + buffer.getLine());
        }

        StringBuilder builder = new StringBuilder();

        if(keepCursorPosition)
            builder.append(ANSI.saveCursor()); //save cursor

        if(buffer.getDelta() > 0) {
            if (currentRow > 0)
                for (int i = 0; i < currentRow; i++)
                    builder.append(Buffer.printAnsi("A")); //move to top
        }
        else
            clearDelta(currentRow, builder);

        builder.append(Buffer.printAnsi("0G")); //clear

        if(!buffer.isPromptDisabled()) {
            if(buffer.getPrompt().hasANSI())
                builder.append(buffer.getPrompt().getANSI());
            else
                builder.append(buffer.getPrompt().getPromptAsString());
        }
        builder.append(buffer.getLine());
        if(buffer.getDelta() < 0)
            builder.append(Buffer.printAnsi("K"));

        // move cursor to saved pos
        if(keepCursorPosition) {
            builder.append(ANSI.restoreCursor());
            out.print(builder.toString());
        }
        else {
            out.print(builder.toString());
            buffer.setCursor(buffer.getLine().length());
        }
    }

    private void clearDelta(int currentRow, StringBuilder builder) {
        if(buffer.getDelta() < 0) {
            int currentLength = buffer.getLineWithPrompt().length();
            int numberOfCurrentRows =  currentLength /  shell.getSize().getWidth();
            int numberOfPrevRows =
                    (currentLength + (buffer.getDelta()*-1)) / shell.getSize().getWidth();
            int numberOfRowsToRemove = numberOfPrevRows - numberOfCurrentRows;
            int numberofRows = ((buffer.getDelta()*-1)+ buffer.getLineWithPrompt().length()) /
                    shell.getSize().getWidth();

            if (numberOfRowsToRemove == 0)
                numberOfRowsToRemove++;

            int tmpCurrentRow = currentRow;
            while(tmpCurrentRow < numberofRows) {
                builder.append(Buffer.printAnsi("B")); //move to the last row
                tmpCurrentRow++;
            }
            while(tmpCurrentRow > 0) {
                if(numberOfRowsToRemove > 0) {
                    builder.append(Buffer.printAnsi("2K"));
                    numberOfRowsToRemove--;
                }
                builder.append(Buffer.printAnsi("A"));
                tmpCurrentRow--;
            }
        }
    }

    private void redrawMultipleLinesBackspace() {
        out.print(Parser.SPACE_CHAR + ANSI.getStart() + "1D"); //move cursor to left
    }

    @Override
    public void syncCursor() {
        if(buffer.getCursor() != buffer.getLine().length()) {
            out.print(Buffer.printAnsi((
                    Math.abs(buffer.getCursor() -
                            buffer.getLine().length()) + "D")));
            out.flush();
        }
    }

    @Override
    public void replace(int rChar) {
        addActionToUndoStack();
        buffer.replaceChar((char) rChar);
        drawLine();
    }

    @Override
    public void changeCase() {
        if(buffer.changeCase()) {
            moveCursor(1);
            drawLine();
        }
    }

    @Override
    public void capitalizeWord() {
        String word = Parser.findWordClosestToCursor(buffer.getLineNoMask(), buffer.getCursor());
        if(word.length() > 0) {
            int pos = buffer.getLineNoMask().indexOf(word, buffer.getCursor()-word.length());
            if(pos < 0)
                pos = 0;
            buffer.replaceChar(Character.toUpperCase(buffer.getLineNoMask().charAt(pos)), pos);
            drawLine();
        }
    }

    @Override
    public void lowerCaseWord() {
        String word = Parser.findWordClosestToCursor(buffer.getLineNoMask(), buffer.getCursor());
        if(word.length() > 0) {
            int pos = buffer.getLineNoMask().indexOf(word, buffer.getCursor()-word.length());
            if(pos < 0)
                pos = 0;
            for(int i = 0; i < word.length(); i++) {
                buffer.replaceChar(Character.toLowerCase(buffer.getLineNoMask().charAt(pos+i)), pos+i);
            }
            drawLine();
        }
    }

    @Override
    public void upperCaseWord() {
        String word = Parser.findWordClosestToCursor(buffer.getLineNoMask(), buffer.getCursor());
        if(word.length() > 0) {
            int pos = buffer.getLineNoMask().indexOf(word, buffer.getCursor()-word.length());
            if(pos < 0)
                pos = 0;
            for(int i = 0; i < word.length(); i++) {
                buffer.replaceChar(Character.toUpperCase(buffer.getLineNoMask().charAt(pos+i)), pos+i);
            }
            drawLine();
        }
    }

    @Override
    public void writeChars(int[] chars) {
        for(int c : chars)
            writeChar((char) c);
    }

    @Override
    public void writeString(String input) {
        for(char c : input.toCharArray())
            writeChar(c);
    }

    @Override
    public void writeChar(char c) {

        buffer.write(c);
        //if mask is set and not set to 0 (nullvalue) we write out
        //the masked char. if masked is set to 0 we write nothing
        if(buffer.getPrompt().isMasking()) {
            if(buffer.getPrompt().getMask() != 0)
                out.print(buffer.getPrompt().getMask());
            else
                return;
        }
        else {
            out.print(c);
        }

        // add a 'fake' new line when inserting at the edge of terminal
        if(buffer.getCursorWithPrompt() > shell.getSize().getWidth() &&
                buffer.getCursorWithPrompt() % shell.getSize().getWidth() == 1) {
            out.print((char) 32);
            out.print((char) 13);
        }

        // if we insert somewhere other than the end of the line we need to redraw from cursor
        if(buffer.getCursor() < buffer.length()) {
            //check if we just started a new line, if we did we need to make sure that we add one
            if(buffer.totalLength() > shell.getSize().getWidth() &&
                    (buffer.totalLength()-1) % shell.getSize().getWidth() == 1) {
                int ansiCurrentRow = shell.getCursor().getRow();
                int currentRow = (buffer.getCursorWithPrompt() / shell.getSize().getWidth());
                if(currentRow > 0 && buffer.getCursorWithPrompt() % shell.getSize().getWidth() == 0)
                    currentRow--;

                int totalRows = buffer.totalLength() / shell.getSize().getWidth();
                if(totalRows > 0 && buffer.totalLength() % shell.getSize().getWidth() == 0)
                    totalRows--;

                if(ansiCurrentRow+(totalRows-currentRow) > shell.getSize().getHeight()) {
                    out.print(Buffer.printAnsi("1S")); //adding a line
                    out.print(Buffer.printAnsi("1A")); // moving up a line
                }
            }
            drawLine();
        }
        out.flush();
    }

    @Override
    public void displayPrompt() {
        displayPrompt(buffer.getPrompt());
    }

    @Override
    public void setPrompt(Prompt prompt) {
        if(!buffer.getPrompt().equals(prompt)) {
            buffer.updatePrompt(prompt);
            //only update the prompt if Console is running
            //set cursor position line.length
            displayPrompt(prompt);
            if(buffer.getLine().length() > 0) {
                out().print(buffer.getLine());
                buffer.setCursor(buffer.getLine().length());
                out().flush();
            }
        }
    }

    @Override
    public void setBufferLine(String newLine) {
        //must make sure that there are enough space for the
        // line thats about to be injected
        if((newLine.length()+buffer.getPrompt().getLength()) >= shell.getSize().getWidth() &&
                newLine.length() >= buffer.getLine().length()) {
            int currentRow = shell.getCursor().getRow();
            if(currentRow > -1) {
                int cursorRow = buffer.getCursorWithPrompt() / shell.getSize().getWidth();
                if(currentRow + (newLine.length() / shell.getSize().getWidth()) - cursorRow >= shell.getSize().getHeight()) {
                    int numNewRows = currentRow +
                            ((newLine.length()+buffer.getPrompt().getLength()) / shell.getSize().getWidth()) -
                            cursorRow - shell.getSize().getHeight();
                    //if the line is exactly equal to termWidth we need to add another row
                    if((newLine.length()+buffer.getPrompt().getLength()) % shell.getSize().getWidth() == 0)
                        numNewRows++;
                    if(numNewRows > 0) {
                        if(isLogging) {
                            int totalRows = (newLine.length()+buffer.getPrompt().getLength()) / shell.getSize().getWidth() +1;
                            LOGGER.info("ADDING "+numNewRows+", totalRows:"+totalRows+
                                    ", currentRow:"+currentRow+", cursorRow:"+cursorRow);
                        }
                        out.print(Buffer.printAnsi(numNewRows + "S"));
                        out.print(Buffer.printAnsi(numNewRows + "A"));
                        out.flush();
                    }
                }
            }
        }
        buffer.setLine(newLine);
    }

    @Override
    public void insertBufferLine(String insert, int position) {
        if((insert.length()+buffer.totalLength()) >= shell.getSize().getWidth()) { //&&
            //(insert.length()+buffer.totalLength()) > buffer.getLine().length()) {
            int currentRow = shell.getCursor().getRow();
            if(currentRow > -1) {
                int newLine = insert.length()+buffer.totalLength();
                int cursorRow = buffer.getCursorWithPrompt() / shell.getSize().getWidth();
                if(currentRow + (newLine / shell.getSize().getWidth()) - cursorRow >= shell.getSize().getHeight()) {
                    int numNewRows = currentRow + (newLine / shell.getSize().getWidth()) - cursorRow - shell.getSize().getHeight();
                    //if the line is exactly equal to termWidth we need to add another row
                    if((insert.length()+buffer.totalLength()) % shell.getSize().getWidth() == 0)
                        numNewRows++;
                    if(numNewRows > 0) {
                        out.print(Buffer.printAnsi(numNewRows + "S"));
                        out.print(Buffer.printAnsi(numNewRows + "A"));
                        out.flush();
                    }
                }
            }
        }
        buffer.insert(position, insert);
    }

    private void displayPrompt(Prompt prompt) {
        if(prompt.hasANSI()) {
            out.print(ANSI.getStart() + "0G" + ANSI.getStart() + "2K");
            out.print(prompt.getANSI());
        }
        else
            out.print(ANSI.getStart() + "0G" + ANSI.getStart() + "2K" + prompt.getPromptAsString());
        out.flush();
    }

    @Override
    public boolean performAction(EditAction action) throws IOException {
        currentAction = action.getAction();
        action.doAction(buffer.getLine());
        if(action.getAction() == Action.MOVE) {
            moveCursor((action.getEnd() - action.getStart()));
            return true;
        }
        else if(action.getAction() == Action.DELETE || action.getAction() == Action.CHANGE) {
            //first trigger undo action
            addActionToUndoStack();

            if(action.getEnd() > action.getStart()) {
                // only if start != cursor we need to move it
                if(action.getStart() != buffer.getCursor()) {
                    moveCursor(action.getStart() - buffer.getCursor());
                }
                addToPaste(buffer.getLine().substring(action.getStart(), action.getEnd()));
                buffer.delete(action.getStart(), action.getEnd());
            }
            else {
                addToPaste(buffer.getLine().substring(action.getEnd(), action.getStart()));
                buffer.delete(action.getEnd(), action.getStart());
                moveCursor((action.getEnd() - action.getStart()));
            }

            if(editMode.getMode() == Mode.VI && buffer.getCursor() == buffer.length()) {
                if(!((ViEditMode) editMode).isInEditMode())
                    moveCursor(-1);
            }
            drawLine();
        }
        else if(action.getAction() == Action.YANK) {
            if(action.getEnd() > action.getStart()) {
                addToPaste(buffer.getLine().substring(action.getStart(), action.getEnd()));
            }
            else {
                addToPaste(buffer.getLine().substring(action.getEnd(), action.getStart()));
            }
        }

        return true;
    }


    /**
     * Paste previous yanked word/char either before or on the cursor position
     *
     * @param index which yank index
     * @param before cursor
     * @return true if everything went as expected
     */
    @Override
    public boolean paste(int index, boolean before) {
        StringBuilder pasteBuffer = pasteManager.get(index);
        if(pasteBuffer == null)
            return false;

        addActionToUndoStack();
        if(before || buffer.getCursor() >= buffer.getLine().length()) {
            insertBufferLine(pasteBuffer.toString(), buffer.getCursor());
            drawLine();
        }
        else {
            insertBufferLine(pasteBuffer.toString(), buffer.getCursor() + 1);
            drawLine();
            //move cursor one char
            moveCursor(1);
        }
        return true;
    }

    /**
     * Add current text and cursor position to the undo stack
     */
    @Override
    public void addActionToUndoStack() {
        UndoAction ua = new UndoAction(buffer.getCursor(), buffer.getLine());
        undoManager.addUndo(ua);
    }

    private void addToPaste(String buffer) {
        pasteManager.addText(new StringBuilder(buffer));
    }

    /**
     * Clear an ansi terminal.
     * Set includeBuffer to true if the current buffer should be
     * printed again after clear.
     *
     * @param includeBuffer if true include the current buffer line
     */
    @Override
    public void clear(boolean includeBuffer) {
        //(windows fix)
        if(!Config.isOSPOSIXCompatible())
            out().print(Config.getLineSeparator());
        //first clear console
        out().print(ANSI.clearScreen());
        //move cursor to correct position
        out().print(Buffer.printAnsi("1;1H"));
        //then write prompt
        if(includeBuffer) {
            displayPrompt();
            out().print(buffer.getLine());
        }
        out().flush();
    }

}
TOP

Related Classes of org.jboss.aesh.console.AeshConsoleBuffer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.