All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.aesh.readline.cursor.Line Maven / Gradle / Ivy

There is a newer version: 1.17
Show newest version
/*
 * 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;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;
import org.aesh.readline.Buffer;
import org.aesh.readline.terminal.formatting.Color;
import org.aesh.readline.terminal.formatting.TerminalCharacter;
import org.aesh.readline.terminal.formatting.TerminalColor;
import org.aesh.terminal.Connection;
import org.aesh.utils.ANSI;
import org.aesh.readline.util.Parser;

/**
 * A command line. This line abstract commands spread-out on multiple lines.
 *
 * @author [email protected]
 */
public class Line {

    private static final Logger LOG = Logger.getLogger(Line.class.getName());

    /**
     * A CursorAction is an action that modifies the cursor.
     */
    public abstract class CursorAction {

        public abstract void apply();
    }

    /**
     * Move the cursor to a given index. It will take into account multilines to
     * retrieve the column/row of a given index.
     */
    public class MoveAction extends CursorAction {

        //private CursorTransaction transaction;
        private final int index;

        MoveAction(int index) {
            this.index = index;
        }

        @Override
        public void apply() {
            CursorLocation loc = buffer.getCursorLocator().locate(index, width);
            if (loc == null) {
                throw new RuntimeException("Null Location for " + index);
            }
            CursorLocation cursorLoc = buffer.getCursorLocator().locate(buffer.multiCursor(), width);
            moveUp(cursorLoc.getRow() - loc.getRow());
            moveBackward(cursorLoc.getColumn());
            moveForward(loc.getColumn());
        }
    }

    /**
     * Move to the index and Colorize the character.
     */
    public class ColorizeAction extends CursorAction {

        private final Color text;
        private final Color background;
        private final int index;
        private final boolean bright;

        ColorizeAction(int index, Color text, Color background, boolean bright) {
            this.index = index;
            this.text = text;
            this.background = background;
            this.bright = bright;
        }

        @Override
        public void apply() {
            MoveAction move = new MoveAction(index);
            move.apply();
            char c = buffer.asString().charAt(index);
            // Usage of Intensity.BRIGHT breaks coloring on Windows.
            // Bold works on both windows and MacOSX, keeping BOLD for all for now.
            //if (OSUtils.IS_WINDOWS) {
            TerminalCharacter characterData = new TerminalCharacter(c, new TerminalColor(text, background));
            if (bright) {
                connection.stdoutHandler().accept(Parser.toCodePoints(ANSI.BOLD));
            } else {
                connection.stdoutHandler().accept(Parser.toCodePoints(ANSI.BOLD_OFF));
            }
            connection.stdoutHandler().accept(Parser.toCodePoints(characterData.toString()));
            //} else {
            //   TerminalColor color = bright ? new TerminalColor(text, background, Color.Intensity.BRIGHT) : new TerminalColor(text, background);
            //   TerminalCharacter characterData = new TerminalCharacter(c, color);
            //  connection.stdoutHandler().accept(Parser.toCodePoints(characterData.toString()));
            //}
            moveBackward(1);
        }
    }

    /**
     * Move cursor backward.
     */
    public class MoveBackwardAction extends CursorAction {

        int move;

        MoveBackwardAction(int move) {
            this.move = move;
        }

        @Override
        public void apply() {
            moveBackward(move);
        }
    }

    /**
     * Move cursor forward.
     */
    public class MoveForwardAction extends CursorAction {

        int move;

        MoveForwardAction(int move) {
            this.move = move;
        }

        @Override
        public void apply() {
            moveForward(move);
        }
    }

    /**
     * Move cursor up.
     */
    public class MoveUpAction extends CursorAction {

        int move;

        MoveUpAction(int move) {
            this.move = move;
        }

        @Override
        public void apply() {
            moveUp(move);
        }
    }

    /**
     * Move cursor down.
     */
    public class MoveDownAction extends CursorAction {

