public inbox for gdb-cvs@sourceware.org
help / color / mirror / Atom feed
From: Tom Tromey <tromey@sourceware.org>
To: gdb-cvs@sourceware.org
Subject: [binutils-gdb] Implement completion for Ada attributes
Date: Mon,  4 Apr 2022 18:50:40 +0000 (GMT)	[thread overview]
Message-ID: <20220404185040.CAE72385840C@sourceware.org> (raw)

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=c66ed94ae961c19b0d3028489d00a2df5a949aac

commit c66ed94ae961c19b0d3028489d00a2df5a949aac
Author: Tom Tromey <tromey@adacore.com>
Date:   Tue Feb 22 11:18:01 2022 -0700

    Implement completion for Ada attributes
    
    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.)

Diff:
---
 gdb/ada-exp.y                           | 31 +++++++++++++-
 gdb/ada-lex.l                           | 71 ++++++++++++++++++++++++++++-----
 gdb/parser-defs.h                       |  8 ++++
 gdb/testsuite/gdb.ada/formatted_ref.exp |  5 +++
 4 files changed, 103 insertions(+), 12 deletions(-)

diff --git a/gdb/ada-exp.y b/gdb/ada-exp.y
index ebf3925b98c..204e77af6ca 100644
--- a/gdb/ada-exp.y
+++ b/gdb/ada-exp.y
@@ -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 ();
diff --git a/gdb/ada-lex.l b/gdb/ada-lex.l
index c6ce1aec53a..6c32a9bd002 100644
--- a/gdb/ada-lex.l
+++ b/gdb/ada-lex.l
@@ -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
diff --git a/gdb/parser-defs.h b/gdb/parser-defs.h
index 71381b1a725..3be7d6c839f 100644
--- a/gdb/parser-defs.h
+++ b/gdb/parser-defs.h
@@ -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)
   {
diff --git a/gdb/testsuite/gdb.ada/formatted_ref.exp b/gdb/testsuite/gdb.ada/formatted_ref.exp
index 882dbf17725..19a32658d98 100644
--- a/gdb/testsuite/gdb.ada/formatted_ref.exp
+++ b/gdb/testsuite/gdb.ada/formatted_ref.exp
@@ -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
 }


                 reply	other threads:[~2022-04-04 18:50 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220404185040.CAE72385840C@sourceware.org \
    --to=tromey@sourceware.org \
    --cc=gdb-cvs@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).