+4
CHANGELOG.md
+4
CHANGELOG.md
+1
-1
birdie_snapshots/diffing_a_case_expression.accepted
+1
-1
birdie_snapshots/diffing_a_case_expression.accepted
+1
-1
birdie_snapshots/my_favourite_number_wrapped_in_a_result.accepted
+1
-1
birdie_snapshots/my_favourite_number_wrapped_in_a_result.accepted
+1
-1
birdie_snapshots/my_first_snapshot.accepted
+1
-1
birdie_snapshots/my_first_snapshot.accepted
+1
-1
birdie_snapshots/snapping_a_list_of_numbers.accepted
+1
-1
birdie_snapshots/snapping_a_list_of_numbers.accepted
-1
gleam.toml
-1
gleam.toml
+2
-4
manifest.toml
+2
-4
manifest.toml
···
4
4
packages = [
5
5
{ name = "argv", version = "1.0.1", build_tools = ["gleam"], requirements = [], otp_app = "argv", source = "hex", outer_checksum = "A6E9009E50BBE863EB37D963E4315398D41A3D87D0075480FC244125808F964A" },
6
6
{ name = "filepath", version = "0.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "534E8161A0DE192A9A105EFEC34369E9FD5834BB58ED449B5ACAEE8704358588" },
7
-
{ name = "gap", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "gleam_community_ansi"], otp_app = "gap", source = "hex", outer_checksum = "2EE1B0A17E85CF73A0C1D29DA315A2699117A8F549C8E8D89FA8261BE41EDEB1" },
8
-
{ name = "glam", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "glam", source = "hex", outer_checksum = "02E0311862B9669C3E8CE73FA5A95D8FA457C6ACB48D95FBE808ABFAE0A1CEB0" },
9
-
{ name = "gleam_community_ansi", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "gleam_community_colour"], otp_app = "gleam_community_ansi", source = "hex", outer_checksum = "FE79E08BF97009729259B6357EC058315B6FBB916FAD1C2FF9355115FEB0D3A4" },
7
+
{ name = "gap", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_community_ansi", "gleam_stdlib"], otp_app = "gap", source = "hex", outer_checksum = "2EE1B0A17E85CF73A0C1D29DA315A2699117A8F549C8E8D89FA8261BE41EDEB1" },
8
+
{ name = "gleam_community_ansi", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_community_colour", "gleam_stdlib"], otp_app = "gleam_community_ansi", source = "hex", outer_checksum = "FE79E08BF97009729259B6357EC058315B6FBB916FAD1C2FF9355115FEB0D3A4" },
10
9
{ name = "gleam_community_colour", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_community_colour", source = "hex", outer_checksum = "A49A5E3AE8B637A5ACBA80ECB9B1AFE89FD3D5351FF6410A42B84F666D40D7D5" },
11
10
{ name = "gleam_erlang", version = "0.24.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "26BDB52E61889F56A291CB34167315780EE4AA20961917314446542C90D1C1A0" },
12
11
{ name = "gleam_stdlib", version = "0.34.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "1FB8454D2991E9B4C0C804544D8A9AD0F6184725E20D63C3155F0AEB4230B016" },
···
20
19
argv = { version = "~> 1.0" }
21
20
filepath = { version = "~> 0.1" }
22
21
gap = { version = "~> 1.1" }
23
-
glam = { version = "~> 1.3" }
24
22
gleam_community_ansi = { version = "~> 1.4" }
25
23
gleam_erlang = { version = "~> 0.24" }
26
24
gleam_stdlib = { version = "~> 0.34 or ~> 1.0" }
+62
-44
src/birdie.gleam
+62
-44
src/birdie.gleam
···
10
10
import argv
11
11
import birdie/internal/diff.{type DiffLine, DiffLine}
12
12
import filepath
13
-
import glam/doc
14
13
import gleeunit/should
15
14
import justin
16
15
import rank
···
508
507
}
509
508
510
509
fn pretty_info_line(line: InfoLine, width: Int) -> String {
511
-
let title_length = case line {
512
-
InfoLineWithNoTitle(..) -> 2
513
-
InfoLineWithTitle(title: title, ..) -> string.length(title)
510
+
let prefix = case line {
511
+
InfoLineWithNoTitle(..) -> " "
512
+
InfoLineWithTitle(title: title, ..) -> " " <> title <> ": "
514
513
}
515
-
516
-
let line_doc = case line.split {
517
-
DoNotSplit -> doc.from_string(line.content)
514
+
let prefix_length = string.length(prefix)
515
+
case line.split {
516
+
Truncate -> prefix <> truncate(line.content, width - prefix_length)
517
+
DoNotSplit -> prefix <> line.content
518
518
SplitWords ->
519
-
string.split(line.content, on: "\n")
520
-
|> list.map(fn(line) {
521
-
string.split(line, on: " ")
522
-
|> list.map(doc.from_string)
523
-
|> doc.join(with: doc.flex_space)
524
-
})
525
-
|> doc.join(with: doc.line)
526
-
|> doc.group
527
-
|> doc.nest(by: title_length + 4)
528
-
529
-
Truncate -> {
530
-
let max_content_length = width - title_length - 6
531
-
let content_length = string.length(line.content)
532
-
case content_length > max_content_length {
533
-
False -> doc.from_string(line.content)
534
-
True ->
535
-
string.to_graphemes(line.content)
536
-
|> list.take(max_content_length - 3)
537
-
|> string.join(with: "")
538
-
|> string.append("...")
539
-
|> doc.from_string
519
+
case to_lines(line.content, width - prefix_length) {
520
+
[] -> prefix
521
+
[line, ..lines] -> {
522
+
use acc, line <- list.fold(over: lines, from: prefix <> line)
523
+
acc <> "\n" <> string.repeat(" ", prefix_length) <> line
524
+
}
540
525
}
541
-
}
542
526
}
543
-
544
-
// This is an ugly hack that I need because `glam` currently doesn't take into
545
-
// account color codes.
546
-
// Those are invisible but still contribute to the length of a string, so I
547
-
// have to artifically set the width to a higher limit to take into account
548
-
// the length of the color codes added to the lines' titles.
549
-
let ansi_code_len = 7
550
-
551
-
case line {
552
-
InfoLineWithNoTitle(..) -> doc.from_string(" ")
553
-
InfoLineWithTitle(title: title, ..) ->
554
-
doc.from_string(ansi.blue(" " <> title <> ": "))
555
-
}
556
-
|> doc.append(line_doc)
557
-
|> doc.to_string(width + ansi_code_len)
558
527
}
559
528
560
529
fn pretty_diff_line(diff_line: DiffLine, padding: Int) -> String {
···
587
556
}
588
557
589
558
pretty_number <> separator <> pretty_line
559
+
}
560
+
561
+
// --- STRING UTILITIES --------------------------------------------------------
562
+
563
+
fn truncate(string: String, max_length: Int) -> String {
564
+
case string.length(string) > max_length {
565
+
False -> string
566
+
True ->
567
+
string.to_graphemes(string)
568
+
|> list.take(max_length - 3)
569
+
|> string.join(with: "")
570
+
|> string.append("...")
571
+
}
572
+
}
573
+
574
+
fn to_lines(string: String, max_length: Int) -> List(String) {
575
+
// We still want to keep the original lines, so we work line by line.
576
+
use line <- list.flat_map(string.split(string, on: "\n"))
577
+
let words = string.split(line, on: " ")
578
+
do_to_lines([], "", 0, words, max_length)
579
+
}
580
+
581
+
fn do_to_lines(
582
+
lines: List(String),
583
+
line: String,
584
+
line_length: Int,
585
+
words: List(String),
586
+
max_length: Int,
587
+
) -> List(String) {
588
+
case words {
589
+
[] ->
590
+
case line == "" {
591
+
True -> list.reverse(lines)
592
+
False -> list.reverse([line, ..lines])
593
+
}
594
+
595
+
[word, ..rest] -> {
596
+
let word_length = string.length(word)
597
+
let new_line_length = word_length + line_length + 1
598
+
// ^ With the +1 we account for the whitespace that separates words!
599
+
case new_line_length > max_length {
600
+
True -> do_to_lines([line, ..lines], "", 0, words, max_length)
601
+
False -> {
602
+
let new_line = line <> " " <> word
603
+
do_to_lines(lines, new_line, new_line_length, rest, max_length)
604
+
}
605
+
}
606
+
}
607
+
}
590
608
}
591
609
592
610
// --- CLI COMMAND -------------------------------------------------------------