From 94d36b07298fa2a46d26623c08a269cc6db6461a Mon Sep 17 00:00:00 2001
From: alterego655 <824662526@qq.com>
Date: Tue, 16 Dec 2025 11:00:25 +0800
Subject: [PATCH v6 3/4] Add tab completion for WAIT FOR LSN MODE parameter

Update psql tab completion to support the optional MODE parameter in
WAIT FOR LSN command. After specifying an LSN value, completion now
offers both MODE and WITH keywords since MODE defaults to REPLAY.
---
 src/bin/psql/tab-complete.in.c | 39 ++++++++++++++++++++++++----------
 1 file changed, 28 insertions(+), 11 deletions(-)

diff --git a/src/bin/psql/tab-complete.in.c b/src/bin/psql/tab-complete.in.c
index b1ff6f6cd94..8f269b5cb13 100644
--- a/src/bin/psql/tab-complete.in.c
+++ b/src/bin/psql/tab-complete.in.c
@@ -5327,10 +5327,11 @@ match_previous_words(int pattern_id,
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_vacuumables);
 
 /*
- * WAIT FOR LSN '<lsn>' [ WITH ( option [, ...] ) ]
+ * WAIT FOR LSN '<lsn>' [ MODE { REPLAY | FLUSH | WRITE } ] [ WITH ( option [, ...] ) ]
  * where option can be:
  *   TIMEOUT '<timeout>'
  *   NO_THROW
+ * MODE defaults to REPLAY if not specified.
  */
 	else if (Matches("WAIT"))
 		COMPLETE_WITH("FOR");
@@ -5339,25 +5340,41 @@ match_previous_words(int pattern_id,
 	else if (Matches("WAIT", "FOR", "LSN"))
 		/* No completion for LSN value - user must provide manually */
 		;
+
+	/*
+	 * After LSN value, offer MODE (optional) or WITH, since MODE defaults to
+	 * REPLAY
+	 */
 	else if (Matches("WAIT", "FOR", "LSN", MatchAny))
+		COMPLETE_WITH("MODE", "WITH");
+	else if (Matches("WAIT", "FOR", "LSN", MatchAny, "MODE"))
+		COMPLETE_WITH("REPLAY", "FLUSH", "WRITE");
+	else if (Matches("WAIT", "FOR", "LSN", MatchAny, "MODE", MatchAny))
 		COMPLETE_WITH("WITH");
+	/* WITH directly after LSN (using default REPLAY mode) */
 	else if (Matches("WAIT", "FOR", "LSN", MatchAny, "WITH"))
 		COMPLETE_WITH("(");
+	else if (Matches("WAIT", "FOR", "LSN", MatchAny, "MODE", MatchAny, "WITH"))
+		COMPLETE_WITH("(");
+
+	/*
+	 * Handle parenthesized option list (both with and without explicit MODE).
+	 * This fires when we're in an unfinished parenthesized option list.
+	 * get_previous_words treats a completed parenthesized option list as one
+	 * word, so the above test is correct. timeout takes a string value,
+	 * no_throw takes no value. We don't offer completions for these values.
+	 */
 	else if (HeadMatches("WAIT", "FOR", "LSN", MatchAny, "WITH", "(*") &&
 			 !HeadMatches("WAIT", "FOR", "LSN", MatchAny, "WITH", "(*)"))
 	{
-		/*
-		 * This fires if we're in an unfinished parenthesized option list.
-		 * get_previous_words treats a completed parenthesized option list as
-		 * one word, so the above test is correct.
-		 */
 		if (ends_with(prev_wd, '(') || ends_with(prev_wd, ','))
 			COMPLETE_WITH("timeout", "no_throw");
-
-		/*
-		 * timeout takes a string value, no_throw takes no value. We don't
-		 * offer completions for these values.
-		 */
+	}
+	else if (HeadMatches("WAIT", "FOR", "LSN", MatchAny, "MODE", MatchAny, "WITH", "(*") &&
+			 !HeadMatches("WAIT", "FOR", "LSN", MatchAny, "MODE", MatchAny, "WITH", "(*)"))
+	{
+		if (ends_with(prev_wd, '(') || ends_with(prev_wd, ','))
+			COMPLETE_WITH("timeout", "no_throw");
 	}
 
 /* WITH [RECURSIVE] */
-- 
2.51.0

