From 7ac44be52f4432c9c1699794055ed433dc291fe5 Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horiguchi.kyotaro@lab.ntt.co.jp>
Date: Mon, 28 Nov 2016 18:59:02 +0900
Subject: [PATCH 12/12] Allow CREATE RULE to use command completion recursively

A trial implement of CREATE RULE completing after DO.  This allows
commands not to use TailMatches. But seems to require more
consideration.
---
 src/bin/psql/tab-complete.c | 54 ++++++++++++++++++++++++++++++++++-----------
 1 file changed, 41 insertions(+), 13 deletions(-)

diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 7fc4965..7995a23 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1916,6 +1916,34 @@ psql_completion_internal(const char *text, char **previous_words,
 	if (TailMatches4("AS", "ON", "SELECT|UPDATE|INSERT|DELETE", "TO"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
 
+	/* Recursive completion for inner command of CREATE RULE */
+	if (HeadMatches2("CREATE", "RULE")){
+		int idx =
+			find_last_index_of("DO", previous_words, previous_words_count);
+		if (idx > 0)
+		{
+			COLLAPSE(1, idx); /* Start from "DO" */
+			if (Matches1("DO"))
+				COMPLETE_WITH_LIST8("ALSO", "INSTEAD", "NOTHING",
+									"SELECT", "INSERT", "UPDATE", "DELETE",
+									"NOTIFY");
+			HeadMatchAndRemove2(2, 1, "DO", "ALSO|INSTEAD");
+			if (Matches1("DO"))
+				COMPLETE_WITH_LIST6("NOTHING",
+									"SELECT", "INSERT", "UPDATE", "DELETE",
+									"NOTIFY");
+			idx =
+				find_last_index_of("SELECT|UPDATE|INSERT|DELETE",
+								   previous_words, previous_words_count);
+			if (idx > 0) {
+				COLLAPSE(1, idx);
+				return psql_completion_internal(text, previous_words,
+												previous_words_count);
+			}
+		}
+	}
+
+
 /* CREATE SEQUENCE --- is allowed inside CREATE SCHEMA */
 	HeadMatchAndRemove3(2, 1, "CREATE", "TEMP|TEMPORARY", "SEQUENCE");
 	if (Matches3("CREATE", "SEQUENCE", MatchAny))
@@ -2091,10 +2119,10 @@ psql_completion_internal(const char *text, char **previous_words,
 	if (Matches1("DELETE"))
 		COMPLETE_WITH_CONST("FROM");
 	/* Complete DELETE FROM with a list of tables */
-	if (TailMatches2("DELETE", "FROM"))
+	if (Matches2("DELETE", "FROM"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables);
 	/* Complete DELETE FROM <table> */
-	if (TailMatches3("DELETE", "FROM", MatchAny))
+	if (Matches3("DELETE", "FROM", MatchAny))
 		COMPLETE_WITH_LIST2("USING", "WHERE");
 	/* XXX: implement tab completion for DELETE ... USING */
 
@@ -2404,32 +2432,32 @@ psql_completion_internal(const char *text, char **previous_words,
 
 /* INSERT --- can be inside EXPLAIN, RULE, etc */
 	/* Complete INSERT with "INTO" */
-	if (TailMatches1("INSERT"))
+	if (Matches1("INSERT"))
 		COMPLETE_WITH_CONST("INTO");
 	/* Complete INSERT INTO with table names */
-	if (TailMatches2("INSERT", "INTO"))
+	if (Matches2("INSERT", "INTO"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables);
 	/* Complete "INSERT INTO <table> (" with attribute names */
-	if (TailMatches4("INSERT", "INTO", MatchAny, "("))
+	if (Matches4("INSERT", "INTO", MatchAny, "("))
 		COMPLETE_WITH_ATTR(prev2_wd);
 
 	/*
 	 * Complete INSERT INTO <table> with "(" or "VALUES" or "SELECT" or
 	 * "TABLE" or "DEFAULT VALUES"
 	 */
-	if (TailMatches3("INSERT", "INTO", MatchAny))
+	if (Matches3("INSERT", "INTO", MatchAny))
 		COMPLETE_WITH_LIST5("(", "DEFAULT VALUES", "SELECT", "TABLE", "VALUES");
 
 	/*
 	 * Complete INSERT INTO <table> (attribs) with "VALUES" or "SELECT" or
 	 * "TABLE"
 	 */
-	if (TailMatches4("INSERT", "INTO", MatchAny, MatchAny) &&
+	if (Matches4("INSERT", "INTO", MatchAny, MatchAny) &&
 			 ends_with(prev_wd, ')'))
 		COMPLETE_WITH_LIST3("SELECT", "TABLE", "VALUES");
 
 	/* Insert an open parenthesis after "VALUES" */
-	if (TailMatches1("VALUES") && !TailMatches2("DEFAULT", "VALUES"))
+	if (Matches1("VALUES") && !TailMatches2("DEFAULT", "VALUES"))
 		COMPLETE_WITH_CONST("(");
 
 /* LOCK */
@@ -2468,7 +2496,7 @@ psql_completion_internal(const char *text, char **previous_words,
 							"UPDATE EXCLUSIVE MODE");
 
 /* NOTIFY --- can be inside EXPLAIN, RULE, etc */
-	if (TailMatches1("NOTIFY"))
+	if (Matches1("NOTIFY"))
 		COMPLETE_WITH_QUERY("SELECT pg_catalog.quote_ident(channel) FROM pg_catalog.pg_listening_channels() AS channel WHERE substring(pg_catalog.quote_ident(channel),1,%d)='%s'");
 
 /* OPTIONS */
@@ -2701,16 +2729,16 @@ psql_completion_internal(const char *text, char **previous_words,
 
 /* UPDATE --- can be inside EXPLAIN, RULE, etc */
 	/* If prev. word is UPDATE suggest a list of tables */
-	if (TailMatches1("UPDATE"))
+	if (Matches1("UPDATE"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables);
 	/* Complete UPDATE <table> with "SET" */
-	if (TailMatches2("UPDATE", MatchAny))
+	if (Matches2("UPDATE", MatchAny))
 		COMPLETE_WITH_CONST("SET");
 	/* Complete UPDATE <table> SET with list of attributes */
-	if (TailMatches3("UPDATE", MatchAny, "SET"))
+	if (Matches3("UPDATE", MatchAny, "SET"))
 		COMPLETE_WITH_ATTR(prev2_wd);
 	/* UPDATE <table> SET <attr> = */
-	if (TailMatches4("UPDATE", MatchAny, "SET", MatchAny))
+	if (Matches4("UPDATE", MatchAny, "SET", MatchAny))
 		COMPLETE_WITH_CONST("=");
 
 /* USER MAPPING */
-- 
2.9.2

