From: Tom Tromey Date: Tue, 22 Feb 2022 18:18:01 +0000 (-0700) Subject: Implement completion for Ada attributes X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=c66ed94ae961c19b0d3028489d00a2df5a949aac;p=binutils-gdb.git 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 --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 +make_tick_completer (struct stoken tok) +{ + return (std::unique_ptr + (new ada_tick_completer (std::string (tok.ptr, tok.length)))); +} + %} %union @@ -420,7 +444,7 @@ pop_associations (int n) %token FLOAT %token TRUEKEYWORD FALSEKEYWORD %token COLONCOLON -%token STRING NAME DOT_ID +%token STRING NAME DOT_ID TICK_COMPLETE %type block %type 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; } -"'" { BEGIN INITIAL; return '\''; } +"'"/{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 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 }