From acff18fc92ff62fda80e26e8b74ff8e1be0167a0 Mon Sep 17 00:00:00 2001 From: aPiecek Date: Fri, 25 Oct 2024 07:24:48 +0200 Subject: [PATCH] yanglint FEATURE multiline for interactive mode The tcl-tests needed to be modified because ANSI escape codes are sent differently. --- tests/tool_i.tcl | 9 ++++---- tests/yanglint/interactive/completion.test | 19 ++++++++++------ tools/lint/linenoise/linenoise.c | 26 ++++++++++++++++++++-- tools/lint/linenoise/linenoise.h | 2 ++ tools/lint/main.c | 1 + 5 files changed, 44 insertions(+), 13 deletions(-) diff --git a/tests/tool_i.tcl b/tests/tool_i.tcl index d0f3d4b1f..fa8c96460 100644 --- a/tests/tool_i.tcl +++ b/tests/tool_i.tcl @@ -135,8 +135,7 @@ proc ly_completion {input output} { global prompt send -- "${input}\t" - # expecting echoing input, output and 10 terminal control characters - expect -re "^${input}\r${prompt}${output}.*\r.*$" + expect -re "${input}${output}" } # Send a completion request and check if the anchored regex hint options match. @@ -146,11 +145,13 @@ proc ly_hint {input prev_input hints} { set output {} foreach i $hints { # each element might have some number of spaces and CRLF around it - append output "${i} *(?:\\r\\n)?" + append output "${i} *(\\r\\n)?" } + set termcode1 "\r\\u001b\\\[0K" + set termcode2 ".*" send -- "${input}\t" # expecting the hints, previous input from which the hints were generated # and some number of terminal control characters - expect -re "${output}\r${prompt}${prev_input}.*\r.*$" + expect -re "${output}${termcode1}${prompt}${prev_input}${termcode2}" } diff --git a/tests/yanglint/interactive/completion.test b/tests/yanglint/interactive/completion.test index 86ded1f8f..27fd52602 100644 --- a/tests/yanglint/interactive/completion.test +++ b/tests/yanglint/interactive/completion.test @@ -12,14 +12,15 @@ test completion_hints_ietf_ip {Completion and hints for ietf-ip.yang} { ly_cmd "add $mdir/ietf-ip.yang" # completion and hint - ly_completion "print -f info -P " "print -f info -P /ietf-" + ly_completion "print -f info -P " "/ietf-" set hints {"/ietf-yang-schema-mount:schema-mounts" "/ietf-interfaces:interfaces" "/ietf-interfaces:interfaces-state"} ly_hint "" "print -f info -P /ietf-" $hints # double completion - ly_completion "i" "print -f info -P /ietf-interfaces:interfaces" - ly_completion "/" "print -f info -P /ietf-interfaces:interfaces/interface" + ly_completion "i" "nterfaces:interfaces" + ly_completion "/" "interface" + # current cli: print -f info -P /ietf-interfaces:interfaces/interface # a lot of hints set hints {"/ietf-interfaces:interfaces/interface" @@ -31,16 +32,20 @@ test completion_hints_ietf_ip {Completion and hints for ietf-ip.yang} { ly_hint "" "print -f info -P /ietf-interfaces:interfaces/interface" $hints # double tab - ly_completion "/i" "print -f info -P /ietf-interfaces:interfaces/interface/ietf-ip:ipv" - ly_completion "4" "print -f info -P /ietf-interfaces:interfaces/interface/ietf-ip:ipv4" + ly_completion "/i" "etf-ip:ipv" + # current cli: print -f info -P /ietf-interfaces:interfaces/interface/ietf-ip:ipv + ly_completion "4" "" + # current cli: print -f info -P /ietf-interfaces:interfaces/interface/ietf-ip:ipv4 + set hints { "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4" "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4/enabled" "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4/forwarding" "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4/mtu" "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4/address" "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4/neighbor" } - ly_hint "\t" "print -f info -P /ietf-interfaces:interfaces/interface/ietf-ip:ipv" $hints + ly_hint "\t" "print -f info -P /ietf-interfaces:interfaces/interface" $hints # no more completion - ly_completion "/e" "print -f info -P /ietf-interfaces:interfaces/interface/ietf-ip:ipv4/enabled " + ly_completion "/e" "nabled " + # current cli: print -f info -P /ietf-interfaces:interfaces/interface/ietf-ip:ipv4/enabled }} # Note that somehow a command is automatically sent again (\t\t replaced by \r) after the hints. diff --git a/tools/lint/linenoise/linenoise.c b/tools/lint/linenoise/linenoise.c index 379f3806a..202295b11 100644 --- a/tools/lint/linenoise/linenoise.c +++ b/tools/lint/linenoise/linenoise.c @@ -245,11 +245,12 @@ static size_t columnPos(const char *buf, size_t buf_len, size_t pos) { static size_t columnPosForMultiLine(const char *buf, size_t buf_len, size_t pos, size_t cols, size_t ini_pos) { size_t ret = 0; size_t colwid = ini_pos; + size_t len; size_t off = 0; while (off < buf_len) { size_t col_len; - size_t len = nextCharLen(buf,buf_len,off,&col_len); + len = nextCharLen(buf,buf_len,off,&col_len); int dif = (int)(colwid + col_len) - (int)cols; if (dif > 0) { @@ -799,7 +800,7 @@ static void refreshSingleLine(struct linenoiseState *l, int flags) { static void refreshMultiLine(struct linenoiseState *l, int flags) { char seq[64]; size_t pcollen = promptTextColumnLen(l->prompt,strlen(l->prompt)); - int colpos = columnPosForMultiLine(l->buf, l->len, l->len, l->cols, pcollen); + size_t colpos = columnPosForMultiLine(l->buf, l->len, l->len, l->cols, pcollen); int colpos2; /* cursor column position. */ int rows = (pcollen+colpos+l->cols-1)/l->cols; /* rows used by current buf. */ int rpos = (pcollen+l->oldcolpos+l->cols)/l->cols; /* cursor relative row. */ @@ -815,6 +816,23 @@ static void refreshMultiLine(struct linenoiseState *l, int flags) { * going to the last row. */ abInit(&ab); + if ((l->oldcollen < l->len) && // some character was added + (l->pos == l->len) && // at the end of the line + (l->pos <= l->cols) && // no multiline break + (l->prev_history_index == l->history_index) // no buffer change based on history + ) { + /* Ignore flags and just add one character. + * This code was added because redundant ANSI escape codes make tcl-tests incapable. + */ + abAppend(&ab,l->buf + l->oldcollen,strlen(l->buf) - l->oldcollen); + l->oldcolpos = colpos; + l->oldcollen = l->len; + if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */ + abFree(&ab); + l->prev_history_index = l->history_index; + return; + } + if (flags & REFRESH_CLEAN) { if (old_rows-rpos > 0) { lndebug("go down %d", old_rows-rpos); @@ -890,6 +908,8 @@ static void refreshMultiLine(struct linenoiseState *l, int flags) { lndebug("\n"); l->oldcolpos = colpos2; + l->oldcollen = l->len; + l->prev_history_index = l->history_index; if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */ abFree(&ab); @@ -1096,6 +1116,7 @@ int linenoiseEditStart(struct linenoiseState *l, int stdin_fd, int stdout_fd, ch l->prompt = prompt; l->plen = strlen(prompt); l->oldcolpos = l->pos = 0; + l->oldcollen = 0; l->len = 0; /* Enter raw mode. */ @@ -1104,6 +1125,7 @@ int linenoiseEditStart(struct linenoiseState *l, int stdin_fd, int stdout_fd, ch l->cols = getColumns(stdin_fd, stdout_fd); l->oldrows = 0; l->history_index = 0; + l->prev_history_index = 0; /* Buffer starts empty. */ l->buf[0] = '\0'; diff --git a/tools/lint/linenoise/linenoise.h b/tools/lint/linenoise/linenoise.h index c5c3f32f9..8949e3a88 100644 --- a/tools/lint/linenoise/linenoise.h +++ b/tools/lint/linenoise/linenoise.h @@ -62,10 +62,12 @@ struct linenoiseState { size_t plen; /* Prompt length. */ size_t pos; /* Current cursor position. */ size_t oldcolpos; /* Previous refresh cursor column position. */ + size_t oldcollen; /* Previous length of buffer. */ size_t len; /* Current edited line length. */ size_t cols; /* Number of columns in terminal. */ size_t oldrows; /* Rows used by last refrehsed line (multiline mode) */ int history_index; /* The history index we are currently editing. */ + int prev_history_index; /* Previous history index. */ }; typedef struct linenoiseCompletions { diff --git a/tools/lint/main.c b/tools/lint/main.c index 49a3abd8d..d57ad9934 100644 --- a/tools/lint/main.c +++ b/tools/lint/main.c @@ -55,6 +55,7 @@ main(int argc, char *argv[]) /* continue in interactive mode */ linenoiseSetCompletionCallback(complete_cmd); linenoiseSetEncodingFunctions(linenoiseUtf8PrevCharLen, linenoiseUtf8NextCharLen, linenoiseUtf8ReadCode); + linenoiseSetMultiLine(1); load_config(); if (ly_ctx_new(NULL, YL_DEFAULT_CTX_OPTIONS, &ctx)) {