Implement completion for Ada attributes
authorTom Tromey <tromey@adacore.com>
Tue, 22 Feb 2022 18:18:01 +0000 (11:18 -0700)
committerTom Tromey <tromey@adacore.com>
Mon, 4 Apr 2022 18:46:09 +0000 (12:46 -0600)
This adds a completer for Ada attributes.  Some work in the lexer is
required in order to match end-of-input correctly, as flex does not
have a general-purpose way of doing this.  (The approach taken here is
recommended in the flex manual.)

gdb/ada-exp.y
gdb/ada-lex.l
gdb/parser-defs.h
gdb/testsuite/gdb.ada/formatted_ref.exp

index ebf3925b98c478f2b877e1deaf2a354422365fe7..204e77af6cac0db453a5fb1d82108e29b96b293d 100644 (file)
@@ -393,6 +393,30 @@ pop_associations (int n)
   return result;
 }
 
+/* Expression completer for attributes.  */
+struct ada_tick_completer : public expr_completion_base
+{
+  explicit ada_tick_completer (std::string &&name)
+    : m_name (std::move (name))
+  {
+  }
+
+  bool complete (struct expression *exp,
+                completion_tracker &tracker) override;
+
+private:
+
+  std::string m_name;
+};
+
+/* Make a new ada_tick_completer and wrap it in a unique pointer.  */
+static std::unique_ptr<expr_completion_base>
+make_tick_completer (struct stoken tok)
+{
+  return (std::unique_ptr<expr_completion_base>
+         (new ada_tick_completer (std::string (tok.ptr, tok.length))));
+}
+
 %}
 
 %union
@@ -420,7 +444,7 @@ pop_associations (int n)
 %token <typed_val_float> FLOAT
 %token TRUEKEYWORD FALSEKEYWORD
 %token COLONCOLON
-%token <sval> STRING NAME DOT_ID 
+%token <sval> STRING NAME DOT_ID TICK_COMPLETE
 %type <bval> block
 %type <lval> arglist tick_arglist
 
@@ -449,6 +473,7 @@ pop_associations (int n)
 %right TICK_ACCESS TICK_ADDRESS TICK_FIRST TICK_LAST TICK_LENGTH
 %right TICK_MAX TICK_MIN TICK_MODULUS
 %right TICK_POS TICK_RANGE TICK_SIZE TICK_TAG TICK_VAL
+%right TICK_COMPLETE
  /* The following are right-associative only so that reductions at this
     precedence have lower precedence than '.' and '('.  The syntax still
     forces a.b.c, e.g., to be LEFT-associated.  */
@@ -784,6 +809,10 @@ primary :  primary TICK_ACCESS
                        { ada_addrof (); }
        |       primary TICK_ADDRESS
                        { ada_addrof (type_system_address (pstate)); }
