Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

interactive shell: autocompletion list corruption #23

Closed
drsm opened this issue Jun 25, 2018 · 2 comments
Closed

interactive shell: autocompletion list corruption #23

drsm opened this issue Jun 25, 2018 · 2 comments
Assignees
Labels

Comments

@drsm
Copy link
Contributor

drsm commented Jun 25, 2018

the autocompletion list is corrupted after the value of any property of any global object was accessed.

$ njs
interactive njs 0.2.2

v.<Tab> -> the properties and prototype methods of v.
type console.help() for more information

>> n
native      new         njs         njs.version null       
>> njs.version
0.2.2
>> njs
njs         njs         njs.version
>> njs.
njs.__proto__      njs.constructor    njs.hasOwnProperty njs.isPrototypeOf  njs.toString       njs.valueOf        njs.version        njs.version       
>> Math.
Math.__proto__ Math.asin      Math.atanh     Math.cos       Math.expm1     Math.imul      Math.log10     Math.LOG2E     Math.pow       Math.sin       Math.SQRT2    
Math.abs       Math.asinh     Math.cbrt      Math.cosh      Math.floor     Math.LN10      Math.LOG10E    Math.max       Math.random    Math.sinh      Math.tan      
Math.acos      Math.atan      Math.ceil      Math.E         Math.fround    Math.LN2       Math.log1p     Math.min       Math.round     Math.sqrt      Math.tanh     
Math.acosh     Math.atan2     Math.clz32     Math.exp       Math.hypot     Math.log       Math.log2      Math.PI        Math.sign      Math.SQRT1_2   Math.trunc    
>> Math.PI
3.14159
>> Math.
Math.__proto__      Math.atan           Math.constructor    Math.floor          Math.LN2            Math.LOG2E          Math.round          Math.SQRT2         
Math.__proto__      Math.atan           Math.cos            Math.fround         Math.log            Math.max            Math.sign           Math.tan           
Math.abs            Math.atan2          Math.cos            Math.fround         Math.log            Math.max            Math.sign           Math.tan           
Math.abs            Math.atan2          Math.cosh           Math.hasOwnProperty Math.log10          Math.min            Math.sin            Math.tanh          
Math.acos           Math.atanh          Math.cosh           Math.hypot          Math.log10          Math.min            Math.sin            Math.tanh          
Math.acos           Math.atanh          Math.E              Math.hypot          Math.LOG10E         Math.PI             Math.sinh           Math.toString      
Math.acosh          Math.cbrt           Math.E              Math.imul           Math.LOG10E         Math.PI             Math.sinh           Math.trunc         
Math.acosh          Math.cbrt           Math.exp            Math.imul           Math.log1p          Math.pow            Math.sqrt           Math.trunc         
Math.asin           Math.ceil           Math.exp            Math.isPrototypeOf  Math.log1p          Math.pow            Math.sqrt           Math.valueOf       
Math.asin           Math.ceil           Math.expm1          Math.LN10           Math.log2           Math.random         Math.SQRT1_2       
Math.asinh          Math.clz32          Math.expm1          Math.LN10           Math.log2           Math.random         Math.SQRT1_2       
Math.asinh          Math.clz32          Math.floor          Math.LN2            Math.LOG2E          Math.round          Math.SQRT2         
>> Math.
@xeioex xeioex added the bug label Jun 26, 2018
@xeioex xeioex self-assigned this Jun 27, 2018
@xeioex
Copy link
Contributor

xeioex commented Jun 28, 2018

It still has a minor issue, that global objects like njs, Math, JSON can appear 2 times in the completion list. I am going to leave this as is, because it would require much more refactoring.

# HG changeset patch
# User Dmitry Volyntsev <xeioex@nginx.com>
# Date 1530119410 -10800
#      Wed Jun 27 20:10:10 2018 +0300
# Node ID 805d38c203bd44a4eaa2bd4b5c6d9bb6318989fc
# Parent  f7fe7dcab2f51ec0551640f0ef35a7777776fe32
Fixed autocompletion for global objects.

This fixes #23 issue on GitHub.

diff --git a/njs/njs_shell.c b/njs/njs_shell.c
--- a/njs/njs_shell.c
+++ b/njs/njs_shell.c
@@ -20,8 +20,9 @@
 
 
 typedef enum {
-    NJS_COMPLETION_GLOBAL = 0,
+    NJS_COMPLETION_VAR = 0,
     NJS_COMPLETION_SUFFIX,
+    NJS_COMPLETION_GLOBAL
 } njs_completion_phase_t;
 
 
