From 3648a0ccf0aee97b328053fef6fa7c336d7ee56b Mon Sep 17 00:00:00 2001 From: Michael Paquier Date: Tue, 29 Dec 2015 23:07:27 +0900 Subject: [PATCH 2/2] Adopt more compact tab completion for backslash commands in psql This upgrades a bit the existing psql facility so as case-sensitive comparisons can be done, which is a requirement contrary to other query types that do not need to mind about that. --- src/bin/psql/tab-complete.c | 182 +++++++++++++++++++++++++------------------- 1 file changed, 104 insertions(+), 78 deletions(-) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index c920353..ffa1942 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -963,8 +963,15 @@ initialize_readline(void) #define MatchAny NULL #define MatchAnyExcept(pattern) ("!" pattern) +/* + * word_matches_internal + * Internal worker routine to check if a word matches a given pattern. Caller + * can optionally decide to make the check case-sensitive or not. + */ static bool -word_matches(const char *pattern, const char *word) +word_matches_internal(const char *pattern, + const char *word, + bool case_sensitive) { size_t wordlen; @@ -974,7 +981,7 @@ word_matches(const char *pattern, const char *word) /* Handle negated patterns from the MatchAnyExcept macro. */ if (*pattern == '!') - return !word_matches(pattern + 1, word); + return !word_matches_internal(pattern + 1, word, case_sensitive); /* Else consider each alternative in the pattern. */ wordlen = strlen(word); @@ -987,9 +994,13 @@ word_matches(const char *pattern, const char *word) while (*c != '\0' && *c != '|') c++; /* Match? */ - if (wordlen == (c - pattern) && - pg_strncasecmp(word, pattern, wordlen) == 0) - return true; + if (wordlen == (c - pattern)) + { + /* Do the pattern comparison, depending on the sensitiveness */ + if ((!case_sensitive && pg_strncasecmp(word, pattern, wordlen) == 0) || + (case_sensitive && strncmp(word, pattern, wordlen) == 0)) + return true; + } /* Out of alternatives? */ if (*c == '\0') break; @@ -1001,6 +1012,26 @@ word_matches(const char *pattern, const char *word) } /* + * word_matches + * Utility routine to match a word with a pattern as case-insensitive. + */ +static bool +word_matches(const char *pattern, const char *word) +{ + return word_matches_internal(pattern, word, false); +} + +/* + * word_case_matches + * Utility routine to match a word with a patterm as case-sensitive. + */ +static bool +word_case_matches(const char *pattern, const char *word) +{ + return word_matches_internal(pattern, word, true); +} + +/* * Check if the final character of 's' is 'c'. */ static bool @@ -1172,6 +1203,18 @@ psql_completion(const char *text, int start, int end) word_matches(p2, previous_words[previous_words_count - 2]) && \ word_matches(p3, previous_words[previous_words_count - 3])) + /* + * Macros for matching N words before point with case-sensitive + * comparison. + */ +#define CaseMatches1(p1) \ + (previous_words_count >= 1 && \ + word_case_matches(p1, prev_wd)) +#define CaseMatches2(p2, p1) \ + (previous_words_count >= 2 && \ + word_case_matches(p1, prev_wd) && \ + word_case_matches(p2, prev2_wd)) + /* Known command-starting keywords. */ static const char *const sql_commands[] = { "ABORT", "ALTER", "ANALYZE", "BEGIN", "CHECKPOINT", "CLOSE", "CLUSTER", @@ -2815,95 +2858,91 @@ psql_completion(const char *text, int start, int end) /* Backslash commands */ /* TODO: \dc \dd \dl */ - else if (strcmp(prev_wd, "\\?") == 0) + else if (CaseMatches1("\\?")) { static const char *const my_list[] = {"commands", "options", "variables", NULL}; COMPLETE_WITH_LIST_CS(my_list); } - else if (strcmp(prev_wd, "\\connect") == 0 || strcmp(prev_wd, "\\c") == 0) + else if (CaseMatches1("\\connect|\\c")) { if (!recognized_connection_string(text)) COMPLETE_WITH_QUERY(Query_for_list_of_databases); } - else if (previous_words_count >= 2 && - (strcmp(prev2_wd, "\\connect") == 0 || - strcmp(prev2_wd, "\\c") == 0)) + else if (CaseMatches2("\\connect|\\c", MatchAny)) { if (!recognized_connection_string(prev_wd)) COMPLETE_WITH_QUERY(Query_for_list_of_roles); } - else if (strncmp(prev_wd, "\\da", strlen("\\da")) == 0) + else if (CaseMatches1("\\da")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_aggregates, NULL); - else if (strncmp(prev_wd, "\\db", strlen("\\db")) == 0) + else if (CaseMatches1("\\db")) COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces); - else if (strncmp(prev_wd, "\\dD", strlen("\\dD")) == 0) + else if (CaseMatches1("\\dD")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL); - else if (strncmp(prev_wd, "\\des", strlen("\\des")) == 0) + else if (CaseMatches1("\\des")) COMPLETE_WITH_QUERY(Query_for_list_of_servers); - else if (strncmp(prev_wd, "\\deu", strlen("\\deu")) == 0) + else if (CaseMatches1("\\deu")) COMPLETE_WITH_QUERY(Query_for_list_of_user_mappings); - else if (strncmp(prev_wd, "\\dew", strlen("\\dew")) == 0) + else if (CaseMatches1("\\dew")) COMPLETE_WITH_QUERY(Query_for_list_of_fdws); - else if (strncmp(prev_wd, "\\df", strlen("\\df")) == 0) + else if (CaseMatches1("\\df")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL); - else if (strncmp(prev_wd, "\\dFd", strlen("\\dFd")) == 0) + else if (CaseMatches1("\\dFd")) COMPLETE_WITH_QUERY(Query_for_list_of_ts_dictionaries); - else if (strncmp(prev_wd, "\\dFp", strlen("\\dFp")) == 0) + else if (CaseMatches1("\\dFp")) COMPLETE_WITH_QUERY(Query_for_list_of_ts_parsers); - else if (strncmp(prev_wd, "\\dFt", strlen("\\dFt")) == 0) + else if (CaseMatches1("\\dFt")) COMPLETE_WITH_QUERY(Query_for_list_of_ts_templates); /* must be at end of \dF */ - else if (strncmp(prev_wd, "\\dF", strlen("\\dF")) == 0) + else if (CaseMatches1("\\dF")) COMPLETE_WITH_QUERY(Query_for_list_of_ts_configurations); - else if (strncmp(prev_wd, "\\di", strlen("\\di")) == 0) + else if (CaseMatches1("\\di")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL); - else if (strncmp(prev_wd, "\\dL", strlen("\\dL")) == 0) + else if (CaseMatches1("\\dL")) COMPLETE_WITH_QUERY(Query_for_list_of_languages); - else if (strncmp(prev_wd, "\\dn", strlen("\\dn")) == 0) + else if (CaseMatches1("\\dn")) COMPLETE_WITH_QUERY(Query_for_list_of_schemas); - else if (strncmp(prev_wd, "\\dp", strlen("\\dp")) == 0 - || strncmp(prev_wd, "\\z", strlen("\\z")) == 0) + else if (CaseMatches1("\\dp") || CaseMatches1("\\z")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, NULL); - else if (strncmp(prev_wd, "\\ds", strlen("\\ds")) == 0) + else if (CaseMatches1("\\ds")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL); - else if (strncmp(prev_wd, "\\dt", strlen("\\dt")) == 0) + else if (CaseMatches1("\\dt")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); - else if (strncmp(prev_wd, "\\dT", strlen("\\dT")) == 0) + else if (CaseMatches1("\\dT")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL); - else if (strncmp(prev_wd, "\\du", strlen("\\du")) == 0 - || (strncmp(prev_wd, "\\dg", strlen("\\dg")) == 0)) + else if (CaseMatches1("\\du") || CaseMatches1("\\dg")) COMPLETE_WITH_QUERY(Query_for_list_of_roles); - else if (strncmp(prev_wd, "\\dv", strlen("\\dv")) == 0) + else if (CaseMatches1("\\dv")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL); - else if (strncmp(prev_wd, "\\dx", strlen("\\dx")) == 0) + else if (CaseMatches1("\\dx")) COMPLETE_WITH_QUERY(Query_for_list_of_extensions); - else if (strncmp(prev_wd, "\\dm", strlen("\\dm")) == 0) + else if (CaseMatches1("\\dm")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL); - else if (strncmp(prev_wd, "\\dE", strlen("\\dE")) == 0) + else if (CaseMatches1("\\dE")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables, NULL); - else if (strncmp(prev_wd, "\\dy", strlen("\\dy")) == 0) + else if (CaseMatches1("\\dy")) COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers); /* must be at end of \d list */ - else if (strncmp(prev_wd, "\\d", strlen("\\d")) == 0) + else if (CaseMatches1("\\d")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_relations, NULL); - else if (strcmp(prev_wd, "\\ef") == 0) + else if (CaseMatches1("\\ef")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL); - else if (strcmp(prev_wd, "\\ev") == 0) + else if (CaseMatches1("\\ev")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL); - else if (strcmp(prev_wd, "\\encoding") == 0) + else if (CaseMatches1("\\encoding")) COMPLETE_WITH_QUERY(Query_for_list_of_encodings); - else if (strcmp(prev_wd, "\\h") == 0 || strcmp(prev_wd, "\\help") == 0) + else if (CaseMatches1("\\h") || CaseMatches1("\\help")) COMPLETE_WITH_LIST(sql_commands); - else if (strcmp(prev_wd, "\\password") == 0) + else if (CaseMatches1("\\password")) COMPLETE_WITH_QUERY(Query_for_list_of_roles); - else if (strcmp(prev_wd, "\\pset") == 0) + else if (CaseMatches1("\\pset")) { static const char *const my_list[] = {"border", "columns", "expanded", "fieldsep", "fieldsep_zero", @@ -2914,10 +2953,9 @@ psql_completion(const char *text, int start, int end) COMPLETE_WITH_LIST_CS(my_list); } - else if (previous_words_count >= 2 && - strcmp(prev2_wd, "\\pset") == 0) + else if (CaseMatches2("\\pset", MatchAny)) { - if (strcmp(prev_wd, "format") == 0) + if (CaseMatches1("format")) { static const char *const my_list[] = {"unaligned", "aligned", "wrapped", "html", "asciidoc", @@ -2925,16 +2963,14 @@ psql_completion(const char *text, int start, int end) COMPLETE_WITH_LIST_CS(my_list); } - else if (strcmp(prev_wd, "linestyle") == 0) + else if (CaseMatches1("linestyle")) { static const char *const my_list[] = {"ascii", "old-ascii", "unicode", NULL}; COMPLETE_WITH_LIST_CS(my_list); } - else if (strcmp(prev_wd, "unicode_border_linestyle") == 0 || - strcmp(prev_wd, "unicode_column_linestyle") == 0 || - strcmp(prev_wd, "unicode_header_linestyle") == 0) + else if (CaseMatches1("unicode_border_linestyle|unicode_column_linestyle|unicode_header_linestyle")) { static const char *const my_list[] = {"single", "double", NULL}; @@ -2943,73 +2979,72 @@ psql_completion(const char *text, int start, int end) } } - else if (strcmp(prev_wd, "\\unset") == 0) + else if (CaseMatches1("\\unset")) { matches = complete_from_variables(text, "", "", true); } - else if (strcmp(prev_wd, "\\set") == 0) + else if (CaseMatches1("\\set")) { matches = complete_from_variables(text, "", "", false); } - else if (previous_words_count >= 2 && - strcmp(prev2_wd, "\\set") == 0) + else if (CaseMatches2("\\set", MatchAny)) { static const char *const boolean_value_list[] = {"on", "off", NULL}; - if (strcmp(prev_wd, "AUTOCOMMIT") == 0) + if (CaseMatches1("AUTOCOMMIT")) COMPLETE_WITH_LIST_CS(boolean_value_list); - else if (strcmp(prev_wd, "COMP_KEYWORD_CASE") == 0) + else if (CaseMatches1("COMP_KEYWORD_CASE")) { static const char *const my_list[] = {"lower", "upper", "preserve-lower", "preserve-upper", NULL}; COMPLETE_WITH_LIST_CS(my_list); } - else if (strcmp(prev_wd, "ECHO") == 0) + else if (CaseMatches1("ECHO")) { static const char *const my_list[] = {"errors", "queries", "all", "none", NULL}; COMPLETE_WITH_LIST_CS(my_list); } - else if (strcmp(prev_wd, "ECHO_HIDDEN") == 0) + else if (CaseMatches1("ECHO_HIDDEN")) { static const char *const my_list[] = {"noexec", "off", "on", NULL}; COMPLETE_WITH_LIST_CS(my_list); } - else if (strcmp(prev_wd, "HISTCONTROL") == 0) + else if (CaseMatches1("HISTCONTROL")) { static const char *const my_list[] = {"ignorespace", "ignoredups", "ignoreboth", "none", NULL}; COMPLETE_WITH_LIST_CS(my_list); } - else if (strcmp(prev_wd, "ON_ERROR_ROLLBACK") == 0) + else if (CaseMatches1("ON_ERROR_ROLLBACK")) { static const char *const my_list[] = {"on", "off", "interactive", NULL}; COMPLETE_WITH_LIST_CS(my_list); } - else if (strcmp(prev_wd, "ON_ERROR_STOP") == 0) + else if (CaseMatches1("ON_ERROR_STOP")) COMPLETE_WITH_LIST_CS(boolean_value_list); - else if (strcmp(prev_wd, "QUIET") == 0) + else if (CaseMatches1("QUIET")) COMPLETE_WITH_LIST_CS(boolean_value_list); - else if (strcmp(prev_wd, "SHOW_CONTEXT") == 0) + else if (CaseMatches1("SHOW_CONTEXT")) { static const char *const my_list[] = {"never", "errors", "always", NULL}; COMPLETE_WITH_LIST_CS(my_list); } - else if (strcmp(prev_wd, "SINGLELINE") == 0) + else if (CaseMatches1("SINGLELINE")) COMPLETE_WITH_LIST_CS(boolean_value_list); - else if (strcmp(prev_wd, "SINGLESTEP") == 0) + else if (CaseMatches1("SINGLESTEP")) COMPLETE_WITH_LIST_CS(boolean_value_list); - else if (strcmp(prev_wd, "VERBOSITY") == 0) + else if (CaseMatches1("VERBOSITY")) { static const char *const my_list[] = {"default", "verbose", "terse", NULL}; @@ -3017,20 +3052,11 @@ psql_completion(const char *text, int start, int end) COMPLETE_WITH_LIST_CS(my_list); } } - else if (strcmp(prev_wd, "\\sf") == 0 || strcmp(prev_wd, "\\sf+") == 0) + else if (CaseMatches1("\\sf|\\sf+")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL); - else if (strcmp(prev_wd, "\\sv") == 0 || strcmp(prev_wd, "\\sv+") == 0) + else if (CaseMatches1("\\sv|\\sv+")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL); - else if (strcmp(prev_wd, "\\cd") == 0 || - strcmp(prev_wd, "\\e") == 0 || strcmp(prev_wd, "\\edit") == 0 || - strcmp(prev_wd, "\\g") == 0 || - strcmp(prev_wd, "\\i") == 0 || strcmp(prev_wd, "\\include") == 0 || - strcmp(prev_wd, "\\ir") == 0 || strcmp(prev_wd, "\\include_relative") == 0 || - strcmp(prev_wd, "\\o") == 0 || strcmp(prev_wd, "\\out") == 0 || - strcmp(prev_wd, "\\s") == 0 || - strcmp(prev_wd, "\\w") == 0 || strcmp(prev_wd, "\\write") == 0 || - strcmp(prev_wd, "\\lo_import") == 0 - ) + else if (CaseMatches1("\\cd|\\e|\\edit|\\g|\\i|\\include|\\ir|\\include_relative|\\o|\\out|\\s|\\w|\\write|\\lo_import")) { completion_charp = "\\"; matches = completion_matches(text, complete_from_files); -- 2.6.4