66import subprocess
77import tempfile
88from pathlib import Path
9- from typing import TypedDict
9+ from typing import Any , TypedDict
1010
1111import questionary
1212import questionary .prompts .text
3030 NothingToCommitError ,
3131)
3232from commitizen .git import smart_open
33+ from commitizen .question import CzQuestion , InputQuestion
3334
3435
3536class CommitArgs (TypedDict , total = False ):
@@ -44,6 +45,25 @@ class CommitArgs(TypedDict, total=False):
4445 retry : bool
4546
4647
48+ def _handle_questionary_prompt (question : CzQuestion , cz_style : Any ) -> dict [str , Any ]:
49+ """Handle questionary prompt with error handling."""
50+ try :
51+ answer = questionary .prompt ([question ], style = cz_style )
52+ if not answer :
53+ raise NoAnswersError ()
54+ return answer
55+ except ValueError as err :
56+ root_err = err .__context__
57+ if isinstance (root_err , CzException ):
58+ raise CustomError (root_err .__str__ ())
59+ raise err
60+
61+
62+ def _handle_multiline_fallback (multiline_question : InputQuestion , cz_style : Any ) -> dict [str , Any ]:
63+ """Handle fallback to standard behavior if custom multiline approach fails."""
64+ return _handle_questionary_prompt (multiline_question , cz_style )
65+
66+
4767class Commit :
4868 """Show prompt for the user to create a guided commit."""
4969
@@ -76,29 +96,21 @@ def _prompt_commit_questions(self) -> str:
7696 for question in questions :
7797 if question ["type" ] == "list" :
7898 question ["use_shortcuts" ] = self .config .settings ["use_shortcuts" ]
79- try :
80- answer = questionary .prompt ([question ], style = cz .style )
81- if not answer :
82- raise NoAnswersError ()
83- answers .update (answer )
84- except ValueError as err :
85- root_err = err .__context__
86- if isinstance (root_err , CzException ):
87- raise CustomError (root_err .__str__ ())
88- raise err
99+ answer = _handle_questionary_prompt (question , cz .style )
100+ answers .update (answer )
89101 elif question ["type" ] == "input" and question .get ("multiline" , False ):
90102 is_optional = (
91103 question .get ("default" ) == ""
92104 or "skip" in question .get ("message" , "" ).lower ()
93105 )
94106
95107 if is_optional :
96- print (
97- "\033 [90m 💡 Multiline input:\n Press Enter on empty line to skip, Enter after text for new lines, Alt+Enter to finish\033 [0m "
108+ out . info (
109+ "💡 Multiline input:\n Press Enter on empty line to skip, Enter after text for new lines, Alt+Enter to finish"
98110 )
99111 else :
100- print (
101- "\033 [90m 💡 Multiline input:\n Press Enter for new lines and Alt+Enter to finish\033 [0m "
112+ out . info (
113+ "💡 Multiline input:\n Press Enter for new lines and Alt+Enter to finish"
102114 )
103115
104116 # Create custom multiline input with Enter-on-empty behavior for optional fields
@@ -142,11 +154,7 @@ def _(event: KeyPressEvent) -> None:
142154
143155 except Exception :
144156 # Fallback to standard behavior if custom approach fails
145- answer = questionary .prompt (
146- [multiline_question ], style = cz .style
147- )
148- if not answer :
149- raise NoAnswersError ()
157+ answer = _handle_multiline_fallback (multiline_question , cz .style )
150158 answers .update (answer )
151159 else :
152160 # Required fields - don't allow newline on empty first line and show error
@@ -158,8 +166,8 @@ def _(event: KeyPressEvent) -> None:
158166 # If buffer is completely empty (no content at all), show error and don't allow newline
159167 if not buffer .text .strip ():
160168 # Show error message with prompt
161- print (
162- "\n \033 [91m ⚠ This field is required. Please enter some content or press Ctrl+C to abort.\033 [0m "
169+ out . error (
170+ "\n ⚠ This field is required. Please enter some content or press Ctrl+C to abort."
163171 )
164172 print ("> " , end = "" , flush = True )
165173 # Don't do anything - require content first
@@ -189,23 +197,11 @@ def _(event: KeyPressEvent) -> None:
189197
190198 except Exception :
191199 # Fallback to standard behavior if custom approach fails
192- answer = questionary .prompt (
193- [multiline_question ], style = cz .style
194- )
195- if not answer :
196- raise NoAnswersError ()
200+ answer = _handle_multiline_fallback (multiline_question , cz .style )
197201 answers .update (answer )
198202 else :
199- try :
200- answer = questionary .prompt ([question ], style = cz .style )
201- if not answer :
202- raise NoAnswersError ()
203- answers .update (answer )
204- except ValueError as err :
205- root_err = err .__context__
206- if isinstance (root_err , CzException ):
207- raise CustomError (root_err .__str__ ())
208- raise err
203+ answer = _handle_questionary_prompt (question , cz .style )
204+ answers .update (answer )
209205
210206 message = cz .message (answers )
211207 message_len = len (message .partition ("\n " )[0 ].strip ())
0 commit comments