From 1bb41bdd83b37f9ef7237095a368ea21e589d262 Mon Sep 17 00:00:00 2001
From: alterego655 <824662526@qq.com>
Date: Tue, 25 Nov 2025 19:18:06 +0800
Subject: [PATCH v1 4/5] 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 20d7a65c614..fcb9f19faef 100644
--- a/src/bin/psql/tab-complete.in.c
+++ b/src/bin/psql/tab-complete.in.c
@@ -5313,10 +5313,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");
@@ -5325,25 +5326,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

