[njs] Shell: simplified input completion handler.

Dmitry Volyntsev xeioex at nginx.com
Wed May 10 02:00:04 UTC 2023


details:   https://hg.nginx.org/njs/rev/d610d744bbd2
branches:  
changeset: 2110:d610d744bbd2
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Mon May 08 22:03:32 2023 -0700
description:
Shell: simplified input completion handler.

Previously, the completion logic was split between njs_vm_completion()
and njs_completion_generator() in shell.  Now the completion part is
done in njs_vm_completion(), as a result njs_completion_generator()
is simplified.

diffstat:

 src/njs_builtin.c |  108 +++++++++++++++++++++++++++++++++++++++++++++++++++--
 src/njs_shell.c   |   88 +++----------------------------------------
 2 files changed, 110 insertions(+), 86 deletions(-)

diffs (312 lines):

diff -r 2467a70fd45a -r d610d744bbd2 src/njs_builtin.c
--- a/src/njs_builtin.c	Mon May 08 16:40:50 2023 -0700
+++ b/src/njs_builtin.c	Mon May 08 22:03:32 2023 -0700
@@ -27,7 +27,10 @@ static njs_int_t njs_global_this_prop_ha
     njs_value_t *retval);
 static njs_arr_t *njs_vm_expression_completions(njs_vm_t *vm,
     njs_str_t *expression);
-static njs_arr_t *njs_object_completions(njs_vm_t *vm, njs_value_t *object);
+static njs_arr_t *njs_vm_global_var_completions(njs_vm_t *vm,
+	njs_str_t *expression);
+static njs_arr_t *njs_object_completions(njs_vm_t *vm, njs_value_t *object,
+    njs_str_t *expression);
 static njs_int_t njs_env_hash_init(njs_vm_t *vm, njs_lvlhsh_t *hash,
     char **environment);
 
@@ -544,15 +547,76 @@ njs_builtin_completions(njs_vm_t *vm)
 njs_arr_t *
 njs_vm_completions(njs_vm_t *vm, njs_str_t *expression)
 {
+    u_char  *p, *end;
+
     if (expression == NULL) {
         return njs_builtin_completions(vm);
     }
 
+    p = expression->start;
+    end = p + expression->length;
+
+    while (p < end && *p != '.') { p++; }
+
+    if (p == end) {
+        return njs_vm_global_var_completions(vm, expression);
+    }
+
     return njs_vm_expression_completions(vm, expression);
 }
 
 
 static njs_arr_t *
