Share completion and add SSH line editing
This commit is contained in:
+57
-44
@@ -7,7 +7,6 @@
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "esp_console.h"
|
||||
#include "linenoise/linenoise.h"
|
||||
|
||||
static const char *const s_root_candidates[] = {
|
||||
@@ -144,6 +143,7 @@ static const char *const s_completion_candidates[] = {
|
||||
"wifi start",
|
||||
"wifi stop",
|
||||
"wifi reconnect",
|
||||
"wifi next-profile",
|
||||
"wifi save",
|
||||
"wifi load",
|
||||
"wifi defaults",
|
||||
@@ -202,10 +202,12 @@ static const char *const s_completion_candidates[] = {
|
||||
"ssh reset --force",
|
||||
};
|
||||
|
||||
bool console_completion_expand(const char *line, char *completed, size_t capacity)
|
||||
void console_completion_visit(const char *line,
|
||||
console_completion_visitor_t visitor,
|
||||
void *context)
|
||||
{
|
||||
if (line == NULL || completed == NULL || capacity == 0U) {
|
||||
return false;
|
||||
if (line == NULL || visitor == NULL) {
|
||||
return;
|
||||
}
|
||||
size_t line_length = strlen(line);
|
||||
const char *const *candidates = strchr(line, ' ') == NULL
|
||||
@@ -215,33 +217,56 @@ bool console_completion_expand(const char *line, char *completed, size_t capacit
|
||||
? sizeof(s_root_candidates) / sizeof(s_root_candidates[0])
|
||||
: sizeof(s_completion_candidates) /
|
||||
sizeof(s_completion_candidates[0]);
|
||||
const char *first = NULL;
|
||||
size_t common_length = 0U;
|
||||
for (size_t index = 0U; index < candidate_count; ++index) {
|
||||
const char *candidate = candidates[index];
|
||||
if (strncmp(candidate, line, line_length) != 0) {
|
||||
continue;
|
||||
if (strlen(candidate) > line_length &&
|
||||
strncmp(candidate, line, line_length) == 0 &&
|
||||
!visitor(candidate, context)) {
|
||||
return;
|
||||
}
|
||||
if (first == NULL) {
|
||||
first = candidate;
|
||||
common_length = strlen(candidate);
|
||||
continue;
|
||||
}
|
||||
size_t candidate_length = strlen(candidate);
|
||||
if (common_length > candidate_length) {
|
||||
common_length = candidate_length;
|
||||
}
|
||||
size_t offset = line_length;
|
||||
while (offset < common_length && first[offset] == candidate[offset]) {
|
||||
++offset;
|
||||
}
|
||||
common_length = offset;
|
||||
}
|
||||
if (first == NULL || common_length <= line_length || common_length >= capacity) {
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
const char *line;
|
||||
const char *first;
|
||||
size_t common_length;
|
||||
} completion_expand_context_t;
|
||||
|
||||
static bool collect_common_prefix(const char *candidate, void *context)
|
||||
{
|
||||
completion_expand_context_t *result = context;
|
||||
if (result->first == NULL) {
|
||||
result->first = candidate;
|
||||
result->common_length = strlen(candidate);
|
||||
return true;
|
||||
}
|
||||
size_t candidate_length = strlen(candidate);
|
||||
if (result->common_length > candidate_length) {
|
||||
result->common_length = candidate_length;
|
||||
}
|
||||
size_t offset = strlen(result->line);
|
||||
while (offset < result->common_length && result->first[offset] == candidate[offset]) {
|
||||
++offset;
|
||||
}
|
||||
result->common_length = offset;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool console_completion_expand(const char *line, char *completed, size_t capacity)
|
||||
{
|
||||
if (line == NULL || completed == NULL || capacity == 0U) {
|
||||
return false;
|
||||
}
|
||||
memcpy(completed, first, common_length);
|
||||
completed[common_length] = '\0';
|
||||
completion_expand_context_t result = {.line = line};
|
||||
console_completion_visit(line, collect_common_prefix, &result);
|
||||
size_t line_length = strlen(line);
|
||||
if (result.first == NULL || result.common_length <= line_length ||
|
||||
result.common_length >= capacity) {
|
||||
return false;
|
||||
}
|
||||
memcpy(completed, result.first, result.common_length);
|
||||
completed[result.common_length] = '\0';
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -261,27 +286,15 @@ static ssize_t console_read_with_late_terminal_upgrade(int file_descriptor,
|
||||
return received;
|
||||
}
|
||||
|
||||
static bool add_linenoise_completion(const char *candidate, void *context)
|
||||
{
|
||||
linenoiseAddCompletion(context, candidate);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void console_completion_callback(const char *buffer, linenoiseCompletions *completions)
|
||||
{
|
||||
/* Preserve ESP-IDF completion for registered root command names. */
|
||||
if (strchr(buffer, ' ') == NULL) {
|
||||
esp_console_get_completion(buffer, completions);
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t buffer_length = strlen(buffer);
|
||||
for (size_t index = 0;
|
||||
index < sizeof(s_completion_candidates) / sizeof(s_completion_candidates[0]);
|
||||
++index) {
|
||||
const char *const candidate = s_completion_candidates[index];
|
||||
const size_t candidate_length = strlen(candidate);
|
||||
|
||||
/* linenoise expects the complete replacement line, not only its suffix. */
|
||||
if (candidate_length > buffer_length &&
|
||||
strncmp(candidate, buffer, buffer_length) == 0) {
|
||||
linenoiseAddCompletion(completions, candidate);
|
||||
}
|
||||
}
|
||||
console_completion_visit(buffer, add_linenoise_completion, completions);
|
||||
}
|
||||
|
||||
void console_completion_install(void)
|
||||
|
||||
Reference in New Issue
Block a user