Packages
glint
1.0.0
1.3.0
1.2.1
1.2.0
1.1.1
1.1.0
1.0.1
1.0.0
1.0.0-rc5
1.0.0-rc4
1.0.0-rc3
1.0.0-rc2
1.0.0-rc1
0.18.1
0.18.0
0.17.1
0.17.0
0.16.0
0.16.0-rc1
0.15.0
0.15.0-rc2
0.15.0-rc1
0.14.0
0.13.0
0.12.0
0.12.0-rc6
0.12.0-rc5
0.12.0-rc4
0.12.0-rc3
0.12.0-rc2
0.12.0-rc1
0.11.4
0.11.3
0.11.2
0.11.0
0.10.0
0.9.0
0.8.0
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.0
0.5.0
0.4.0
0.3.0
0.2.0
0.1.3
0.1.2
0.1.1
0.1.0
Gleam command-line argument parsing with flags and automated help text generation.
Current section
Files
Jump to
Current section
Files
src/glint/internal/utils.gleam
import gleam/bool
import gleam/int
import gleam/list
import gleam/string
/// Returns the length of the longest string in the list.
///
pub fn max_string_length(strings: List(String)) -> Int {
use max, f <- list.fold(strings, 0)
f
|> string.length
|> int.max(max)
}
/// Wraps the given string so that no lines exceed the given width. Newlines in
/// the input string are retained.
///
pub fn wordwrap(s: String, max_width: Int) -> List(String) {
use <- bool.guard(s == "", [])
use line <- list.flat_map(space_split_lines(s))
line
|> string.split(" ")
|> do_wordwrap(max_width, "", [])
}
fn do_wordwrap(
tokens: List(String),
max_width: Int,
line: String,
lines: List(String),
) -> List(String) {
case tokens {
// Handle the next token
[token, ..tokens] -> {
let token_length = string.length(token)
let line_length = string.length(line)
case line, line_length + 1 + token_length <= max_width {
// When the current line is empty the next token always goes on it
// regardless of its length
"", _ -> do_wordwrap(tokens, max_width, token, lines)
// Add the next token to the current line if it fits
_, True -> do_wordwrap(tokens, max_width, line <> " " <> token, lines)
// Start a new line with the next token as it exceeds the max width if
// added to the current line
_, False -> do_wordwrap(tokens, max_width, token, [line, ..lines])
}
}
// There are no more tokens so return the final result, adding the current
// line to it if it's not empty
[] if line == "" -> list.reverse(lines)
[] -> list.reverse([line, ..lines])
}
}
/// split a string for consecutive newline groups
/// replaces individual newlines with a spacet
/// groups of newlines > 1 are replaced with n-2 newlines followed by a new item
fn space_split_lines(s: String) -> List(String) {
let chunks =
s
|> string.trim
|> string.to_graphemes
|> list.chunk(fn(s) { s == "\n" })
let lines = {
use acc, chunk <- list.fold(chunks, #([], False))
case chunk, acc.0 {
// convert newline chunks into n-2 newlines
["\n", "\n", ..rest], [s, ..accs] -> #(
[s <> string.concat(rest), ..accs],
True,
)
// convert single newlines into spaces
["\n"], [s, ..accs] -> #([s <> " ", ..accs], False)
// add the next string to the end of the last IFF the last was not a multi newline
_, [s, ..accs] if !acc.1 -> #([s <> string.concat(chunk), ..accs], False)
// add the next string as the next value in the accumulated list
_, _ -> #([string.concat(chunk), ..acc.0], False)
}
}
list.reverse(lines.0)
}