Skip to content

Commit 4374b05

Browse files
committed
feat(commit): integrate custom comment_char into commit, reword, and branch rename workflows
1 parent 4325a21 commit 4374b05

4 files changed

Lines changed: 118 additions & 35 deletions

File tree

crates/but/src/command/legacy/commit.rs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,12 @@ pub(crate) fn commit(
416416
"In JSON mode, a commit message must be provided via --message (-m), --message-file, or --ai (-i)"
417417
);
418418
}
419-
get_commit_message_from_editor(ctx, &files_to_commit, &changes, show_diff_in_editor)?
419+
let comment_char = {
420+
let repo = ctx.repo.get()?;
421+
let config = repo.config_snapshot();
422+
crate::command::config::get_comment_char(&config)
423+
};
424+
get_commit_message_from_editor(ctx, &files_to_commit, &changes, show_diff_in_editor, comment_char)?
420425
};
421426

422427
if commit_message.trim().is_empty() {
@@ -720,19 +725,20 @@ fn get_commit_message_from_editor(
720725
files_to_commit: &[FileAssignment],
721726
changes: &[TreeChange],
722727
show_diff_in_editor: ShowDiffInEditor,
728+
comment_char: char,
723729
) -> anyhow::Result<String> {
724730
// Generate commit message template
725731
let mut template = String::new();
726-
template.push_str("\n# Please enter the commit message for your changes. Lines starting\n");
727-
template.push_str("# with '#' will be ignored, and an empty message aborts the commit.\n");
728-
template.push_str("#\n");
729-
template.push_str("# Changes to be committed:\n");
732+
template.push_str(&format!("\n{comment_char} Please enter the commit message for your changes. Lines starting\n"));
733+
template.push_str(&format!("{comment_char} with '{comment_char}' will be ignored, and an empty message aborts the commit.\n"));
734+
template.push_str(&format!("{comment_char}\n"));
735+
template.push_str(&format!("{comment_char} Changes to be committed:\n"));
730736

731737
for fa in files_to_commit {
732738
let status_char = get_status_char(&fa.path, changes);
733-
template.push_str(&format!("#\t{} {}\n", status_char, fa.path.to_str_lossy()));
739+
template.push_str(&format!("{comment_char}\t{} {}\n", status_char, fa.path.to_str_lossy()));
734740
}
735-
template.push_str("#\n");
741+
template.push_str(&format!("{comment_char}\n"));
736742

737743
// Compute diff for the editor if requested
738744
let should_show_diff = show_diff_in_editor.should_show_diff(|| {
@@ -758,6 +764,7 @@ fn get_commit_message_from_editor(
758764
"commit_msg",
759765
&template,
760766
diff_text.as_deref(),
767+
comment_char,
761768
)?
762769
.to_string();
763770
Ok(lossy_message)

crates/but/src/command/legacy/reword.rs

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,13 @@ fn edit_branch_name(
9393
}
9494

9595
if let Some(sid) = stack_entry.id {
96+
let comment_char = {
97+
let repo = ctx.repo.get()?;
98+
let config = repo.config_snapshot();
99+
crate::command::config::get_comment_char(&config)
100+
};
96101
let new_name = prepare_provided_message(message, "branch name")
97-
.unwrap_or_else(|| get_branch_name_from_editor(branch_name))?;
102+
.unwrap_or_else(|| get_branch_name_from_editor(branch_name, comment_char))?;
98103
but_api::legacy::stack::update_branch_name_with_perm(
99104
ctx,
100105
sid,
@@ -145,10 +150,17 @@ pub(crate) fn get_commit_message_from_editor(
145150
})
146151
.transpose()?;
147152

153+
let comment_char = {
154+
let repo = ctx.repo.get()?;
155+
let config = repo.config_snapshot();
156+
crate::command::config::get_comment_char(&config)
157+
};
158+
148159
let new_message = actually_get_commit_message_from_editor(
149160
&editor_initial_message,
150161
&changed_files,
151162
diff.as_deref(),
163+
comment_char,
152164
)?;
153165

154166
if should_update_commit_message(current_message_for_comparison, &new_message) {
@@ -250,22 +262,23 @@ fn actually_get_commit_message_from_editor(
250262
current_message: &str,
251263
changed_files: &[String],
252264
diff: Option<&[BString]>,
265+
comment_char: char,
253266
) -> Result<String> {
254267
// Generate commit message template with current message
255268
let mut template = String::new();
256269
template.push_str(current_message);
257270
if !current_message.is_empty() && !current_message.ends_with('\n') {
258271
template.push('\n');
259272
}
260-
template.push_str("\n# Please enter the commit message for your changes. Lines starting\n");
261-
template.push_str("# with '#' will be ignored, and an empty message aborts the commit.\n");
262-
template.push_str("#\n");
263-
template.push_str("# Changes in this commit:\n");
273+
template.push_str(&format!("\n{comment_char} Please enter the commit message for your changes. Lines starting\n"));
274+
template.push_str(&format!("{comment_char} with '{comment_char}' will be ignored, and an empty message aborts the commit.\n"));
275+
template.push_str(&format!("{comment_char}\n"));
276+
template.push_str(&format!("{comment_char} Changes in this commit:\n"));
264277

265278
for file in changed_files {
266-
template.push_str(&format!("#\t{file}\n"));
279+
template.push_str(&format!("{comment_char}\t{file}\n"));
267280
}
268-
template.push_str("#\n");
281+
template.push_str(&format!("{comment_char}\n"));
269282

270283
let mut template_rest = String::new();
271284
if let Some(diff) = diff
@@ -281,6 +294,7 @@ fn actually_get_commit_message_from_editor(
281294
"commit_msg",
282295
&template,
283296
Some(template_rest.as_str()).filter(|s| !s.is_empty()),
297+
comment_char,
284298
)?
285299
.to_string();
286300

@@ -291,18 +305,18 @@ fn actually_get_commit_message_from_editor(
291305
Ok(lossy_message)
292306
}
293307

294-
pub(crate) fn get_branch_name_from_editor(current_name: &str) -> Result<String> {
308+
pub(crate) fn get_branch_name_from_editor(current_name: &str, comment_char: char) -> Result<String> {
295309
let mut template = String::new();
296310
template.push_str(current_name);
297311
if !current_name.is_empty() && !current_name.ends_with('\n') {
298312
template.push('\n');
299313
}
300-
template.push_str("\n# Please enter the new branch name. Lines starting\n");
301-
template.push_str("# with '#' will be ignored, and an empty name aborts the operation.\n");
302-
template.push_str("#\n");
314+
template.push_str(&format!("\n{comment_char} Please enter the new branch name. Lines starting\n"));
315+
template.push_str(&format!("{comment_char} with '{comment_char}' will be ignored, and an empty name aborts the operation.\n"));
316+
template.push_str(&format!("{comment_char}\n"));
303317

304318
let branch_name_lossy =
305-
tui::get_text::from_editor_no_comments("branch_name", &template)?.to_string();
319+
tui::get_text::from_editor_no_comments("branch_name", &template, comment_char)?.to_string();
306320
let branch_name = branch_name_lossy.trim();
307321

308322
if branch_name.is_empty() {

crates/but/src/command/legacy/status/tui/mod.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2319,7 +2319,12 @@ impl App {
23192319
}
23202320
}
23212321
InlineRewordMode::Branch { name, stack_id, .. } => {
2322-
let new_name = get_branch_name_from_editor(line)?;
2322+
let comment_char = {
2323+
let repo = ctx.repo.get()?;
2324+
let config = repo.config_snapshot();
2325+
crate::command::config::get_comment_char(&config)
2326+
};
2327+
let new_name = get_branch_name_from_editor(line, comment_char)?;
23232328
let normalized_name =
23242329
operations::reword_branch_legacy(ctx, *stack_id, name.clone(), new_name)?;
23252330
SelectAfterReload::Branch(normalized_name)

crates/but/src/tui/get_text.rs

Lines changed: 72 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ const REST_TEXT_MARKER: &str = "# --- ignore-rest ---";
1212
/// identified by a `filename_safe_intent` to help the user understand what's wanted of them.
1313
/// Note that this string must be valid in filenames.
1414
///
15-
/// Returns the edited text (*without known encoding*), with comment lines (starting with `#`) removed.
16-
pub fn from_editor_no_comments(filename_safe_intent: &str, initial_text: &str) -> Result<BString> {
17-
let content = from_editor(filename_safe_intent, initial_text, None, ".txt")?;
18-
let filtered_lines = filter_content_from_editor(content.as_bstr());
15+
/// Returns the edited text (*without known encoding*), with comment lines (starting with `comment_char`) removed.
16+
pub fn from_editor_no_comments(filename_safe_intent: &str, initial_text: &str, comment_char: char) -> Result<BString> {
17+
let content = from_editor_impl(filename_safe_intent, initial_text, None, ".txt", Some(comment_char))?;
18+
let filtered_lines = filter_content_from_editor(content.as_bstr(), comment_char);
1919
Ok(filtered_lines.into_iter().collect())
2020
}
2121

@@ -25,23 +25,26 @@ pub fn from_editor_no_comments(filename_safe_intent: &str, initial_text: &str) -
2525
/// If `diff_text` is `Some`, appends it after a `REST_TEXT_MARKER` separator line in the editor.
2626
/// The marker and everything below it is automatically stripped from the returned text.
2727
///
28-
/// Returns the edited text (*without known encoding*), with comment lines (starting with `#`) removed.
28+
/// Returns the edited text (*without known encoding*), with comment lines (starting with `comment_char`) removed.
2929
pub fn from_editor_no_comments_as_patch(
3030
filename_safe_intent: &str,
3131
initial_text: &str,
3232
diff_text: Option<&str>,
33+
comment_char: char,
3334
) -> Result<BString> {
34-
let content = from_editor(filename_safe_intent, initial_text, diff_text, ".patch")?;
35-
let filtered_lines = filter_content_from_editor(content.as_bstr());
35+
let content = from_editor_impl(filename_safe_intent, initial_text, diff_text, ".patch", Some(comment_char))?;
36+
let filtered_lines = filter_content_from_editor(content.as_bstr(), comment_char);
3637
Ok(filtered_lines.into_iter().collect())
3738
}
3839

39-
/// Strip comment lines (starting with '#') and everything below `REST_TEXT_MARKER`.
40-
fn filter_content_from_editor(content: &BStr) -> Vec<&BStr> {
40+
/// Strip comment lines (starting with `comment_char`) and everything below `REST_TEXT_MARKER`.
41+
fn filter_content_from_editor(content: &BStr, comment_char: char) -> Vec<&BStr> {
42+
let comment_prefix = comment_char.to_string();
43+
let rest_marker = format!("{comment_char} --- ignore-rest ---");
4144
content
4245
.lines_with_terminator()
43-
.take_while(|line| !line.trim_start().starts_with_str(REST_TEXT_MARKER))
44-
.filter(|line| !line.trim_start().starts_with_str("#"))
46+
.take_while(|line| !line.trim_start().starts_with_str(&rest_marker))
47+
.filter(|line| !line.trim_start().starts_with_str(&comment_prefix))
4548
.map(|line| line.as_bstr())
4649
.collect()
4750
}
@@ -59,6 +62,16 @@ pub fn from_editor(
5962
initial_text: &str,
6063
rest_text: Option<&str>,
6164
file_suffix: &str,
65+
) -> Result<BString> {
66+
from_editor_impl(filename_safe_intent, initial_text, rest_text, file_suffix, None)
67+
}
68+
69+
fn from_editor_impl(
70+
filename_safe_intent: &str,
71+
initial_text: &str,
72+
rest_text: Option<&str>,
73+
file_suffix: &str,
74+
comment_char: Option<char>,
6275
) -> Result<BString> {
6376
const ALLOWED_SUFFIXES: &[&str] = &[".txt", ".md", ".patch"]; // feel free to add more allowed suffixes
6477
if !ALLOWED_SUFFIXES.contains(&file_suffix) {
@@ -76,8 +89,9 @@ pub fn from_editor(
7689
initial_text,
7790
rest_text,
7891
file_suffix,
92+
comment_char,
7993
),
80-
None => from_builtin_editor(filename_safe_intent, initial_text, rest_text),
94+
None => from_builtin_editor(filename_safe_intent, initial_text, rest_text, comment_char),
8195
}
8296
}
8397

@@ -88,6 +102,7 @@ fn from_external_editor(
88102
initial_text: &str,
89103
rest_text: Option<&str>,
90104
file_suffix: &str,
105+
comment_char: Option<char>,
91106
) -> Result<BString> {
92107
// Create a temporary file with the initial text
93108
let mut tempfile = tempfile::Builder::new()
@@ -101,7 +116,10 @@ fn from_external_editor(
101116
if !initial_text.ends_with('\n') {
102117
writeln!(&mut tempfile)?;
103118
}
104-
writeln!(&mut tempfile, "{REST_TEXT_MARKER}")?;
119+
let marker = comment_char
120+
.map(|c| format!("{c} --- ignore-rest ---"))
121+
.unwrap_or_else(|| REST_TEXT_MARKER.to_string());
122+
writeln!(&mut tempfile, "{marker}")?;
105123
writeln!(&mut tempfile, "{rest_text}")?;
106124
}
107125

@@ -129,6 +147,7 @@ fn from_builtin_editor(
129147
filename_safe_intent: &str,
130148
initial_text: &str,
131149
rest_text: Option<&str>,
150+
comment_char: Option<char>,
132151
) -> Result<BString> {
133152
// Determine editor mode based on the intent
134153
let mode = if filename_safe_intent.contains("commit") {
@@ -144,7 +163,10 @@ fn from_builtin_editor(
144163
if !initial_text.ends_with('\n') {
145164
initial_text.push('\n');
146165
}
147-
initial_text.push_str(REST_TEXT_MARKER);
166+
let marker = comment_char
167+
.map(|c| format!("{c} --- ignore-rest ---"))
168+
.unwrap_or_else(|| REST_TEXT_MARKER.to_string());
169+
initial_text.push_str(&marker);
148170
initial_text.push('\n');
149171
initial_text.push_str(rest_text);
150172
super::editor::run_builtin_editor(filename_safe_intent, &initial_text, mode)?
@@ -404,7 +426,42 @@ will
404426
be
405427
ignored"#
406428
));
407-
let filtered_content = filter_content_from_editor(raw_content.as_bstr());
429+
let filtered_content = filter_content_from_editor(raw_content.as_bstr(), '#');
430+
431+
assert_eq!(
432+
filtered_content,
433+
Vec::from([
434+
"commit message\n",
435+
"\n",
436+
"here is a longer description about the commit\n",
437+
"\n",
438+
"1. It does the thing\n",
439+
"2. It does the other thing\n",
440+
"\n",
441+
])
442+
);
443+
}
444+
445+
#[test]
446+
fn test_filter_content_from_editor_custom_comment_char() {
447+
let raw_content = BString::from(format!(
448+
r#"commit message
449+
450+
here is a longer description about the commit
451+
452+
1. It does the thing
453+
2. It does the other thing
454+
455+
; this line will be ignored
456+
; as will this
457+
; --- ignore-rest ---
458+
all
459+
this
460+
will
461+
be
462+
ignored"#
463+
));
464+
let filtered_content = filter_content_from_editor(raw_content.as_bstr(), ';');
408465

409466
assert_eq!(
410467
filtered_content,

0 commit comments

Comments
 (0)