blob: 8c40f14a8a7d56fec342a217555a192fad307558 [file] [log] [blame]
//===--- StringInterpolation.swift - String Interpolation -----------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
/// Represents a string literal with interpolations while it is being built up.
///
/// Do not create an instance of this type directly. It is used by the compiler
/// when you create a string using string interpolation. Instead, use string
/// interpolation to create a new string by including values, literals,
/// variables, or expressions enclosed in parentheses, prefixed by a
/// backslash (`\(`...`)`).
///
/// let price = 2
/// let number = 3
/// let message = """
/// If one cookie costs \(price) dollars, \
/// \(number) cookies cost \(price * number) dollars.
/// """
/// print(message)
/// // Prints "If one cookie costs 2 dollars, 3 cookies cost 6 dollars."
///
/// When implementing an `ExpressibleByStringInterpolation` conformance,
/// set the `StringInterpolation` associated type to
/// `DefaultStringInterpolation` to get the same interpolation behavior as
/// Swift's built-in `String` type and construct a `String` with the results.
/// If you don't want the default behavior or don't want to construct a
/// `String`, use a custom type conforming to `StringInterpolationProtocol`
/// instead.
///
/// Extending default string interpolation behavior
/// ===============================================
///
/// Code outside the standard library can extend string interpolation on
/// `String` and many other common types by extending
/// `DefaultStringInterpolation` and adding an `appendInterpolation(...)`
/// method. For example:
///
/// extension DefaultStringInterpolation {
/// fileprivate mutating func appendInterpolation(
/// escaped value: String, asASCII forceASCII: Bool = false) {
/// for char in value.unicodeScalars {
/// appendInterpolation(char.escaped(asASCII: forceASCII)
/// }
/// }
/// }
///
/// print("Escaped string: \(escaped: string)")
///
/// See `StringInterpolationProtocol` for details on `appendInterpolation`
/// methods.
///
/// `DefaultStringInterpolation` extensions should add only `mutating` members
/// and should not copy `self` or capture it in an escaping closure.
@_fixed_layout
public struct DefaultStringInterpolation: StringInterpolationProtocol {
/// The string contents accumulated by this instance.
@usableFromInline
internal var _storage: String
/// Creates a string interpolation with storage pre-sized for a literal
/// with the indicated attributes.
///
/// Do not call this initializer directly. It is used by the compiler when
/// interpreting string interpolations.
@inlinable
public init(literalCapacity: Int, interpolationCount: Int) {
let capacityPerInterpolation = 2
let initialCapacity = literalCapacity +
interpolationCount * capacityPerInterpolation
_storage = String(_StringGuts(_initialCapacity: initialCapacity))
}
/// Appends a literal segment of a string interpolation.
///
/// Do not call this method directly. It is used by the compiler when
/// interpreting string interpolations.
@inlinable
public mutating func appendLiteral(_ literal: String) {
literal.write(to: &self)
}
/// Interpolates the given value's textual representation into the
/// string literal being created.
///
/// Do not call this method directly. It is used by the compiler when
/// interpreting string interpolations. Instead, use string
/// interpolation to create a new string by including values, literals,
/// variables, or expressions enclosed in parentheses, prefixed by a
/// backslash (`\(`...`)`).
///
/// let price = 2
/// let number = 3
/// let message = """
/// If one cookie costs \(price) dollars, \
/// \(number) cookies cost \(price * number) dollars.
/// """
/// print(message)
/// // Prints "If one cookie costs 2 dollars, 3 cookies cost 6 dollars."
@inlinable
public mutating func appendInterpolation<T>(_ value: T)
where T: TextOutputStreamable, T: CustomStringConvertible
{
value.write(to: &self)
}
/// Interpolates the given value's textual representation into the
/// string literal being created.
///
/// Do not call this method directly. It is used by the compiler when
/// interpreting string interpolations. Instead, use string
/// interpolation to create a new string by including values, literals,
/// variables, or expressions enclosed in parentheses, prefixed by a
/// backslash (`\(`...`)`).
///
/// let price = 2
/// let number = 3
/// let message = "If one cookie costs \(price) dollars, " +
/// "\(number) cookies cost \(price * number) dollars."
/// print(message)
/// // Prints "If one cookie costs 2 dollars, 3 cookies cost 6 dollars."
@inlinable
public mutating func appendInterpolation<T>(_ value: T)
where T: TextOutputStreamable
{
value.write(to: &self)
}
/// Interpolates the given value's textual representation into the
/// string literal being created.
///
/// Do not call this method directly. It is used by the compiler when
/// interpreting string interpolations. Instead, use string
/// interpolation to create a new string by including values, literals,
/// variables, or expressions enclosed in parentheses, prefixed by a
/// backslash (`\(`...`)`).
///
/// let price = 2
/// let number = 3
/// let message = """
/// If one cookie costs \(price) dollars, \
/// \(number) cookies cost \(price * number) dollars.
/// """
/// print(message)
/// // Prints "If one cookie costs 2 dollars, 3 cookies cost 6 dollars."
@inlinable
public mutating func appendInterpolation<T>(_ value: T)
where T: CustomStringConvertible
{
value.description.write(to: &self)
}
/// Interpolates the given value's textual representation into the
/// string literal being created.
///
/// Do not call this method directly. It is used by the compiler when
/// interpreting string interpolations. Instead, use string
/// interpolation to create a new string by including values, literals,
/// variables, or expressions enclosed in parentheses, prefixed by a
/// backslash (`\(`...`)`).
///
/// let price = 2
/// let number = 3
/// let message = """
/// If one cookie costs \(price) dollars, \
/// \(number) cookies cost \(price * number) dollars.
/// """
/// print(message)
/// // Prints "If one cookie costs 2 dollars, 3 cookies cost 6 dollars."
@inlinable
public mutating func appendInterpolation<T>(_ value: T) {
_print_unlocked(value, &self)
}
/// Creates a string from this instance, consuming the instance in the
/// process.
@inlinable
internal __consuming func make() -> String {
return _storage
}
}
extension DefaultStringInterpolation: CustomStringConvertible {
@inlinable
public var description: String {
return _storage
}
}
extension DefaultStringInterpolation: TextOutputStream {
@inlinable
public mutating func write(_ string: String) {
_storage.append(string)
}
public mutating func _writeASCII(_ buffer: UnsafeBufferPointer<UInt8>) {
_storage._guts.append(_StringGuts(buffer, isASCII: true))
}
}
// While not strictly necessary, declaring these is faster than using the
// default implementation.
extension String {
/// Creates a new instance from an interpolated string literal.
///
/// Do not call this initializer directly. It is used by the compiler when
/// you create a string using string interpolation. Instead, use string
/// interpolation to create a new string by including values, literals,
/// variables, or expressions enclosed in parentheses, prefixed by a
/// backslash (`\(`...`)`).
///
/// let price = 2
/// let number = 3
/// let message = """
/// If one cookie costs \(price) dollars, \
/// \(number) cookies cost \(price * number) dollars.
/// """
/// print(message)
/// // Prints "If one cookie costs 2 dollars, 3 cookies cost 6 dollars."
@inlinable
@_effects(readonly)
public init(stringInterpolation: DefaultStringInterpolation) {
self = stringInterpolation.make()
}
}
extension Substring {
/// Creates a new instance from an interpolated string literal.
///
/// Do not call this initializer directly. It is used by the compiler when
/// you create a string using string interpolation. Instead, use string
/// interpolation to create a new string by including values, literals,
/// variables, or expressions enclosed in parentheses, prefixed by a
/// backslash (`\(`...`)`).
///
/// let price = 2
/// let number = 3
/// let message = """
/// If one cookie costs \(price) dollars, \
/// \(number) cookies cost \(price * number) dollars.
/// """
/// print(message)
/// // Prints "If one cookie costs 2 dollars, 3 cookies cost 6 dollars."
@inlinable
@_effects(readonly)
public init(stringInterpolation: DefaultStringInterpolation) {
self.init(stringInterpolation.make())
}
}