@@ -476,6 +477,11 @@ njs_completion_handler(const char *text,
 
 #define njs_completion(c, i) &(((nxt_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)
 {
@@ -489,14 +495,108 @@ njs_completion_generator(const char *tex
     cmpl = &njs_completion;
 
     if (state == 0) {
+        cmpl->phase = 0;
         cmpl->index = 0;
         cmpl->length = strlen(text);
-        cmpl->phase = NJS_COMPLETION_GLOBAL;
+        cmpl->suffix_completions = NULL;
 
         nxt_lvlhsh_each_init(&cmpl->lhe, &njs_variables_hash_proto);
     }
 
-    if (cmpl->phase == NJS_COMPLETION_GLOBAL) {
+next:
+
+    switch (cmpl->phase) {
+    case NJS_COMPLETION_VAR:
+        if (cmpl->vm->parser == NULL) {
+            njs_next_phase(cmpl);
+        }
+
+        for ( ;; ) {
+            var = nxt_lvlhsh_each(&cmpl->vm->parser->scope->variables,
+                                  &cmpl->lhe);
+
+            if (var == NULL) {
+                break;
+            }
+
+            if (var->name.length < cmpl->length) {
+                continue;
+            }
+
+            if (strncmp(text, (char *) var->name.start, cmpl->length) == 0) {
+                return njs_editline(&var->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;
+
+            cmpl->suffix_completions = njs_vm_completions(cmpl->vm,
+                                                          &expression);
+            if (cmpl->suffix_completions == NULL) {
+                njs_next_phase(cmpl);
+            }
+        }
+
+        /* 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);
+            }
+
+            suffix = njs_completion(cmpl->suffix_completions, cmpl->index++);
+
+            if (len != 0 && strncmp((char *) suffix->start, p,
+                                    nxt_min(len, suffix->length)) != 0)
+            {
+                continue;
+            }
+
+            len = suffix->length + (p - text) + 1;
+            completion = malloc(len);
+            if (completion == NULL) {
+                return NULL;
+            }
+
+            snprintf(completion, len, "%.*s%.*s", (int) (p - text), text,
+                     (int) suffix->length, suffix->start);
+            return completion;
+        }
+
+    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;
@@ -508,88 +608,10 @@ njs_completion_generator(const char *tex
                 continue;
             }
 
-            if (strncmp(text, (char *) suffix->start,
-                        nxt_min(suffix->length, cmpl->length)) == 0)
-            {
+            if (strncmp(text, (char *) suffix->start, cmpl->length) == 0) {
                 return njs_editline(suffix);
             }
         }
-
-        if (cmpl->vm->parser != NULL) {
-            for ( ;; ) {
-                var = nxt_lvlhsh_each(&cmpl->vm->parser->scope->variables,
-                                      &cmpl->lhe);
-                if (var == NULL || var->name.length < cmpl->length) {
-                    break;
-                }
-
-                if (strncmp(text, (char *) var->name.start,
-                            nxt_min(var->name.length, cmpl->length)) == 0)
-                {
-                    return njs_editline(&var->name);
-                }
-            }
-        }
-
-        if (cmpl->length == 0) {
-            return NULL;
-        }
-
-        /* Getting the longest prefix before a '.' */
-
-        p = &text[cmpl->length - 1];
-        while (p > text && *p != '.') { p--; }
-
-        if (*p != '.') {
-            return NULL;
-        }
-
-        expression.start = (u_char *) text;
-        expression.length = p - text;
-
-        cmpl->suffix_completions = njs_vm_completions(cmpl->vm, &expression);
-        if (cmpl->suffix_completions == NULL) {
-            return NULL;
-        }
-
-        cmpl->index = 0;
-        cmpl->phase = NJS_COMPLETION_SUFFIX;
-    }
-
-    /* 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) {
-            break;
-        }
-
-        suffix = njs_completion(cmpl->suffix_completions, cmpl->index++);
-
-        if (len != 0 && strncmp((char *) suffix->start, p,
-                                nxt_min(len, suffix->length)) != 0)
-        {
-            continue;
-        }
-
-        len = suffix->length + (p - text) + 1;
-        completion = malloc(len);
-        if (completion == NULL) {
-            return NULL;
-        }
-
-        snprintf(completion, len, "%.*s%.*s", (int) (p - text), text,
-                 (int) suffix->length, suffix->start);
-        return completion;
     }
 
     return NULL;

@drsm
Copy link
Contributor Author

drsm commented Jun 30, 2018

@xeioex the patch works mostly fine, the most annoying problem is resolved.
also, i've noticed that methods of Object.prototype has been added to the list only after an any property was used.

>> Math.
Math.__proto__ Math.asin      Math.atanh     Math.cos       Math.expm1     Math.imul      Math.log10     Math.LOG2E     Math.pow       Math.sin       Math.SQRT2    
Math.abs       Math.asinh     Math.cbrt      Math.cosh      Math.floor     Math.LN10      Math.LOG10E    Math.max       Math.random    Math.sinh      Math.tan      
Math.acos      Math.atan      Math.ceil      Math.E         Math.fround    Math.LN2       Math.log1p     Math.min       Math.round     Math.sqrt      Math.tanh     
Math.acosh     Math.atan2     Math.clz32     Math.exp       Math.hypot     Math.log       Math.log2      Math.PI        Math.sign      Math.SQRT1_2   Math.trunc    
>> Math.PI
3.14159
>> Math.
Math.__proto__      Math.atan2          Math.cosh           Math.hypot          Math.LOG10E         Math.pow            Math.SQRT1_2       
Math.abs            Math.atanh          Math.E              Math.imul           Math.log1p          Math.random         Math.SQRT2         
Math.acos           Math.cbrt           Math.exp            Math.isPrototypeOf  Math.log2           Math.round          Math.tan           
Math.acosh          Math.ceil           Math.expm1          Math.LN10           Math.LOG2E          Math.sign           Math.tanh          
Math.asin           Math.clz32          Math.floor          Math.LN2            Math.max            Math.sin            Math.toString      
Math.asinh          Math.constructor    Math.fround         Math.log            Math.min            Math.sinh           Math.trunc         
Math.atan           Math.cos            Math.hasOwnProperty Math.log10          Math.PI             Math.sqrt           Math.valueOf       
>> Math.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants