2018-08-09 09:56:10 +02:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2018 Nordic Semiconductor ASA
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "shell_ops.h"
|
|
|
|
|
|
|
|
void shell_op_cursor_vert_move(const struct shell *shell, s32_t delta)
|
|
|
|
{
|
|
|
|
if (delta != 0) {
|
|
|
|
shell_raw_fprintf(shell->fprintf_ctx, "\033[%d%c",
|
|
|
|
delta > 0 ? delta : -delta,
|
|
|
|
delta > 0 ? 'A' : 'B');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void shell_op_cursor_horiz_move(const struct shell *shell, s32_t delta)
|
|
|
|
{
|
|
|
|
if (delta != 0) {
|
|
|
|
shell_raw_fprintf(shell->fprintf_ctx, "\033[%d%c",
|
|
|
|
delta > 0 ? delta : -delta,
|
|
|
|
delta > 0 ? 'C' : 'D');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Function returns true if command length is equal to multiplicity of terminal
|
|
|
|
* width.
|
|
|
|
*/
|
|
|
|
static inline bool full_line_cmd(const struct shell *shell)
|
|
|
|
{
|
2018-09-21 16:41:22 +02:00
|
|
|
return ((shell->ctx->cmd_buff_len + shell_strlen(shell->prompt))
|
2018-08-09 09:56:10 +02:00
|
|
|
% shell->ctx->vt100_ctx.cons.terminal_wid == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Function returns true if cursor is at beginning of an empty line. */
|
|
|
|
bool shell_cursor_in_empty_line(const struct shell *shell)
|
|
|
|
{
|
2018-09-21 16:41:22 +02:00
|
|
|
return ((shell->ctx->cmd_buff_pos + shell_strlen(shell->prompt))
|
2018-08-09 09:56:10 +02:00
|
|
|
% shell->ctx->vt100_ctx.cons.terminal_wid == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void shell_op_cond_next_line(const struct shell *shell)
|
|
|
|
{
|
|
|
|
if (shell_cursor_in_empty_line(shell) || full_line_cmd(shell)) {
|
|
|
|
cursor_next_line_move(shell);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void shell_op_cursor_position_synchronize(const struct shell *shell)
|
|
|
|
{
|
|
|
|
struct shell_multiline_cons *cons = &shell->ctx->vt100_ctx.cons;
|
|
|
|
bool last_line;
|
|
|
|
|
|
|
|
shell_multiline_data_calc(cons, shell->ctx->cmd_buff_pos,
|
|
|
|
shell->ctx->cmd_buff_len);
|
|
|
|
last_line = (cons->cur_y == cons->cur_y_end);
|
|
|
|
|
|
|
|
/* In case cursor reaches the bottom line of a terminal, it will
|
|
|
|
* be moved to the next line.
|
|
|
|
*/
|
|
|
|
if (full_line_cmd(shell)) {
|
|
|
|
cursor_next_line_move(shell);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (last_line) {
|
|
|
|
shell_op_cursor_horiz_move(shell, cons->cur_x -
|
|
|
|
cons->cur_x_end);
|
|
|
|
} else {
|
|
|
|
shell_op_cursor_vert_move(shell, cons->cur_y_end - cons->cur_y);
|
|
|
|
shell_op_cursor_horiz_move(shell, cons->cur_x -
|
|
|
|
cons->cur_x_end);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void shell_op_cursor_move(const struct shell *shell, s16_t val)
|
|
|
|
{
|
|
|
|
struct shell_multiline_cons *cons = &shell->ctx->vt100_ctx.cons;
|
|
|
|
u16_t new_pos = shell->ctx->cmd_buff_pos + val;
|
|
|
|
s32_t row_span;
|
|
|
|
s32_t col_span;
|
|
|
|
|
|
|
|
shell_multiline_data_calc(cons, shell->ctx->cmd_buff_pos,
|
|
|
|
shell->ctx->cmd_buff_len);
|
|
|
|
|
|
|
|
/* Calculate the new cursor. */
|
|
|
|
row_span = row_span_with_buffer_offsets_get(&shell->ctx->vt100_ctx.cons,
|
|
|
|
shell->ctx->cmd_buff_pos,
|
|
|
|
new_pos);
|
|
|
|
col_span = column_span_with_buffer_offsets_get(
|
|
|
|
&shell->ctx->vt100_ctx.cons,
|
|
|
|
shell->ctx->cmd_buff_pos,
|
|
|
|
new_pos);
|
|
|
|
|
|
|
|
shell_op_cursor_vert_move(shell, -row_span);
|
|
|
|
shell_op_cursor_horiz_move(shell, col_span);
|
|
|
|
shell->ctx->cmd_buff_pos = new_pos;
|
|
|
|
}
|
|
|
|
|
|
|
|
void shell_op_word_remove(const struct shell *shell)
|
|
|
|
{
|
|
|
|
char *str = &shell->ctx->cmd_buff[shell->ctx->cmd_buff_pos - 1];
|
|
|
|
char *str_start = &shell->ctx->cmd_buff[0];
|
|
|
|
u16_t chars_to_delete;
|
|
|
|
|
|
|
|
/* Line must not be empty and cursor must not be at 0 to continue. */
|
|
|
|
if ((shell->ctx->cmd_buff_len == 0) ||
|
|
|
|
(shell->ctx->cmd_buff_pos == 0)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Start at the current position. */
|
|
|
|
chars_to_delete = 0;
|
|
|
|
|
|
|
|
/* Look back for all spaces then for non-spaces. */
|
|
|
|
while ((str >= str_start) && (*str == ' ')) {
|
|
|
|
++chars_to_delete;
|
|
|
|
--str;
|
|
|
|
}
|
|
|
|
|
|
|
|
while ((str >= str_start) && (*str != ' ')) {
|
|
|
|
++chars_to_delete;
|
|
|
|
--str;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Manage the buffer. */
|
|
|
|
memmove(str + 1, str + 1 + chars_to_delete,
|
|
|
|
shell->ctx->cmd_buff_len - chars_to_delete);
|
|
|
|
shell->ctx->cmd_buff_len -= chars_to_delete;
|
|
|
|
shell->ctx->cmd_buff[shell->ctx->cmd_buff_len] = '\0';
|
|
|
|
|
|
|
|
/* Update display. */
|
|
|
|
shell_op_cursor_move(shell, -chars_to_delete);
|
|
|
|
cursor_save(shell);
|
|
|
|
shell_fprintf(shell, SHELL_NORMAL, "%s", str + 1);
|
|
|
|
clear_eos(shell);
|
|
|
|
cursor_restore(shell);
|
|
|
|
}
|
|
|
|
|
|
|
|
void shell_op_cursor_home_move(const struct shell *shell)
|
|
|
|
{
|
|
|
|
shell_op_cursor_move(shell, -shell->ctx->cmd_buff_pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
void shell_op_cursor_end_move(const struct shell *shell)
|
|
|
|
{
|
|
|
|
shell_op_cursor_move(shell, shell->ctx->cmd_buff_len -
|
|
|
|
shell->ctx->cmd_buff_pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void shell_op_left_arrow(const struct shell *shell)
|
|
|
|
{
|
|
|
|
if (shell->ctx->cmd_buff_pos > 0) {
|
|
|
|
shell_op_cursor_move(shell, -1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void shell_op_right_arrow(const struct shell *shell)
|
|
|
|
{
|
|
|
|
if (shell->ctx->cmd_buff_pos < shell->ctx->cmd_buff_len) {
|
|
|
|
shell_op_cursor_move(shell, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void reprint_from_cursor(const struct shell *shell, u16_t diff,
|
|
|
|
bool data_removed)
|
|
|
|
{
|
|
|
|
/* Clear eos is needed only when newly printed command is shorter than
|
|
|
|
* previously printed command. This can happen when delete or backspace
|
|
|
|
* was called.
|
|
|
|
*
|
|
|
|
* Such condition is useful for Bluetooth devices to save number of
|
|
|
|
* bytes transmitted between terminal and device.
|
|
|
|
*/
|
|
|
|
if (data_removed) {
|
|
|
|
clear_eos(shell);
|
|
|
|
}
|
|
|
|
|
|
|
|
shell_fprintf(shell, SHELL_NORMAL, "%s",
|
|
|
|
&shell->ctx->cmd_buff[shell->ctx->cmd_buff_pos]);
|
|
|
|
shell->ctx->cmd_buff_pos = shell->ctx->cmd_buff_len;
|
|
|
|
|
|
|
|
if (full_line_cmd(shell)) {
|
|
|
|
if (((data_removed) && (diff > 0)) || (!data_removed)) {
|
|
|
|
cursor_next_line_move(shell);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
shell_op_cursor_move(shell, -diff);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void data_insert(const struct shell *shell, const char *data, u16_t len)
|
|
|
|
{
|
|
|
|
u16_t after = shell->ctx->cmd_buff_len - shell->ctx->cmd_buff_pos;
|
|
|
|
char *curr_pos = &shell->ctx->cmd_buff[shell->ctx->cmd_buff_pos];
|
|
|
|
|
|
|
|
if ((shell->ctx->cmd_buff_len + len) >= CONFIG_SHELL_CMD_BUFF_SIZE) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
memmove(curr_pos + len, curr_pos, after);
|
|
|
|
memcpy(curr_pos, data, len);
|
|
|
|
shell->ctx->cmd_buff_len += len;
|
|
|
|
shell->ctx->cmd_buff[shell->ctx->cmd_buff_len] = '\0';
|
|
|
|
|
|
|
|
if (!flag_echo_is_set(shell)) {
|
|
|
|
shell->ctx->cmd_buff_pos += len;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
reprint_from_cursor(shell, after, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void char_replace(const struct shell *shell, char data)
|
|
|
|
{
|
|
|
|
shell->ctx->cmd_buff[shell->ctx->cmd_buff_pos++] = data;
|
|
|
|
shell_raw_fprintf(shell->fprintf_ctx, "%c", data);
|
|
|
|
if (shell_cursor_in_empty_line(shell)) {
|
|
|
|
cursor_next_line_move(shell);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void shell_op_char_insert(const struct shell *shell, char data)
|
|
|
|
{
|
|
|
|
if (shell->ctx->internal.flags.insert_mode &&
|
|
|
|
(shell->ctx->cmd_buff_len != shell->ctx->cmd_buff_pos)) {
|
|
|
|
char_replace(shell, data);
|
|
|
|
} else {
|
|
|
|
data_insert(shell, &data, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void shell_op_char_backspace(const struct shell *shell)
|
|
|
|
{
|
|
|
|
if ((shell->ctx->cmd_buff_len == 0) ||
|
|
|
|
(shell->ctx->cmd_buff_pos == 0)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
shell_op_cursor_move(shell, -1);
|
|
|
|
shell_op_char_delete(shell);
|
|
|
|
}
|
|
|
|
|
|
|
|
void shell_op_char_delete(const struct shell *shell)
|
|
|
|
{
|
|
|
|
u16_t diff = shell->ctx->cmd_buff_len - shell->ctx->cmd_buff_pos;
|
|
|
|
char *str = &shell->ctx->cmd_buff[shell->ctx->cmd_buff_pos];
|
|
|
|
|
|
|
|
if (diff == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
memmove(str, str + 1, diff);
|
|
|
|
--shell->ctx->cmd_buff_len;
|
|
|
|
reprint_from_cursor(shell, --diff, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void shell_op_completion_insert(const struct shell *shell,
|
|
|
|
const char *compl,
|
|
|
|
u16_t compl_len)
|
|
|
|
{
|
|
|
|
data_insert(shell, compl, compl_len);
|
|
|
|
}
|