+njs_vm_global_var_completions(njs_vm_t *vm, njs_str_t *expression)
+{
+    njs_str_t                *completion;
+    njs_arr_t                *array;
+    njs_rbtree_t             *variables;
+    njs_rbtree_node_t        *node;
+    njs_variable_node_t      *vnode;
+    const njs_lexer_entry_t  *lex_entry;
+
+    variables = (vm->global_scope != NULL) ? &vm->global_scope->variables
+                                           : NULL;
+    if (njs_slow_path(variables == NULL)) {
+        return NULL;
+    }
+
+    array = njs_arr_create(vm->mem_pool, 8, sizeof(njs_str_t));
+    if (njs_slow_path(array == NULL)) {
+        return NULL;
+    }
+
+    node = njs_rbtree_min(variables);
+
+    while (njs_rbtree_is_there_successor(variables, node)) {
+        vnode = (njs_variable_node_t *) node;
+
+        node = njs_rbtree_node_successor(variables, node);
+
+        lex_entry = njs_lexer_entry(vnode->key);
+        if (lex_entry == NULL) {
+            continue;
+        }
+
+        if (lex_entry->name.length >= expression->length
+            && njs_strncmp(expression->start, lex_entry->name.start,
+                           expression->length) == 0)
+        {
+            completion = njs_arr_add(array);
+            if (njs_slow_path(completion == NULL)) {
+                return NULL;
+            }
+
+            *completion = lex_entry->name;
+        }
+    }
+
+    return array;
+}
+
+
+static njs_arr_t *
 njs_vm_expression_completions(njs_vm_t *vm, njs_str_t *expression)
 {
     u_char               *p, *end;
@@ -611,6 +675,10 @@ njs_vm_expression_completions(njs_vm_t *
 
         ret = njs_lvlhsh_find(njs_object_hash(value), &lhq);
         if (njs_slow_path(ret != NJS_OK)) {
+            if (ret == NJS_DECLINED) {
+                break;
+            }
+
             return NULL;
         }
 
@@ -625,20 +693,31 @@ njs_vm_expression_completions(njs_vm_t *
         value = njs_prop_value(prop);
     }
 
-    return njs_object_completions(vm, value);
+    return njs_object_completions(vm, value, expression);
 }
 
 
 static njs_arr_t *
-njs_object_completions(njs_vm_t *vm, njs_value_t *object)
+njs_object_completions(njs_vm_t *vm, njs_value_t *object, njs_str_t *expression)
 {
+    u_char            *prefix;
     double            num;
+    size_t            len;
     njs_arr_t         *array;
-    njs_str_t         *completion;
+    njs_str_t         *completion, key;
     njs_uint_t        n;
     njs_array_t       *keys;
     njs_value_type_t  type;
 
+    prefix = expression->start + expression->length;
+
+    while (prefix > expression->start && *prefix != '.') {
+        prefix--;
+    }
+
+    prefix++;
+    len = expression->length - (prefix - expression->start);
+
     array = NULL;
     type = object->type;
 
@@ -657,6 +736,14 @@ njs_object_completions(njs_vm_t *vm, njs
     }
 
     for (n = 0; n < keys->length; n++) {
+        njs_string_get(&keys->start[n], &key);
+
+        if (len != 0
+            && njs_strncmp(key.start, prefix, njs_min(len, key.length)) != 0)
+        {
+            continue;
+        }
+
         num = njs_key_to_index(&keys->start[n]);
 
         if (!njs_key_is_integer_index(num, &keys->start[n])) {
@@ -667,7 +754,18 @@ njs_object_completions(njs_vm_t *vm, njs
                 goto done;
             }
 
-            njs_string_get(&keys->start[n], completion);
+            completion->length = (prefix - expression->start) + key.length + 1;
+            completion->start = njs_mp_alloc(vm->mem_pool, completion->length);
+            if (completion == NULL) {
+                njs_arr_destroy(array);
+                array = NULL;
+                goto done;
+            }
+
+            njs_sprintf(completion->start,
+                        completion->start + completion->length,
+                        "%*s%V%Z", prefix - expression->start,
+                        expression->start, &key);
         }
     }
 
diff -r 2467a70fd45a -r d610d744bbd2 src/njs_shell.c
--- a/src/njs_shell.c	Mon May 08 16:40:50 2023 -0700
+++ b/src/njs_shell.c	Mon May 08 22:03:32 2023 -0700
@@ -63,8 +63,7 @@ typedef struct {
     njs_rbtree_node_t       *node;
 
     enum {
-       NJS_COMPLETION_VAR = 0,
-       NJS_COMPLETION_SUFFIX,
+       NJS_COMPLETION_SUFFIX = 0,
        NJS_COMPLETION_GLOBAL
     }                       phase;
 } njs_completion_t;
@@ -1189,78 +1188,31 @@ njs_editline_init(void)
 static char *
 njs_completion_generator(const char *text, int state)
 {
-    char                     *completion;
-    size_t                   len;
-    njs_str_t                expression, *suffix;
-    njs_vm_t                 *vm;
-    const char               *p;
-    njs_rbtree_t             *variables;
-    njs_completion_t         *cmpl;
-    njs_variable_node_t      *var_node;
-    const njs_lexer_entry_t  *lex_entry;
+    njs_str_t         expression, *suffix;
+    njs_vm_t          *vm;
+    njs_completion_t  *cmpl;
 
     vm = njs_console.vm;
     cmpl = &njs_console.completion;
 
     if (state == 0) {
-        cmpl->phase = 0;
+        cmpl->phase = NJS_COMPLETION_SUFFIX;
         cmpl->index = 0;
         cmpl->length = njs_strlen(text);
         cmpl->suffix_completions = NULL;
-
-        if (vm->global_scope != NULL) {
-            cmpl->node = njs_rbtree_min(&vm->global_scope->variables);
-        }
     }
 
 next:
 
     switch (cmpl->phase) {
-    case NJS_COMPLETION_VAR:
-        variables = (vm->global_scope != NULL) ? &vm->global_scope->variables
-                                               : NULL;
-
-        if (variables == NULL) {
-            njs_next_phase(cmpl);
-        }
-
-        while (njs_rbtree_is_there_successor(variables, cmpl->node)) {
-            var_node = (njs_variable_node_t *) cmpl->node;
-
-            lex_entry = njs_lexer_entry(var_node->key);
-            if (lex_entry == NULL) {
-                break;
-            }
-
-            cmpl->node = njs_rbtree_node_successor(variables, cmpl->node);
-
-            if (lex_entry->name.length >= cmpl->length
-                && njs_strncmp(text, lex_entry->name.start, cmpl->length) == 0)
-            {
-                return njs_editline(&lex_entry->name);
-            }
-
-        }
-
-        njs_next_phase(cmpl);
-
     case NJS_COMPLETION_SUFFIX:
         if (cmpl->length == 0) {
             njs_next_phase(cmpl);
         }
 
         if (cmpl->suffix_completions == NULL) {
-            /* Getting the longest prefix before a '.' */
-
-            p = &text[cmpl->length - 1];
-            while (p > text && *p != '.') { p--; }
-
-            if (*p != '.') {
-                njs_next_phase(cmpl);
-            }
-
             expression.start = (u_char *) text;
-            expression.length = p - text;
+            expression.length = cmpl->length;
 
             cmpl->suffix_completions = njs_vm_completions(vm, &expression);
             if (cmpl->suffix_completions == NULL) {
@@ -1268,18 +1220,6 @@ next:
             }
         }
 
-        /* Getting the right-most suffix after a '.' */
-
-        len = 0;
-        p = &text[cmpl->length - 1];
-
-        while (p > text && *p != '.') {
-            p--;
-            len++;
-        }
-
-        p++;
-
         for ( ;; ) {
             if (cmpl->index >= cmpl->suffix_completions->items) {
                 njs_next_phase(cmpl);
@@ -1287,21 +1227,7 @@ next:
 
             suffix = njs_completion(cmpl->suffix_completions, cmpl->index++);
 
-            if (len != 0 && njs_strncmp(suffix->start, p,
-                                        njs_min(len, suffix->length)) != 0)
-            {
-                continue;
-            }
-
-            len = suffix->length + (p - text) + 1;
-            completion = malloc(len);
-            if (completion == NULL) {
-                return NULL;
-            }
-
-            njs_sprintf((u_char *) completion, (u_char *) completion + len,
-                        "%*s%V%Z", p - text, text, suffix);
-            return completion;
+            return njs_editline(suffix);
         }
 
     case NJS_COMPLETION_GLOBAL:


More information about the nginx-devel mailing list