Welcome! Log In Create A New Profile

Advanced

[njs] Shell: completions are refactored and moved out of the core.

Dmitry Volyntsev
March 19, 2024 02:00AM
details: https://hg.nginx.org/njs/rev/fe94552843d7
branches:
changeset: 2301:fe94552843d7
user: Dmitry Volyntsev <xeioex@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
Subject Author Views Posted

[njs] Shell: completions are refactored and moved out of the core.

Dmitry Volyntsev 101 March 19, 2024 02:00AM



Sorry, you do not have permission to post/reply in this forum.

Online Users

Guests: 144
Record Number of Users: 8 on April 13, 2023
Record Number of Guests: 421 on December 02, 2018
Powered by nginx      Powered by FreeBSD      PHP Powered      Powered by MariaDB      ipv6 ready