psql: Case preserving completion of SQL key words
authorPeter Eisentraut <peter_e@gmx.net>
Wed, 1 Feb 2012 18:16:40 +0000 (20:16 +0200)
committerPeter Eisentraut <peter_e@gmx.net>
Wed, 1 Feb 2012 18:18:32 +0000 (20:18 +0200)
Instead of always completing SQL key words in upper case, look at the
word being completed and match the case.

reviewed by Fujii Masao

src/bin/psql/tab-complete.c

index b0b318f426fb0db576e1264ec5e354522d89a965..3854f7f421fc853a60ce538f6e3f2f670f454021 100644 (file)
@@ -132,6 +132,7 @@ static const char *const * completion_charpp;       /* to pass a list of strings */
 static const char *completion_info_charp;              /* to pass a second string */
 static const char *completion_info_charp2;             /* to pass a third string */
 static const SchemaQuery *completion_squery;   /* to pass a SchemaQuery */
+static bool completion_case_sensitive;                 /* completion is case sensitive */
 
 /*
  * A few macros to ease typing. You can use these to complete the given
@@ -155,15 +156,24 @@ do { \
        matches = completion_matches(text, complete_from_schema_query); \
 } while (0)
 
+#define COMPLETE_WITH_LIST_CS(list) \
+do { \
+       completion_charpp = list; \
+       completion_case_sensitive = true; \
+       matches = completion_matches(text, complete_from_list); \
+} while (0)
+
 #define COMPLETE_WITH_LIST(list) \
 do { \
        completion_charpp = list; \
+       completion_case_sensitive = false; \
        matches = completion_matches(text, complete_from_list); \
 } while (0)
 
 #define COMPLETE_WITH_CONST(string) \
 do { \
        completion_charp = string; \
+       completion_case_sensitive = false; \
        matches = completion_matches(text, complete_from_const); \
 } while (0)
 
@@ -671,6 +681,7 @@ static char *complete_from_const(const char *text, int state);
 static char **complete_from_variables(char *text,
                                                const char *prefix, const char *suffix);
 
+static char *pg_strdup_same_case(const char *s, const char *ref);
 static PGresult *exec_query(const char *query);
 
 static void get_previous_words(int point, char **previous_words, int nwords);
@@ -771,7 +782,7 @@ psql_completion(char *text, int start, int end)
 
        /* If a backslash command was started, continue */
        if (text[0] == '\\')
-               COMPLETE_WITH_LIST(backslash_commands);
+               COMPLETE_WITH_LIST_CS(backslash_commands);
 
        /* Variable interpolation */
        else if (text[0] == ':' && text[1] != ':')
@@ -2907,7 +2918,7 @@ psql_completion(char *text, int start, int end)
                        "null", "fieldsep", "tuples_only", "title", "tableattr",
                "linestyle", "pager", "recordsep", NULL};
 
-               COMPLETE_WITH_LIST(my_list);
+               COMPLETE_WITH_LIST_CS(my_list);
        }
        else if (strcmp(prev2_wd, "\\pset") == 0)
        {
@@ -2917,14 +2928,14 @@ psql_completion(char *text, int start, int end)
                        {"unaligned", "aligned", "wrapped", "html", "latex",
                        "troff-ms", NULL};
 
-                       COMPLETE_WITH_LIST(my_list);
+                       COMPLETE_WITH_LIST_CS(my_list);
                }
                else if (strcmp(prev_wd, "linestyle") == 0)
                {
                        static const char *const my_list[] =
                        {"ascii", "old-ascii", "unicode", NULL};
 
-                       COMPLETE_WITH_LIST(my_list);
+                       COMPLETE_WITH_LIST_CS(my_list);
                }
        }
        else if (strcmp(prev_wd, "\\set") == 0)
@@ -3030,7 +3041,7 @@ create_or_drop_command_generator(const char *text, int state, bits32 excluded)
        {
                if ((pg_strncasecmp(name, text, string_length) == 0) &&
                        !(words_after_create[list_index - 1].flags & excluded))
-                       return pg_strdup(name);
+                       return pg_strdup_same_case(name, text);
        }
        /* if nothing matches, return NULL */
        return NULL;
@@ -3298,7 +3309,7 @@ complete_from_list(const char *text, int state)
        {
                list_index = 0;
                string_length = strlen(text);
-               casesensitive = true;
+               casesensitive = completion_case_sensitive;
                matches = 0;
        }
 
@@ -3313,7 +3324,14 @@ complete_from_list(const char *text, int state)
 
                /* Second pass is case insensitive, don't bother counting matches */
                if (!casesensitive && pg_strncasecmp(text, item, string_length) == 0)
-                       return pg_strdup(item);
+               {
+                       if (completion_case_sensitive)
+                               return pg_strdup(item);
+                       else
+                               /* If case insensitive matching was requested initially, return
+                                * it in the case of what was already entered. */
+                               return pg_strdup_same_case(item, text);
+               }
        }
 
        /*
@@ -3343,12 +3361,16 @@ complete_from_list(const char *text, int state)
 static char *
 complete_from_const(const char *text, int state)
 {
-       (void) text;                            /* We don't care about what was entered
-                                                                * already. */
-
        psql_assert(completion_charp);
        if (state == 0)
-               return pg_strdup(completion_charp);
+       {
+               if (completion_case_sensitive)
+                       return pg_strdup(completion_charp);
+               else
+                       /* If case insensitive matching was requested initially, return it
+                        * in the case of what was already entered. */
+                       return pg_strdup_same_case(completion_charp, text);
+       }
        else
                return NULL;
 }
@@ -3394,7 +3416,7 @@ complete_from_variables(char *text, const char *prefix, const char *suffix)
        }
 
        varnames[nvars] = NULL;
-       COMPLETE_WITH_LIST((const char * const *) varnames);
+       COMPLETE_WITH_LIST_CS((const char * const *) varnames);
 
        for (i = 0; i < nvars; i++)
                free(varnames[i]);
@@ -3407,6 +3429,31 @@ complete_from_variables(char *text, const char *prefix, const char *suffix)
 /* HELPER FUNCTIONS */
 
 
+/*
+ * Make a pg_strdup copy of s and convert it to the same case as ref.
+ */
+static char *
+pg_strdup_same_case(const char *s, const char *ref)
+{
+       char *ret, *p;
+       unsigned char first = ref[0];
+
+       if (isalpha(first))
+       {
+               ret = pg_strdup(s);
+               if (islower(first))
+                       for (p = ret; *p; p++)
+                               *p = pg_tolower((unsigned char) *p);
+               else
+                       for (p = ret; *p; p++)
+                               *p = pg_toupper((unsigned char) *p);
+               return ret;
+       }
+       else
+               return pg_strdup(s);
+}
+
+
 /*
  * Execute a query and report any errors. This should be the preferred way of
  * talking to the database in this file.