From f670fa5394206175d1ab04f9feec28af423d9f81 Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horiguchi.kyotaro@lab.ntt.co.jp>
Date: Wed, 14 Dec 2016 14:52:27 +0900
Subject: [PATCH 04/17] Add README for tab-completion

---
 src/bin/psql/README.completion | 169 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 169 insertions(+)
 create mode 100644 src/bin/psql/README.completion

diff --git a/src/bin/psql/README.completion b/src/bin/psql/README.completion
new file mode 100644
index 0000000..f3cefd0
--- /dev/null
+++ b/src/bin/psql/README.completion
@@ -0,0 +1,169 @@
+Word completion of interactive psql
+===================================
+
+psql supports word completion on interactive input. The core function
+of the feature is psql_completion_internal in tab-complete.c. A bunch
+of macros are provided in order to make it easier to read and maintain
+the completion code. psql_completion is called with reference console
+input stored in char ** previous_words in reverse order but developers
+don't need to be aware of the detail. Most of the operations can be
+described using the provided macros.
+
+Basic structure of the completion code
+--------------------------------------
+
+The main part of the function is just a series of completion
+definitions, where the first match wins. Each definition basically is
+in the following shape.
+
+   if (*matching expression*)
+      *enumeration of words for completion, then return*
+
+The matching expression is examined against previous_words which
+contains the whole command line before the last space. The completion
+code enumerates the expected words. For example, for "CREATE <tab>"
+the word list to be matched is ["CREATE"] and the prefix for
+completion is nothing. For "CREATE INDEX i", the list is ["CREATE",
+"INDEX"] and the prefix for enumeration is "i".
+
+
+Matching expression macros
+--------------------------
+There are four types of matching expression macros.
+
+- MatchesN(word1, word2 .. , wordN)
+
+ true iff the word list is exactly the same as the paremeter.
+
+- HeadMatchesN(word1, word2 .., wordN)
+
+ true iff the first N words in the word list matches the parameter.
+
+- TailMatchesN(word1, word2 .., wordN)
+
+ true iff the last N words in the word list matches the parameter.
+
+- MidMatchesN(pos, word1, word2 .., wordN)
+
+ true iff N successive words starts from pos in the word list matches
+ the parameter. The position is 1-based.
+
+Special matching words
+----------------------
+
+A defined symbol MatchAny matches any word. If you want to match any
+of several words, multiple words concatenated by '|' can be
+used. "CREATE|UPDATE" matches any of "CREATE" and "UPDATE".
+
+
+Enumeration macros
+-----------------
+There are N types of word enumeration macros.
+
+- COMPLETE_WITH_QUERY(query), COMPLETE_WITH_QUERY_KW(query, addon)
+
+  Suggests completion words acquired by using the given query. The
+  query details are seen in the comment for _complete_from_query().
+  Word matching is case-sensitive.
+
+  The latter form takes an additional parameter, which should be a
+  fragment of query starts with " UNION " followed by a query string
+  which gives some additional words. For case-insensitive suggestion,
+  this could be as simple as a static query string but for
+  case-sensitive cases of the letter form where the suggested word
+  case follows input, ADDLISTN() macro can be used.
+
+- COMPLETE_WITH_SCHEMA_QUERY(squery),
+  COMPLETE_WITH_SCHEMA_QUERY_KW(squery, addon)
+
+  Suggests words based on a "schema query", which is a struct that
+  containing parameters. You will see the details in the comment for
+  _complete_from_query(). Word maching is case-sensitive.
+
+  Just same as COMPLETE_WITH_QUERY_KW, the latter form takes a
+  fragment query same to that for COMPLETE_WITH_QUERY_KW.
+
+- COMPLETE_WITH_LIST_CS(list)
+
+  Suggests completion words given as a string array. Word matching is
+  case-sensitive.
+
+- COMPLETE_WITH_LIST_CSN(s1, s2.. ,sN)
+
+  Shortcut for COMPLETE_WITH_LIST_CS.
+
+- COMPLETE_WITH_LIST(list)
+
+  Same as COMPLETE_WITH_LIST_CS except that word matching is
+  case-insensitive and the letter case of suggested words is
+  determined according to COMP_KEYWORD_CASE.
+
+- COMPLETE_WITH_LISTN(s1, s2.. ,sN)
+
+  Shortcut for COMPLETE_WITH_LIST.
+
+- COMPLETE_WITH_CONST(string)
+
+  Same as COMPLETE_WITH_LIST but with just one suggestion.
+
+- COMPLETE_WITH_ATTR(relation), COMPLETE_WITH_ATTR_KW(relation, addon)
+
+  Suggests attribute names for the given relation. Word matching is
+  case-sensitve.
+
+- COMPLETE_WITH_FUNCTION_ARG(function)
+
+  Suggests function name for the given SQL function. Word matching is
+  case-sensitve.
+
+- COMPLETE_THING(relpos), COMPLETE_THING_KW(relpos, addon)
+
+  Suggests any object name designated by the word at relpos from the
+  current word. COMPLETE_THING(-1) for "... TABLE " enumerates names
+  of all available tables.
+
+Additional keywords for COMPLETE_WITH(_SCHEMA)_QUERY
+----------------------------------------------------
+
+Some syntaxes need suggestion by mixture of object names and
+keywords. Object names are enumarated with
+COMPLETE_WITH(_SCHEMA)_QUERY but keywords should be added manually
+onto them. COMPLETE_WITH(_SCHEMA)_QUERY_KW takes a fragment query that
+gives keywords to be suggested with the object names. Since the
+framgent queriy is just appended to the main query so it is in the
+form of ' UNION <any sql>' that adds arbitrary number of tuples
+contain a text value. For example,
+
+" UNION ALL SELECT 'CURRENT_USER' UNION ALL SELECT 'PUBLIC'"
+
+If you want the case of the keywords to follow input, ADDLISTN() macro
+provides a fragment query containing such keywords.
+
+ADDLIST3("CURRENT_USER", "PUBLIC", "USER") for input 'c' returns a
+fragment query equivalent to the following,
+
+" UNION ALL SELECT 'current_user' UNION ALL SELECT 'public'"
+
+
+Removing "NOISE" words
+----------------------------------------------------
+
+Many syntaxes has "NOISE" words, that is, words that has no effect on
+the following completion. For example, the existence of the second
+word in "CREATE UNIQUE INDEX" makes no difference for further
+completion behavior. Removing such words makes further completion code
+simpler.
+
+COLLAPSE(s, l) removes l words starts from s from previous_words.
+
+if (Matches("CREATE", "UNIQUE", "INDEX"))
+   COLLAPSE(2, 1);
+
+After the above code, the previous_words is ["CREATE", "INDEX"] so the
+following completion definitions need not care about the removed words.
+
+COLLAPSE is used after HeadMatches for most cases. HeadMatchAndRemoveN()
+macros are the composite macros. They are equivalents to the following
+and returns true if the condition meets.
+
+HeadMatchAndRemoveN() === if (HeadMatchesN(s, l, p1, ...)) COLLAPSE(s, l);
-- 
2.9.2