+       |       primary TICK_COMPLETE
+                       {
+                         pstate->mark_completion (make_tick_completer ($2));
+                       }
        |       primary TICK_FIRST tick_arglist
                        {
                          operation_up arg = ada_pop ();
index c6ce1aec53adec731213e2ecff009723d8f25522..6c32a9bd002e651215d8ff4adfdb1acf0aeb3ab9 100644 (file)
@@ -39,6 +39,11 @@ OPER    ([-+*/=<>&]|"<="|">="|"**"|"/="|"and"|"or"|"xor"|"not"|"mod"|"rem"|"abs"
 EXP    (e[+-]{NUM10})
 POSEXP  (e"+"?{NUM10})
 
+/* This must agree with COMPLETION_CHAR below.  See the comment there
+   for the explanation.  */
+COMPLETE "\001"
+NOT_COMPLETE [^\001]
+
 %{
 
 #include "diagnostics.h"
@@ -73,16 +78,35 @@ static void rewind_to_char (int);
    Defining YY_NO_INPUT comments it out.  */
 #define YY_NO_INPUT
 
+/* When completing, we'll return a special character at the end of the
+   input, to signal the completion position to the lexer.  This is
+   done because flex does not have a generally useful way to detect
+   EOF in a pattern.  This variable records whether the special
+   character has been emitted.  */
+static bool returned_complete = false;
+
+/* The character we use to represent the completion point.  */
+#define COMPLETE_CHAR '\001'
+
 #undef YY_INPUT
-#define YY_INPUT(BUF, RESULT, MAX_SIZE) \
-    if ( *pstate->lexptr == '\000' ) \
-      (RESULT) = YY_NULL; \
-    else \
-      { \
-        *(BUF) = *pstate->lexptr; \
-        (RESULT) = 1; \
-       pstate->lexptr += 1; \
-      }
+#define YY_INPUT(BUF, RESULT, MAX_SIZE)                                        \
+  if ( *pstate->lexptr == '\000' )                                     \
+    {                                                                  \
+      if (pstate->parse_completion && !returned_complete)              \
+       {                                                               \
+         returned_complete = true;                                     \
+         *(BUF) = COMPLETE_CHAR;                                       \
+         (RESULT) = 1;                                                 \
+       }                                                               \
+      else                                                             \
+       (RESULT) = YY_NULL;                                             \
+    }                                                                  \
+  else                                                                 \
+    {                                                                  \
+      *(BUF) = *pstate->lexptr == COMPLETE_CHAR ? ' ' : *pstate->lexptr; \
+      (RESULT) = 1;                                                    \
+      pstate->lexptr += 1;                                             \
+    }
 
 static int find_dot_all (const char *);
 
@@ -227,7 +251,7 @@ false               { return FALSEKEYWORD; }
 
         /* ATTRIBUTES */
 
-{TICK}[a-z][a-z_]+ { BEGIN INITIAL; return processAttribute (yytext); }
+{TICK}([a-z][a-z_]*)?{COMPLETE}? { BEGIN INITIAL; return processAttribute (yytext); }
 
        /* PUNCTUATION */
 
@@ -239,7 +263,7 @@ false               { return FALSEKEYWORD; }
 "<="           { return LEQ; }
 ">="           { return GEQ; }
 
-<BEFORE_QUAL_QUOTE>"'" { BEGIN INITIAL; return '\''; }
+<BEFORE_QUAL_QUOTE>"'"/{NOT_COMPLETE} { BEGIN INITIAL; return '\''; }
 
 [-&*+./:<>=|;\[\]] { return yytext[0]; }
 
@@ -320,6 +344,7 @@ lexer_init (FILE *inp)
 {
   BEGIN INITIAL;
   paren_depth = 0;
+  returned_complete = false;
   yyrestart (inp);
 }
 
@@ -668,6 +693,16 @@ processAttribute (const char *str)
   while (isspace (*str))
     ++str;
 
+  int len = strlen (str);
+  if (len > 0 && str[len - 1] == COMPLETE_CHAR)
+    {
+      /* This is enforced by YY_INPUT.  */
+      gdb_assert (pstate->parse_completion);
+      yylval.sval.ptr = obstack_strndup (&temp_parse_space, str, len - 1);
+      yylval.sval.length = len - 1;
+      return TICK_COMPLETE;
+    }
+
   for (const auto &item : attributes)
     if (strcasecmp (str, item.name) == 0)
       return item.code;
@@ -687,6 +722,20 @@ processAttribute (const char *str)
   return *found;
 }
 
+bool
+ada_tick_completer::complete (struct expression *exp,
+                             completion_tracker &tracker)
+{
+  completion_list output;
+  for (const auto &item : attributes)
+    {
+      if (strncasecmp (item.name, m_name.c_str (), m_name.length ()) == 0)
+       output.emplace_back (xstrdup (item.name));
+    }
+  tracker.add_completions (std::move (output));
+  return true;
+}
+
 /* Back up lexptr by yyleng and then to the rightmost occurrence of
    character CH, case-folded (there must be one).  WARNING: since
    lexptr points to the next input character that Flex has not yet
index 71381b1a7255493398bcc63b8d0536912bdf1931..3be7d6c839f6641b6e07dc9524dc7d8fdc1f66ae 100644 (file)
@@ -195,6 +195,14 @@ struct parser_state : public expr_builder
 
   void mark_completion_tag (enum type_code tag, const char *ptr, int length);
 
+  /* Mark for completion, using an arbitrary completer.  */
+
+  void mark_completion (std::unique_ptr<expr_completion_base> completer)
+  {
+    gdb_assert (m_completion_state == nullptr);
+    m_completion_state = std::move (completer);
+  }
+
   /* Push an operation on the stack.  */
   void push (expr::operation_up &&op)
   {
index 882dbf177251966c2fcd9ce4ab6b31a030b7d398..19a32658d9809dfe380948359eb697aebaf60eed 100644 (file)
@@ -82,6 +82,11 @@ proc test_p_x_addr { var addr } {
            }
        }
     }
+
+    gdb_test "complete print/x $var'unres" "print/x $var'unrestricted_access"
+    gdb_test_no_output "complete print/x $var'abcd"
+    gdb_test "complete print $var'f" "print $var'first"
+
     return 0
 }