details:   https://hg.nginx.org/njs/rev/fe94552843d7
branches:  
changeset: 2301:fe94552843d7
user:      Dmitry Volyntsev <xei...@nginx.com>
date:      Mon Mar 18 22:15:48 2024 -0700
description:
Shell: completions are refactored and moved out of the core.

diffstat:

 external/njs_shell.c    |  275 +++++++++++++++++++++++++++++++-------------
 src/njs.h               |    2 -
 src/njs_builtin.c       |  294 +-----------------------------------------------
 test/shell_test_njs.exp |   26 +--
 4 files changed, 201 insertions(+), 396 deletions(-)

diffs (737 lines):

diff -r 1aa2bb15c966 -r fe94552843d7 external/njs_shell.c
--- a/external/njs_shell.c      Fri Mar 15 23:14:39 2024 -0700
+++ b/external/njs_shell.c      Mon Mar 18 22:15:48 2024 -0700
@@ -83,15 +83,7 @@ typedef enum {
 
 typedef struct {
     size_t                  index;
-    size_t                  length;
-    njs_arr_t               *completions;
     njs_arr_t               *suffix_completions;
-    njs_rbtree_node_t       *node;
-
-    enum {
-       NJS_COMPLETION_SUFFIX = 0,
-       NJS_COMPLETION_GLOBAL
-    }                       phase;
 } njs_completion_t;
 
 
@@ -157,7 +149,6 @@ struct njs_engine_s {
             njs_vm_t            *vm;
 
             njs_opaque_value_t  value;
-            njs_completion_t    completion;
         }                       njs;
 #if (NJS_HAVE_QUICKJS)
         struct {
@@ -174,9 +165,11 @@ struct njs_engine_s {
     njs_int_t                 (*process_events)(njs_engine_t *engine);
     njs_int_t                 (*destroy)(njs_engine_t *engine);
     njs_int_t                 (*output)(njs_engine_t *engine, njs_int_t ret);
+    njs_arr_t                *(*complete)(njs_engine_t *engine, njs_str_t *ex);
 
     unsigned                    type;
     njs_mp_t                    *pool;
+    njs_completion_t            completion;
 };
 
 typedef struct {
@@ -1361,11 +1354,6 @@ njs_engine_njs_init(njs_engine_t *engine
         return NJS_ERROR;
     }
 
-    engine->u.njs.completion.completions = njs_vm_completions(vm, NULL);
-    if (engine->u.njs.completion.completions == NULL) {
-        return NJS_ERROR;
-    }
-
     if (opts->unhandled_rejection) {
         njs_vm_set_rejection_tracker(vm, njs_rejection_tracker,
                                      njs_vm_external_ptr(vm));
@@ -1455,6 +1443,179 @@ njs_engine_njs_output(njs_engine_t *engi
 }
 
 
+static njs_arr_t *
+njs_object_completions(njs_vm_t *vm, njs_value_t *object, njs_str_t 
*expression)
+{
+    u_char              *prefix;
+    size_t              len, prefix_len;
+    int64_t             k, n, length;
+    njs_int_t           ret;
+    njs_arr_t           *array;
+    njs_str_t           *completion, key;
+    njs_value_t         *keys;
+    njs_opaque_value_t  *start, retval, prototype;
+
+    prefix = expression->start + expression->length;
+
+    while (prefix > expression->start && *prefix != '.') {
+        prefix--;
+    }
+
+    if (prefix != expression->start) {
+        prefix++;
+    }
+
+    prefix_len = prefix - expression->start;
+    len = expression->length - prefix_len;
+
+    array = njs_arr_create(njs_vm_memory_pool(vm), 8, sizeof(njs_str_t));
+    if (njs_slow_path(array == NULL)) {
+        goto fail;
+    }
+
+    while (!njs_value_is_null(object)) {
+        keys = njs_vm_value_enumerate(vm, object, NJS_ENUM_KEYS
+                                      | NJS_ENUM_STRING,
+                                      njs_value_arg(&retval));
+        if (njs_slow_path(keys == NULL)) {
+            goto fail;
+        }
+
+        (void) njs_vm_array_length(vm, keys, &length);
+
+        start = (njs_opaque_value_t *) njs_vm_array_start(vm, keys);
+        if (start == NULL) {
+            goto fail;
+        }
+
+
+        for (n = 0; n < length; n++) {
+            ret = njs_vm_value_to_string(vm, &key, njs_value_arg(start));
+            if (njs_slow_path(ret != NJS_OK)) {
+                goto fail;
+            }
+
+            start++;
+
+            if (len > key.length || njs_strncmp(key.start, prefix, len) != 0) {
+                continue;
+            }
+
+            for (k = 0; k < array->items; k++) {
+                completion = njs_arr_item(array, k);
+
+                if ((completion->length - prefix_len - 1) == key.length
+                    && njs_strncmp(&completion->start[prefix_len],
+                                   key.start, key.length)
+                       == 0)
+                {
+                    break;
+                }
+            }
+
+            if (k != array->items) {
+                continue;
+            }
+
+            completion = njs_arr_add(array);
+            if (njs_slow_path(completion == NULL)) {
+                goto fail;
+            }
+
+            completion->length = prefix_len + key.length + 1;
+            completion->start = njs_mp_alloc(njs_vm_memory_pool(vm),
+                                             completion->length);
+            if (njs_slow_path(completion->start == NULL)) {
+                goto fail;
+            }
+
+
+            njs_sprintf(completion->start,
+                        completion->start + completion->length,
+                        "%*s%V%Z", prefix_len, expression->start, &key);
+        }
+
+        ret = njs_vm_prototype(vm, object, njs_value_arg(&prototype));
+        if (njs_slow_path(ret != NJS_OK)) {
+            goto fail;
+        }
+
+        object = njs_value_arg(&prototype);
+    }
+
+    return array;
+
+fail:
+
+    if (array != NULL) {
+        njs_arr_destroy(array);
+    }
+
+    return NULL;
+}
+
+
+static njs_arr_t *
+njs_engine_njs_complete(njs_engine_t *engine, njs_str_t *expression)
+{
+    u_char              *p, *start, *end;
+    njs_vm_t            *vm;
+    njs_int_t           ret;
+    njs_bool_t          global;
+    njs_opaque_value_t  value, key, retval;
+
+    vm = engine->u.njs.vm;
+
+    p = expression->start;
+    end = p + expression->length;
+
+    global = 1;
+    (void) njs_vm_global(vm, njs_value_arg(&value));
+
+    while (p < end && *p != '.') { p++; }
+
+    if (p == end) {
+        goto done;
+    }
+
+    p = expression->start;
+
+    for ( ;; ) {
+
+        start = (*p == '.' && p < end) ? ++p: p;
+
+        if (p == end) {
+            break;
+        }
+
+        while (p < end && *p != '.') { p++; }
+
+        ret = njs_vm_value_string_set(vm, njs_value_arg(&key), start,
+                                      p - start);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return NULL;
+        }
+
+        ret = njs_value_property(vm, njs_value_arg(&value), 
njs_value_arg(&key),
+                                 njs_value_arg(&retval));
+        if (njs_slow_path(ret != NJS_OK)) {
+            if (ret == NJS_DECLINED && !global) {
+                goto done;
+            }
+
+            return NULL;
+        }
+
+        global = 0;
+        njs_value_assign(&value, &retval);
+    }
+
+done:
+
+    return njs_object_completions(vm, njs_value_arg(&value), expression);
+}
+
+
 static njs_int_t
 njs_engine_njs_process_events(njs_engine_t *engine)
 {
@@ -2984,6 +3145,7 @@ njs_create_engine(njs_opts_t *opts)
         engine->process_events = njs_engine_njs_process_events;
         engine->destroy = njs_engine_njs_destroy;
         engine->output = njs_engine_njs_output;
+        engine->complete = njs_engine_njs_complete;
         break;
 
 #ifdef NJS_HAVE_QUICKJS
@@ -3413,91 +3575,38 @@ njs_editline_init(void)
 }
 
 
-/* editline frees the buffer every time. */
-#define njs_editline(s) strndup((char *) (s)->start, (s)->length)
-
-#define njs_completion(c, i) &(((njs_str_t *) (c)->start)[i])
-
-#define njs_next_phase(c)                                                   \
-    (c)->index = 0;                                                         \
-    (c)->phase++;                                                           \
-    goto next;
-
 static char *
 njs_completion_generator(const char *text, int state)
 {
     njs_str_t         expression, *suffix;
-    njs_vm_t          *vm;
+    njs_engine_t      *engine;
     njs_completion_t  *cmpl;
 
-    if (njs_console.engine == NULL
-        || njs_console.engine->type != NJS_ENGINE_NJS)
-    {
+    engine = njs_console.engine;
+    if (engine->complete == NULL) {
         return NULL;
     }
 
-    vm = njs_console.engine->u.njs.vm;
-    cmpl = &njs_console.engine->u.njs.completion;
+    cmpl = &engine->completion;
 
     if (state == 0) {
-        cmpl->phase = NJS_COMPLETION_SUFFIX;
         cmpl->index = 0;
-        cmpl->length = njs_strlen(text);
-        cmpl->suffix_completions = NULL;
-    }
-
-next:
-
-    switch (cmpl->phase) {
-    case NJS_COMPLETION_SUFFIX:
-        if (cmpl->length == 0) {
-            njs_next_phase(cmpl);
-        }
-
+        expression.start = (u_char *) text;
+        expression.length = njs_strlen(text);
+
+        cmpl->suffix_completions = engine->complete(engine, &expression);
         if (cmpl->suffix_completions == NULL) {
-            expression.start = (u_char *) text;
-            expression.length = cmpl->length;
-
-            cmpl->suffix_completions = njs_vm_completions(vm, &expression);
-            if (cmpl->suffix_completions == NULL) {
-                njs_next_phase(cmpl);
-            }
-        }
-
-        for ( ;; ) {
-            if (cmpl->index >= cmpl->suffix_completions->items) {
-                njs_next_phase(cmpl);
-            }
-
-            suffix = njs_completion(cmpl->suffix_completions, cmpl->index++);
-
-            return njs_editline(suffix);
-        }
-
-    case NJS_COMPLETION_GLOBAL:
-        if (cmpl->suffix_completions != NULL) {
-            /* No global completions if suffixes were found. */
-            njs_next_phase(cmpl);
-        }
-
-        for ( ;; ) {
-            if (cmpl->index >= cmpl->completions->items) {
-                break;
-            }
-
-            suffix = njs_completion(cmpl->completions, cmpl->index++);
-
-            if (suffix->start[0] == '.' || suffix->length < cmpl->length) {
-                continue;
-            }
-
-            if (njs_strncmp(text, suffix->start, cmpl->length) == 0) {
-                return njs_editline(suffix);
-            }
+            return NULL;
         }
     }
 
-    return NULL;
+    if (cmpl->index == cmpl->suffix_completions->items) {
+        return NULL;
+    }
+
+    suffix = njs_arr_item(cmpl->suffix_completions, cmpl->index++);
+
+    return strndup((char *) suffix->start, suffix->length);
 }
 
 #endif
diff -r 1aa2bb15c966 -r fe94552843d7 src/njs.h
--- a/src/njs.h Fri Mar 15 23:14:39 2024 -0700
+++ b/src/njs.h Mon Mar 18 22:15:48 2024 -0700
@@ -316,8 +316,6 @@ NJS_EXPORT njs_int_t njs_vm_pending(njs_
 NJS_EXPORT void njs_vm_set_rejection_tracker(njs_vm_t *vm,
         njs_rejection_tracker_t rejection_tracker, void *opaque);
 
-NJS_EXPORT void *njs_vm_completions(njs_vm_t *vm, njs_str_t *expression);
-
 /*
  * Runs the specified function with provided arguments.
  *  NJS_OK successful run.
diff -r 1aa2bb15c966 -r fe94552843d7 src/njs_builtin.c
--- a/src/njs_builtin.c Fri Mar 15 23:14:39 2024 -0700
+++ b/src/njs_builtin.c Mon Mar 18 22:15:48 2024 -0700
@@ -25,12 +25,7 @@ typedef struct {
 static njs_int_t njs_global_this_prop_handler(njs_vm_t *vm,
     njs_object_prop_t *self, njs_value_t *global, njs_value_t *setval,
     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_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);
 
@@ -415,293 +410,6 @@ njs_builtin_traverse(njs_vm_t *vm, njs_t
 }
 
 
-static njs_arr_t *
-njs_builtin_completions(njs_vm_t *vm)
-{
-    njs_arr_t                *array;
-    njs_str_t                *completion;
-    njs_int_t                ret;
-    njs_lvlhsh_each_t        lhe;
-    njs_builtin_traverse_t   ctx;
-    const njs_object_prop_t  *prop;
-
-    array = njs_arr_create(vm->mem_pool, 64, sizeof(njs_str_t));
-    if (njs_slow_path(array == NULL)) {
-        return NULL;
-    }
-
-    ret = njs_lexer_keywords(array);
-    if (njs_slow_path(ret != NJS_OK)) {
-        return NULL;
-    }
-
-    /* Global object completions. */
-
-    ctx.type = NJS_BUILTIN_TRAVERSE_KEYS;
-    njs_lvlhsh_init(&ctx.keys);
-
-    ret = njs_object_traverse(vm, njs_object(&vm->global_value), &ctx,
-                              njs_builtin_traverse);
-    if (njs_slow_path(ret != NJS_OK)) {
-        return NULL;
-    }
-
-    njs_lvlhsh_each_init(&lhe, &njs_object_hash_proto);
-
-    for ( ;; ) {
-        prop = njs_lvlhsh_each(&ctx.keys, &lhe);
-
-        if (prop == NULL) {
-            break;
-        }
-
-        completion = njs_arr_add(array);
-        if (njs_slow_path(completion == NULL)) {
-            return NULL;
-        }
-
-        njs_string_get(&prop->name, completion);
-    }
-
-    return array;
-}
-
-
-void *
-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;
-    njs_int_t            ret;
-    njs_value_t          *value;
-    njs_variable_t       *var;
-    njs_rbtree_node_t    *node;
-    njs_object_prop_t    *prop;
-    njs_lvlhsh_query_t   lhq;
-    njs_variable_node_t  var_node;
-
-    p = expression->start;
-    end = p + expression->length;
-
-    lhq.key.start = p;
-
-    while (p < end && *p != '.') { p++; }
-
-    lhq.proto = &njs_lexer_hash_proto;
-    lhq.key.length = p - lhq.key.start;
-    lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length);
-
-    ret = njs_lvlhsh_find(&vm->shared->keywords_hash, &lhq);
-    if (njs_slow_path(ret != NJS_OK)) {
-        return NULL;
-    }
-
-    var_node.key = (uintptr_t) lhq.value;
-
-    node = njs_rbtree_find(&vm->global_scope->variables, &var_node.node);
-    if (njs_slow_path(node == NULL)) {
-        return NULL;
-    }
-
-    var = ((njs_variable_node_t *) node)->variable;
-    value = njs_scope_value(vm, var->index);
-
-    if (!njs_is_object(value)) {
-        return NULL;
-    }
-
-    lhq.proto = &njs_object_hash_proto;
-
-    for ( ;; ) {
-
-        if (p == end) {
-            break;
-        }
-
-        lhq.key.start = ++p;
-
-        while (p < end && *p != '.') { p++; }
-
-        lhq.key.length = p - lhq.key.start;
-        lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length);
-
-        ret = njs_lvlhsh_find(njs_object_hash(value), &lhq);
-        if (njs_slow_path(ret != NJS_OK)) {
-            if (ret == NJS_DECLINED) {
-                break;
-            }
-
-            return NULL;
-        }
-
-        prop = lhq.value;
-
-        if (njs_is_accessor_descriptor(prop) ||
-            !njs_is_object(njs_prop_value(prop)))
-        {
-            return NULL;
-        }
-
-        value = njs_prop_value(prop);
-    }
-
-    return njs_object_completions(vm, value, expression);
-}
-
-
-static njs_arr_t *
-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, 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;
-
-    if (type == NJS_ARRAY || type == NJS_TYPED_ARRAY) {
-        object->type = NJS_OBJECT;
-    }
-
-    keys = njs_value_enumerate(vm, object, NJS_ENUM_KEYS | NJS_ENUM_STRING);
-    if (njs_slow_path(keys == NULL)) {
-        goto done;
-    }
-
-    array = njs_arr_create(vm->mem_pool, 8, sizeof(njs_str_t));
-    if (njs_slow_path(array == NULL)) {
-        goto done;
-    }
-
-    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])) {
-            completion = njs_arr_add(array);
-            if (njs_slow_path(completion == NULL)) {
-                njs_arr_destroy(array);
-                array = NULL;
-                goto done;
-            }
-
-            completion->length = (prefix - expression->start) + key.length + 1;
-            completion->start = njs_mp_alloc(vm->mem_pool, completion->length);
-            if (njs_slow_path(completion->start == 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);
-        }
-    }
-
-done:
-
-    if (type == NJS_ARRAY || type == NJS_TYPED_ARRAY) {
-        object->type = type;
-    }
-
-    return array;
-}
-
-
 typedef struct {
     njs_str_t               name;
     njs_function_native_t   native;
diff -r 1aa2bb15c966 -r fe94552843d7 test/shell_test_njs.exp
--- a/test/shell_test_njs.exp   Fri Mar 15 23:14:39 2024 -0700
+++ b/test/shell_test_njs.exp   Mon Mar 18 22:15:48 2024 -0700
@@ -46,23 +46,15 @@ njs_test {
      "a *= 2\r\n2\r\n>> "}
 }
 
