summaryrefslogtreecommitdiff
path: root/Documentation
diff options
context:
space:
mode:
authorSam Atkins <atkinssj@serenityos.org>2022-01-04 15:34:53 +0000
committerBrian Gianforcaro <b.gianfo@gmail.com>2022-01-08 06:18:04 -0800
commite8c71605b9eb652aed82f11f185a06ac5bd8990d (patch)
tree3e9c053bb6f4fb06505cbc3055ea644224e7e3a3 /Documentation
parentf5b584f966bf0f3e7f9a3d6189ec269bbf2c5903 (diff)
downloadserenity-e8c71605b9eb652aed82f11f185a06ac5bd8990d.zip
Documentation: Document string formatting format
Diffstat (limited to 'Documentation')
-rw-r--r--Documentation/StringFormatting.md140
1 files changed, 140 insertions, 0 deletions
diff --git a/Documentation/StringFormatting.md b/Documentation/StringFormatting.md
new file mode 100644
index 0000000000..9b7598f847
--- /dev/null
+++ b/Documentation/StringFormatting.md
@@ -0,0 +1,140 @@
+# String Formatting
+
+Many places in Serenity allow you to format strings, similar to `printf()`, for example `String::formatted()`
+, `StringBuilder::appendff()`, or `dbgln()`. These are checked at compile time to ensure the format string matches the
+number of parameters. The syntax is largely based on
+the [C++ `std::formatter` syntax](https://en.cppreference.com/w/cpp/utility/format/formatter#Standard_format_specification)
+but there are some differences.
+
+For basic usage, any occurrences of `{}` in the format string are replaced with the other arguments, converted to string
+form, in order:
+
+```c++
+String::formatted("Well, {} my {} friends!", "hello", 42) == "Well, hello my 42 friends!";
+```
+
+If you want to include a literal `{` in the output, use `{{`:
+
+```c++
+String::formatted("{{ {}", "hello") == "{ hello";
+```
+
+You can refer to the arguments by index, if you want to repeat one or change the order:
+
+```c++
+String::formatted("{2}{0}{1}", "a", "b", "c") == "cab";
+```
+
+To control how the arguments are formatted, add colon after the optional index, and then add format specifier
+characters:
+
+```c++
+String::formatted("{:.4}", "cool dude") == "cool";
+String::formatted("{0:.4}", "cool dude") == "cool";
+```
+
+## Format specifiers
+
+In order, the format can contain:
+
+- Fill character and alignment
+- Sign
+- `#` Hash
+- `0` Zero
+- Width
+- Precision
+- Type specifier
+
+Each of these is optional. You can include any combination of them, but they must be in this order.
+
+### Fill and alignment
+
+This is an optional fill character, followed by an alignment. The fill character can be anything apart from `{` or `}`,
+and is used to fill any space left when the input has fewer characters than the format requests. By default, it is a
+space. (` `)
+
+The alignment characters are:
+
+- `<`: Align left.
+- `>`: Align right.
+- `^`: Align centered.
+
+### Sign
+
+- `+`: Always display a sign before the number.
+- `-`: Display a sign for negative numbers only.
+- (space): Display a sign for negative numbers, and a leading space for other numbers.
+
+### Hash
+
+`#` causes an "alternate form" to be used.
+
+For integer types, this adds the number-base prefix after the sign:
+
+- `0b` for binary.
+- `0` for octal.
+- `0x` for hexadecimal.
+
+### Zero
+
+`0` pads the number with leading zeros.
+
+### Width and Precision
+
+The width defines the minimum number of characters in the output. The precision is a `.` followed by a precision number,
+which is used as the precision of floating-point numbers, or a maximum-width for string values.
+
+Both the width and precision can be provided as a replacement field (`{}`, optionally including an argument index) which
+allows you to use an integer argument instead of a hard-coded number.
+
+### Type specifiers
+
+| Type | Effect | Example output |
+|-----------|-----------------------|--------------------------|
+| *nothing* | default format | Anything! :^) |
+| b | binary | `110`, `0b000110` |
+| B | binary uppercase | `110`, `0B000110` |
+| d | decimal | `42`, `+0000042` |
+| o | octal | `043` |
+| x | hexadecimal | `ff0`, `0x00000ff0` |
+| X | hexadecimal uppercase | `FF0`, `0X00000FF0` |
+| c | character | `a` |
+| s | string | `well, hello friends!` |
+| p | pointer | `0xdeadc0de` |
+| f | float | `1.234`, `-inf` |
+| a | hex float | |
+| A | hex float uppercase | |
+| hex-dump | hexadecimal dump | `fdfdfdfd`, `3030 00` |
+
+Not all type specifiers are compatible with all input types, of course.
+
+## Formatting custom types
+
+You can provide a custom `AK::Formatter<Foo>` class to format `Foo` values. For the simplest case where you already have
+a function that produces a string from your type, that would look like this:
+
+```c++
+template<>
+struct AK::Formatter<Web::CSS::Selector> : Formatter<StringView> {
+ ErrorOr<void> format(FormatBuilder& builder, Web::CSS::Selector const& selector)
+ {
+ return Formatter<StringView>::format(builder, selector.serialize());
+ }
+};
+```
+
+More advanced formatters that make check for format-specifier flags can be written by referring to the fields
+in `StandardFormatter` (which most `Formatter` classes extend).
+
+## Detecting if a type can be formatted
+
+The `AK::HasFormatter<T>` template has a boolean value representing whether `T` can be formatted.
+
+The `FormatIfSupported<T>` makes use of this to return either the formatted value of `T`, or a series of `?`s if the
+type cannot be formatted. For example:
+
+```c++
+// B has a Formatter defined, but A does not.
+String::formatted("{}", FormatIfSupported { A {} }) == "?";
+String::formatted("{}", FormatIfSupported { B {} }) == "B";
+```