6
6
import subprocess
7
7
import tempfile
8
8
from pathlib import Path
9
- from typing import TypedDict
9
+ from typing import Any , TypedDict
10
10
11
11
import questionary
12
12
import questionary .prompts .text
30
30
NothingToCommitError ,
31
31
)
32
32
from commitizen .git import smart_open
33
+ from commitizen .question import CzQuestion , InputQuestion
33
34
34
35
35
36
class CommitArgs (TypedDict , total = False ):
@@ -44,6 +45,25 @@ class CommitArgs(TypedDict, total=False):
44
45
retry : bool
45
46
46
47
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
+
47
67
class Commit :
48
68
"""Show prompt for the user to create a guided commit."""
49
69
@@ -76,29 +96,21 @@ def _prompt_commit_questions(self) -> str:
76
96
for question in questions :
77
97
if question ["type" ] == "list" :
78
98
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 )
89
101
elif question ["type" ] == "input" and question .get ("multiline" , False ):
90
102
is_optional = (
91
103
question .get ("default" ) == ""
92
104
or "skip" in question .get ("message" , "" ).lower ()
93
105
)
94
106
95
107
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"
98
110
)
99
111
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"
102
114
)
103
115
104
116
# Create custom multiline input with Enter-on-empty behavior for optional fields
@@ -142,11 +154,7 @@ def _(event: KeyPressEvent) -> None:
142
154
143
155
except Exception :
144
156
# 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 )
150
158
answers .update (answer )
151
159
else :
152
160
# Required fields - don't allow newline on empty first line and show error
@@ -158,8 +166,8 @@ def _(event: KeyPressEvent) -> None:
158
166
# If buffer is completely empty (no content at all), show error and don't allow newline
159
167
if not buffer .text .strip ():
160
168
# 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."
163
171
)
164
172
print ("> " , end = "" , flush = True )
165
173
# Don't do anything - require content first
@@ -189,23 +197,11 @@ def _(event: KeyPressEvent) -> None:
189
197
190
198
except Exception :
191
199
# 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 )
197
201
answers .update (answer )
198
202
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 )
209
205
210
206
message = cz .message (answers )
211
207
message_len = len (message .partition ("\n " )[0 ].strip ())
0 commit comments