-# Global completions, no
-# Disabled: readline does not support it in callback mode
-# njs_test {
-#    {"\t\tn"
-#     "\a\r\nDisplay all*possibilities? (y or n)*>> "}
-# }
+# Global completions
 
-# Global completions, yes
 njs_test {
-    {"\t\ty"
-     "\a\r\nDisplay all*possibilities? (y or n)*Array"}
+    {"\t\t"
+     "$262*"}
 }
 
 # Global completions, single partial match
 
-# \a* is WORKAROUND for libedit-20170329.3.1-r3
-# which inserts '\rESC[6G' after '\a'.
 njs_test {
     {"O\t"
      "O\a*bject"}
@@ -87,13 +79,13 @@ njs_test {
 njs_test {
     {"Type\t"
      "Type\a*Error"}
-    {"\t\t"
-     "TypeError.length"}
+    {".\t\t"
+     "TypeError.__proto__"}
 }
 
 njs_test {
     {"TypeError.\t\t"
-     "TypeError.length*TypeError.prototype"}
+     "TypeError.__proto__*TypeError.prototype"}
 }
 
 njs_test {
@@ -106,8 +98,8 @@ njs_test {
 njs_test {
     {"JS\t"
      "JS\a*ON"}
-    {"\t\t"
-     "JSON.parse*JSON.stringify"}
+    {".\t\t"
+     "JSON.__proto__*JSON.stringify"}
 }
 
 # Global completions, no matches
@@ -134,8 +126,6 @@ njs_test {
      "AA*AAA*"}
 }
 
-# z*z is WORKAROUND for libedit-20170329.3.1-r3
-# which inserts bogus '\a' between 'z'
 njs_test {
     {"var zz = 1\r\n"
      "var zz = 1\r\nundefined\r\n>> "}
_______________________________________________
nginx-devel mailing list
nginx-devel@nginx.org
https://mailman.nginx.org/mailman/listinfo/nginx-devel

Reply via email to