///|
fn triple_at(source : String, start : Int, end : Int) -> Bool {
start + 3 <= end &&
source[start] == '"' &&
source[start + 1] == '"' &&
source[start + 2] == '"'
}
///|
/// Extended dialect only: literal triple-double-quoted values; no escapes.
/// Semantic newlines inside the value remain exactly CR/LF/CRLF as supplied.
fn decode_multiline(
source : String,
line : Line,
start : Int,
dialect : Dialect,
diagnostics : Array[Diagnostic],
) -> (String, Span, Int) {
let mut close = start + 3
while close < source.length() && !triple_at(source, close, source.length()) {
close += 1
}
if close == source.length() {
diagnostics.push(
diagnostic(
"INI010",
"unclosed triple-quoted value",
line_span(line, start, close),
),
)
return (
text(source, start + 3, close),
line_span(line, start, close),
close,
)
}
let value_end = close + 3
let mut end = value_end
while end < source.length() && source[end] != '\r' && source[end] != '\n' {
end += 1
}
let (tail, _) = trim_bounds(source, value_end, end)
if tail < end &&
!(dialect.inline_comments &&
tail > value_end &&
is_comment(source[tail], dialect)) {
let prefix = text(source, line.start, tail)
let suffix_lines = scan_lines(prefix)
let last = suffix_lines[suffix_lines.length() - 1]
diagnostics.push(
diagnostic(
"INI007",
"unexpected text after multiline value",
span(
tail,
end,
line.number + last.number - 1,
tail - line.start - last.start + 1,
),
),
)
}
if end < source.length() {
if source[end] == '\r' &&
end + 1 < source.length() &&
source[end + 1] == '\n' {
end += 2
} else {
end += 1
}
}
(text(source, start + 3, close), line_span(line, start, value_end), end)
}