v9-0002-psql-Add-PartialMatches-macro-for-better-tab-complet.patch
text/plain
Filename: v9-0002-psql-Add-PartialMatches-macro-for-better-tab-complet.patch
Type: text/plain
Part: 1
Patch
Format: format-patch
Series: patch v9-0002
Subject: psql: Add PartialMatches() macro for better tab-completion.
| File | + | − |
|---|---|---|
| src/bin/psql/tab-complete.c | 113 | 39 |
From 8c7e25e4a2d939a32751bd9a0d487c510ec66191 Mon Sep 17 00:00:00 2001
From: Fujii Masao <fujii@postgresql.org>
Date: Wed, 21 Sep 2022 13:58:50 +0900
Subject: [PATCH 2/2] psql: Add PartialMatches() macro for better
tab-completion.
---
src/bin/psql/tab-complete.c | 152 +++++++++++++++++++++++++++---------
1 file changed, 113 insertions(+), 39 deletions(-)
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 820f47d23a..0b8c252615 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1582,6 +1582,65 @@ HeadMatchesImpl(bool case_sensitive,
return true;
}
+/*
+ * Implementation of PartialMatches and PartialMatchesCS macros: do parts of
+ * the words in previous_words match the variadic arguments?
+ */
+static bool
+PartialMatchesImpl(bool case_sensitive,
+ int previous_words_count, char **previous_words,
+ int narg,...)
+{
+ va_list args;
+ char *firstarg = NULL;
+
+ if (previous_words_count < narg)
+ return false;
+
+ for (int startpos = 0; startpos < previous_words_count; startpos++)
+ {
+ int argno;
+
+ if (firstarg == NULL)
+ {
+ va_start(args, narg);
+ firstarg = va_arg(args, char *);
+ }
+
+ if (!word_matches(firstarg,
+ previous_words[previous_words_count - startpos - 1],
+ case_sensitive))
+ continue;
+
+ if (previous_words_count - startpos < narg)
+ {
+ va_end(args);
+ return false;
+ }
+
+ for (argno = 1; argno < narg; argno++)
+ {
+ const char *arg = va_arg(args, const char *);
+
+ if (!word_matches(arg,
+ previous_words[previous_words_count - argno - startpos - 1],
+ case_sensitive))
+ break;
+ }
+
+ va_end(args);
+ firstarg = NULL;
+
+ if (argno == narg)
+ return true;
+ }
+
+ if (firstarg != NULL)
+ va_end(args);
+
+ return false;
+}
+
/*
* Check if the final character of 's' is 'c'.
*/
@@ -1663,6 +1722,16 @@ psql_completion(const char *text, int start, int end)
HeadMatchesImpl(true, previous_words_count, previous_words, \
VA_ARGS_NARGS(__VA_ARGS__), __VA_ARGS__)
+ /* Match N words on the line partially, case-insensitively. */
+#define PartialMatches(...) \
+ PartialMatchesImpl(false, previous_words_count, previous_words, \
+ VA_ARGS_NARGS(__VA_ARGS__), __VA_ARGS__)
+
+ /* Match N words on the line partially, case-sensitively. */
+#define PartialMatchesCS(...) \
+ PartialMatchesImpl(true, previous_words_count, previous_words, \
+ VA_ARGS_NARGS(__VA_ARGS__), __VA_ARGS__)
+
/* Known command-starting keywords. */
static const char *const sql_commands[] = {
"ABORT", "ALTER", "ANALYZE", "BEGIN", "CALL", "CHECKPOINT", "CLOSE", "CLUSTER",
@@ -4108,46 +4177,51 @@ psql_completion(const char *text, int start, int end)
TailMatches("MERGE", "INTO", MatchAny, MatchAny, "USING", MatchAny, MatchAnyExcept("ON|AS")))
COMPLETE_WITH("ON");
- /* Complete MERGE INTO ... ON with target table attributes */
- else if (TailMatches("INTO", MatchAny, "USING", MatchAny, "ON"))
- COMPLETE_WITH_ATTR(prev4_wd);
- else if (TailMatches("INTO", MatchAny, "AS", MatchAny, "USING", MatchAny, "AS", MatchAny, "ON"))
- COMPLETE_WITH_ATTR(prev8_wd);
- else if (TailMatches("INTO", MatchAny, MatchAny, "USING", MatchAny, MatchAny, "ON"))
- COMPLETE_WITH_ATTR(prev6_wd);
+ else if (PartialMatches("MERGE", "INTO", MatchAny, "USING") ||
+ PartialMatches("MERGE", "INTO", MatchAny, "AS", MatchAny, "USING") ||
+ PartialMatches("MERGE", "INTO", MatchAny, MatchAny, "USING"))
+ {
+ /* Complete MERGE INTO ... ON with target table attributes */
+ if (TailMatches("INTO", MatchAny, "USING", MatchAny, "ON"))
+ COMPLETE_WITH_ATTR(prev4_wd);
+ else if (TailMatches("INTO", MatchAny, "AS", MatchAny, "USING", MatchAny, "AS", MatchAny, "ON"))
+ COMPLETE_WITH_ATTR(prev8_wd);
+ else if (TailMatches("INTO", MatchAny, MatchAny, "USING", MatchAny, MatchAny, "ON"))
+ COMPLETE_WITH_ATTR(prev6_wd);
- /*
- * Complete ... USING <relation> [[AS] alias] ON join condition
- * (consisting of one or three words typically used) with WHEN [NOT]
- * MATCHED
- */
- else if (TailMatches("USING", MatchAny, "ON", MatchAny) ||
- TailMatches("USING", MatchAny, "AS", MatchAny, "ON", MatchAny) ||
- TailMatches("USING", MatchAny, MatchAny, "ON", MatchAny) ||
- TailMatches("USING", MatchAny, "ON", MatchAny, MatchAnyExcept("WHEN"), MatchAnyExcept("WHEN")) ||
- TailMatches("USING", MatchAny, "AS", MatchAny, "ON", MatchAny, MatchAnyExcept("WHEN"), MatchAnyExcept("WHEN")) ||
- TailMatches("USING", MatchAny, MatchAny, "ON", MatchAny, MatchAnyExcept("WHEN"), MatchAnyExcept("WHEN")))
- COMPLETE_WITH("WHEN MATCHED", "WHEN NOT MATCHED");
- else if (TailMatches("USING", MatchAny, "ON", MatchAny, "WHEN") ||
- TailMatches("USING", MatchAny, "AS", MatchAny, "ON", MatchAny, "WHEN") ||
- TailMatches("USING", MatchAny, MatchAny, "ON", MatchAny, "WHEN") ||
- TailMatches("USING", MatchAny, "ON", MatchAny, MatchAny, MatchAny, "WHEN") ||
- TailMatches("USING", MatchAny, "AS", MatchAny, "ON", MatchAny, MatchAny, MatchAny, "WHEN") ||
- TailMatches("USING", MatchAny, MatchAny, "ON", MatchAny, MatchAny, MatchAny, "WHEN"))
- COMPLETE_WITH("MATCHED", "NOT MATCHED");
-
- /* Complete ... WHEN [NOT] MATCHED with THEN/AND */
- else if (TailMatches("WHEN", "MATCHED") ||
- TailMatches("WHEN", "NOT", "MATCHED"))
- COMPLETE_WITH("THEN", "AND");
-
- /* Complete ... WHEN MATCHED THEN with UPDATE SET/DELETE/DO NOTHING */
- else if (TailMatches("WHEN", "MATCHED", "THEN"))
- COMPLETE_WITH("UPDATE SET", "DELETE", "DO NOTHING");
-
- /* Complete ... WHEN NOT MATCHED THEN with INSERT/DO NOTHING */
- else if (TailMatches("WHEN", "NOT", "MATCHED", "THEN"))
- COMPLETE_WITH("INSERT", "DO NOTHING");
+ /*
+ * Complete ... USING <relation> [[AS] alias] ON join condition
+ * (consisting of one or three words typically used) with WHEN [NOT]
+ * MATCHED
+ */
+ else if (TailMatches("USING", MatchAny, "ON", MatchAny) ||
+ TailMatches("USING", MatchAny, "AS", MatchAny, "ON", MatchAny) ||
+ TailMatches("USING", MatchAny, MatchAny, "ON", MatchAny) ||
+ TailMatches("USING", MatchAny, "ON", MatchAny, MatchAnyExcept("WHEN"), MatchAnyExcept("WHEN")) ||
+ TailMatches("USING", MatchAny, "AS", MatchAny, "ON", MatchAny, MatchAnyExcept("WHEN"), MatchAnyExcept("WHEN")) ||
+ TailMatches("USING", MatchAny, MatchAny, "ON", MatchAny, MatchAnyExcept("WHEN"), MatchAnyExcept("WHEN")))
+ COMPLETE_WITH("WHEN MATCHED", "WHEN NOT MATCHED");
+ else if (TailMatches("USING", MatchAny, "ON", MatchAny, "WHEN") ||
+ TailMatches("USING", MatchAny, "AS", MatchAny, "ON", MatchAny, "WHEN") ||
+ TailMatches("USING", MatchAny, MatchAny, "ON", MatchAny, "WHEN") ||
+ TailMatches("USING", MatchAny, "ON", MatchAny, MatchAny, MatchAny, "WHEN") ||
+ TailMatches("USING", MatchAny, "AS", MatchAny, "ON", MatchAny, MatchAny, MatchAny, "WHEN") ||
+ TailMatches("USING", MatchAny, MatchAny, "ON", MatchAny, MatchAny, MatchAny, "WHEN"))
+ COMPLETE_WITH("MATCHED", "NOT MATCHED");
+
+ /* Complete ... WHEN [NOT] MATCHED with THEN/AND */
+ else if (TailMatches("WHEN", "MATCHED") ||
+ TailMatches("WHEN", "NOT", "MATCHED"))
+ COMPLETE_WITH("THEN", "AND");
+
+ /* Complete ... WHEN MATCHED THEN with UPDATE SET/DELETE/DO NOTHING */
+ else if (TailMatches("WHEN", "MATCHED", "THEN"))
+ COMPLETE_WITH("UPDATE SET", "DELETE", "DO NOTHING");
+
+ /* Complete ... WHEN NOT MATCHED THEN with INSERT/DO NOTHING */
+ else if (TailMatches("WHEN", "NOT", "MATCHED", "THEN"))
+ COMPLETE_WITH("INSERT", "DO NOTHING");
+ }
/* NOTIFY --- can be inside EXPLAIN, RULE, etc */
else if (TailMatches("NOTIFY"))
--
2.37.1