1
1
use std:: collections:: HashMap ;
2
+ use std:: io:: Write as _;
2
3
3
4
use anyhow:: { anyhow, bail, Context } ;
4
5
use console:: style;
@@ -11,26 +12,43 @@ use syntect::easy::HighlightLines;
11
12
use syntect:: highlighting:: ThemeSet ;
12
13
use syntect:: parsing:: SyntaxSet ;
13
14
use syntect:: util:: as_24_bit_terminal_escaped;
15
+ use termcolor:: { ColorChoice , StandardStream , WriteColor as _} ;
16
+ use terminal_size:: { terminal_size, Height } ;
14
17
15
18
const DEFAULT_UPSTREAM_BRANCHES : & [ & str ] = & [ "main" , "master" , "develop" , "trunk" ] ;
19
+ pub const DEFAULT_THEME : & str = "base16-ocean.dark" ;
20
+
21
+ pub struct Config {
22
+ /// Change the commit message that you amend, instead of using the original commit message
23
+ pub squash : bool ,
24
+ /// The maximum number of commits to show when looking for your merge point
25
+ pub max_commits : usize ,
26
+ /// Specify a commit to ammend by the subject line of the commit
27
+ pub commit_message_pattern : Option < String > ,
28
+ pub default_upstream_branch : Option < String > ,
29
+ /// Require a newline when confirming y/n questions
30
+ pub require_newline : bool ,
31
+ /// Which theme to use
32
+ pub theme : String ,
33
+ }
16
34
17
- pub fn instafix (
18
- squash : bool ,
19
- max_commits : usize ,
20
- message_pattern : Option < String > ,
21
- upstream_branch_name : Option < & str > ,
22
- require_newline : bool ,
23
- ) -> Result < ( ) , anyhow:: Error > {
35
+ pub fn instafix ( c : Config ) -> Result < ( ) , anyhow:: Error > {
24
36
let repo = Repository :: open ( "." ) . context ( "opening repo" ) ?;
25
- let diff = create_diff ( & repo, require_newline) . context ( "creating diff" ) ?;
37
+ let diff = create_diff ( & repo, & c . theme , c . require_newline ) . context ( "creating diff" ) ?;
26
38
let head = repo. head ( ) . context ( "finding head commit" ) ?;
27
39
let head_branch = Branch :: wrap ( head) ;
28
- let upstream =
29
- get_merge_base ( & repo, & head_branch, upstream_branch_name) . context ( "creating merge base" ) ?;
30
- let commit_to_amend = select_commit_to_amend ( & repo, upstream, max_commits, & message_pattern)
31
- . context ( "selecting commit to amend" ) ?;
40
+ let upstream = get_merge_base ( & repo, & head_branch, c. default_upstream_branch . as_deref ( ) )
41
+ . context ( "creating merge base" ) ?;
42
+ let commit_to_amend = select_commit_to_amend (
43
+ & repo,
44
+ upstream,
45
+ c. max_commits ,
46
+ c. commit_message_pattern . as_deref ( ) ,
47
+ )
48
+ . context ( "selecting commit to amend" ) ?;
32
49
eprintln ! ( "Selected {}" , disp( & commit_to_amend) ) ;
33
- do_fixup_commit ( & repo, & head_branch, & commit_to_amend, squash) . context ( "doing fixup commit" ) ?;
50
+ do_fixup_commit ( & repo, & head_branch, & commit_to_amend, c. squash )
51
+ . context ( "doing fixup commit" ) ?;
34
52
let needs_stash = worktree_is_dirty ( & repo) ?;
35
53
if needs_stash {
36
54
// TODO: is it reasonable to create a new repo to work around lifetime issues?
@@ -239,7 +257,11 @@ fn get_merge_base<'a>(
239
257
}
240
258
241
259
/// Get a diff either from the index or the diff from the index to the working tree
242
- fn create_diff ( repo : & Repository , require_newline : bool ) -> Result < Diff , anyhow:: Error > {
260
+ fn create_diff < ' a > (
261
+ repo : & ' a Repository ,
262
+ theme : & str ,
263
+ require_newline : bool ,
264
+ ) -> Result < Diff < ' a > , anyhow:: Error > {
243
265
let head = repo. head ( ) ?;
244
266
let head_tree = head. peel_to_tree ( ) ?;
245
267
let staged_diff = repo. diff_tree_to_index ( Some ( & head_tree) , None , None ) ?;
@@ -248,11 +270,18 @@ fn create_diff(repo: &Repository, require_newline: bool) -> Result<Diff, anyhow:
248
270
let diff = if diffstat. files_changed ( ) == 0 {
249
271
let dirty_workdir_stats = dirty_diff. stats ( ) ?;
250
272
if dirty_workdir_stats. files_changed ( ) > 0 {
273
+ let Height ( h) = terminal_size ( ) . map ( |( _w, h) | h) . unwrap_or ( Height ( 24 ) ) ;
274
+ let cutoff_height = ( h - 5 ) as usize ; // give some room for the prompt
251
275
let total_change = dirty_workdir_stats. insertions ( ) + dirty_workdir_stats. deletions ( ) ;
252
- if total_change < 50 {
253
- native_diff ( & dirty_diff) ?;
254
- } else {
276
+ if total_change >= cutoff_height {
255
277
print_diffstat ( "Unstaged" , & dirty_diff) ?;
278
+ } else {
279
+ let diff_lines = native_diff ( & dirty_diff, theme) ?;
280
+ if diff_lines. len ( ) >= cutoff_height {
281
+ print_diffstat ( "Unstaged" , & dirty_diff) ?;
282
+ } else {
283
+ print_diff_lines ( & diff_lines) ?;
284
+ }
256
285
}
257
286
if !Confirm :: new ( )
258
287
. with_prompt ( "Nothing staged, stage and commit everything?" )
@@ -312,7 +341,7 @@ fn select_commit_to_amend<'a>(
312
341
repo : & ' a Repository ,
313
342
upstream : Option < Object < ' a > > ,
314
343
max_commits : usize ,
315
- message_pattern : & Option < String > ,
344
+ message_pattern : Option < & str > ,
316
345
) -> Result < Commit < ' a > , anyhow:: Error > {
317
346
let mut walker = repo. revwalk ( ) ?;
318
347
walker. push_head ( ) ?;
@@ -417,15 +446,29 @@ fn format_ref(rf: &git2::Reference<'_>) -> Result<String, anyhow::Error> {
417
446
Ok ( format ! ( "{} ({})" , shorthand, & sha[ ..10 ] ) )
418
447
}
419
448
449
+ /// A vec of all built-in theme names
450
+ pub fn print_themes ( ) {
451
+ println ! ( "Available themes:" ) ;
452
+ for theme in ThemeSet :: load_defaults ( ) . themes . keys ( ) {
453
+ println ! ( " {}" , theme) ;
454
+ }
455
+ }
456
+
420
457
// diff helpers
421
458
422
- fn native_diff ( diff : & Diff < ' _ > ) -> Result < ( ) , anyhow:: Error > {
459
+ fn native_diff ( diff : & Diff < ' _ > , theme : & str ) -> Result < Vec < String > , anyhow:: Error > {
423
460
let ss = SyntaxSet :: load_defaults_newlines ( ) ;
424
461
let ts = ThemeSet :: load_defaults ( ) ;
425
462
let syntax = ss. find_syntax_by_extension ( "patch" ) . unwrap ( ) ;
426
- let mut h = HighlightLines :: new ( syntax, & ts. themes [ "base16-ocean.dark" ] ) ;
463
+ let mut h = HighlightLines :: new (
464
+ syntax,
465
+ ts. themes
466
+ . get ( theme)
467
+ . unwrap_or_else ( || & ts. themes [ DEFAULT_THEME ] ) ,
468
+ ) ;
427
469
428
470
let mut inner_err = None ;
471
+ let mut diff_lines = Vec :: new ( ) ;
429
472
430
473
diff. print ( DiffFormat :: Patch , |_delta, _hunk, line| {
431
474
let content = std:: str:: from_utf8 ( line. content ( ) ) . unwrap ( ) ;
@@ -441,7 +484,7 @@ fn native_diff(diff: &Diff<'_>) -> Result<(), anyhow::Error> {
441
484
}
442
485
} ;
443
486
let escaped = as_24_bit_terminal_escaped ( & ranges[ ..] , true ) ;
444
- print ! ( "{}" , escaped) ;
487
+ diff_lines . push ( escaped) ;
445
488
}
446
489
_ => {
447
490
let ranges = match h. highlight_line ( content, & ss) {
@@ -452,7 +495,7 @@ fn native_diff(diff: &Diff<'_>) -> Result<(), anyhow::Error> {
452
495
}
453
496
} ;
454
497
let escaped = as_24_bit_terminal_escaped ( & ranges[ ..] , true ) ;
455
- print ! ( "{}" , escaped) ;
498
+ diff_lines . push ( escaped) ;
456
499
}
457
500
}
458
501
true
@@ -461,10 +504,20 @@ fn native_diff(diff: &Diff<'_>) -> Result<(), anyhow::Error> {
461
504
if let Some ( err) = inner_err {
462
505
Err ( err. into ( ) )
463
506
} else {
464
- Ok ( ( ) )
507
+ Ok ( diff_lines )
465
508
}
466
509
}
467
510
511
+ fn print_diff_lines ( diff_lines : & [ String ] ) -> Result < ( ) , anyhow:: Error > {
512
+ let mut stdout = StandardStream :: stdout ( ColorChoice :: Auto ) ;
513
+ for line in diff_lines {
514
+ write ! ( & mut stdout, "{}" , line) ?;
515
+ }
516
+ stdout. reset ( ) ?;
517
+ writeln ! ( & mut stdout) ?;
518
+ Ok ( ( ) )
519
+ }
520
+
468
521
fn print_diffstat ( prefix : & str , diff : & Diff < ' _ > ) -> Result < ( ) , anyhow:: Error > {
469
522
let buf = diff. stats ( ) ?. to_buf ( DiffStatsFormat :: FULL , 80 ) ?;
470
523
let stat = std:: str:: from_utf8 ( & buf) . context ( "converting diffstat to utf-8" ) ?;
0 commit comments