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