        int move;

        MoveDownAction(int move) {
            this.move = move;
        }

        @Override
        public void apply() {
            moveDown(move);
        }
    }

    /**
     * A cursor transaction runs action. NB: At the end of a transaction, the
     * cursor is back to where it was before the transaction was run. A
     * transaction doesn't change the Aesh internal cursor/content states.
     */
    public class CursorTransaction {

        private final List actions = new ArrayList<>();

        CursorTransaction(List actions) {
            this.actions.addAll(actions);
        }

        public void run() {
            saveCursor();
            try {
                for (CursorAction a : actions) {
                    try {
                        a.apply();
                    } catch (Exception ex) {
                        // Something went wrong, don't go any further
                        LOG.fine("Exception in Cursor transaction: "
                                + ex.getLocalizedMessage());
                        break;
                    }
                }
            } finally {
                restoreCursor();
            }
        }
    }

    /**
     * Builder for CursorTransaction.
     */
    public class CursorTransactionBuilder {

        private final List actions = new ArrayList<>();

        public CursorTransactionBuilder move(int value) {
            actions.add(new MoveAction(value));
            return this;
        }

        public CursorTransactionBuilder colorize(int index, Color text, Color bg, boolean bright) {
            actions.add(new ColorizeAction(index, text, bg, bright));
            return this;
        }

        public CursorTransactionBuilder moveBackward(int value) {
            actions.add(new MoveBackwardAction(value));
            return this;
        }

        public CursorTransactionBuilder moveForward(int value) {
            actions.add(new MoveForwardAction(value));
            return this;
        }

        public CursorTransactionBuilder moveUp(int value) {
            actions.add(new MoveUpAction(value));
            return this;
        }

        public CursorTransactionBuilder moveDown(int value) {
            actions.add(new MoveDownAction(value));
            return this;
        }

        public CursorTransaction build() {
            if (buffer.getCursorLocator().isLocationInvalidated()) {
                return new CursorTransaction(Collections.emptyList());
            } else {
                return new CursorTransaction(actions);
            }
        }
    }

    private final Buffer buffer;
    private final Connection connection;
    private final int width;

    public Line(Buffer buffer, Connection connection, int width) {
        this.buffer = buffer;
        this.connection = connection;
        this.width = width;
    }

    /**
     * Build a new builder.
     *
     * @return The builder.
     */
    public CursorTransactionBuilder newCursorTransactionBuilder() {
        return new CursorTransactionBuilder();
    }

    public CursorLocator getCursorLocator() {
        return buffer.getCursorLocator();
    }

    /**
     * Gets the index of the character where the cursor is located.
     *
     * @return The selected character index.
     */
    public int getCurrentCharacterIndex() {
        return buffer.multiCursor();
    }

    /**
     * Returns a string from the beginning of the line to the cursor. Takes into
     * account multiple lines.
     *
     * @return
     */
    public String getLineToCursor() {
        return buffer.asString().substring(0, buffer.multiCursor());
    }

    /**
     * Returns the character located at the cursor.
     *
     * @return The character.
     */
    public int getCharacterAtCursor() {
        return buffer.get(buffer.cursor());
    }

    private void moveUp(int delta) {
        move(delta, 'A');
    }

    private void moveDown(int delta) {
        move(delta, 'B');
    }

    private void moveForward(int delta) {
        move(delta, 'C');
    }

    private void moveBackward(int delta) {
        move(delta, 'D');
    }

    private void move(int delta, char action) {
        if (delta > 0) {
            connection.stdoutHandler().accept(buffer.moveNumberOfColumns(delta, action));
        }
    }

    private void saveCursor() {
        connection.stdoutHandler().accept(Parser.toCodePoints(ANSI.CURSOR_SAVE));
    }

    private void restoreCursor() {
        connection.stdoutHandler().accept(Parser.toCodePoints(ANSI.CURSOR_RESTORE));
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy