Fixed presenter regarding new style annotations

* fixed presentation of flow mappings inside block collections
 * improved logic checking whether a collection can be written on a
   single line
 * fixed tests
This commit is contained in:
Felix Krause 2023-11-14 15:30:43 +01:00
parent a698289223
commit bf87d508d0
5 changed files with 68 additions and 65 deletions

View File

@ -676,14 +676,16 @@ suite "Serialization":
flowChild: AsFlow(a: "abc", b: "abc", c: "abc"), flowChild: AsFlow(a: "abc", b: "abc", c: "abc"),
blockChild: AsFlow(a: "a\nc", b: "abc", c: "ab:") blockChild: AsFlow(a: "a\nc", b: "abc", c: "ab:")
) )
var output = blockOnlyDumper().dump(input) var dumper = blockOnlyDumper()
dumper.presentation.maxLineLength = some(20)
var output = dumper.dump(input)
assertStringEqual "flowChild: {\n" & assertStringEqual "flowChild: {\n" &
" a: 'abc',\n" & " a: 'abc',\n" &
" b: \"abc\",\n" & " b: \"abc\",\n" &
" c: abc\n" & " c: abc\n" &
" }\n" & " }\n" &
"blockChild:\n" & "blockChild:\n" &
" a: \"a\\\nc\"\n" & " a: \"a\\nc\"\n" &
" b: \"abc\"\n" & " b: \"abc\"\n" &
" c: \"ab:\"\n", output " c: \"ab:\"\n", output

View File

@ -53,7 +53,7 @@ suite "Presenter":
test "Forced multiline flow sequence": test "Forced multiline flow sequence":
var input = inputSingle(startSeqEvent(), scalarEvent("1"), scalarEvent("2"), endSeqEvent()) var input = inputSingle(startSeqEvent(), scalarEvent("1"), scalarEvent("2"), endSeqEvent())
assertOutput(input, "[\n 1,\n 2\n]\n", PresentationOptions(outputVersion: ovNone, condenseFlow: false)) assertOutput(input, "[\n 1,\n 2\n]\n", PresentationOptions(outputVersion: ovNone, condenseFlow: false, containers: cFlow))
test "Compact flow mapping": test "Compact flow mapping":
var input = inputSingle(startMapEvent(), scalarEvent("1"), scalarEvent("2"), endMapEvent()) var input = inputSingle(startMapEvent(), scalarEvent("1"), scalarEvent("2"), endMapEvent())

View File

@ -33,7 +33,8 @@ proc setMinimalStyle*(dumper: var Dumper) =
suppressAttrs: false, suppressAttrs: false,
quoting: sqJson, quoting: sqJson,
condenseFlow: true, condenseFlow: true,
explicitKeys: false explicitKeys: false,
maxLineLength: none(int)
) )
dumper.serialization = SerializationOptions( dumper.serialization = SerializationOptions(
tagStyle: tsNone, tagStyle: tsNone,

View File

@ -1243,7 +1243,6 @@ proc recGenFieldRepresenters(
)) ))
when `fieldAccessor`.hasCustomPragma(scalar): when `fieldAccessor`.hasCustomPragma(scalar):
ctx.overridingScalarStyle = `fieldAccessor`.getCustomPragmaVal(scalar) ctx.overridingScalarStyle = `fieldAccessor`.getCustomPragmaVal(scalar)
echo "set scalar style to ", $ctx.overridingScalarStyle
when `fieldAccessor`.hasCustomPragma(collection): when `fieldAccessor`.hasCustomPragma(collection):
ctx.overridingCollectionStyle = `fieldAccessor`.getCustomPragmaVal(collection) ctx.overridingCollectionStyle = `fieldAccessor`.getCustomPragmaVal(collection)
ctx.representChild(`fieldAccessor`) ctx.representChild(`fieldAccessor`)
@ -1281,7 +1280,6 @@ proc recGenFieldRepresenters(
)) ))
when `itemAccessor`.hasCustomPragma(scalar): when `itemAccessor`.hasCustomPragma(scalar):
ctx.overridingScalarStyle = `itemAccessor`.getCustomPragmaVal(scalar) ctx.overridingScalarStyle = `itemAccessor`.getCustomPragmaVal(scalar)
echo "set scalar style to ", $ctx.overridingScalarStyle
when `itemAccessor`.hasCustomPragma(collection): when `itemAccessor`.hasCustomPragma(collection):
ctx.overridingCollectionStyle = `itemAccessor`.getCustomPragmaVal(collection) ctx.overridingCollectionStyle = `itemAccessor`.getCustomPragmaVal(collection)
ctx.representChild(`itemAccessor`) ctx.representChild(`itemAccessor`)
@ -1307,7 +1305,6 @@ proc recGenFieldRepresenters(
)) ))
when `childAccessor`.hasCustomPragma(scalar): when `childAccessor`.hasCustomPragma(scalar):
ctx.overridingScalarStyle = `childAccessor`.getCustomPragmaVal(scalar) ctx.overridingScalarStyle = `childAccessor`.getCustomPragmaVal(scalar)
echo "set scalar style to ", $ctx.overridingScalarStyle
when `childAccessor`.hasCustomPragma(collection): when `childAccessor`.hasCustomPragma(collection):
ctx.overridingCollectionStyle = `childAccessor`.getCustomPragmaVal(collection) ctx.overridingCollectionStyle = `childAccessor`.getCustomPragmaVal(collection)
ctx.representChild(`childAccessor`) ctx.representChild(`childAccessor`)

View File

@ -111,9 +111,13 @@ type
needsWhitespace: int needsWhitespace: int
wroteDirectivesEnd: bool wroteDirectivesEnd: bool
lastImplicitKeyLen: int lastImplicitKeyLen: int
cached: DeQue[Event]
ItemKind = enum ItemKind = enum
ikCompactScalar, ikMultilineFlowScalar, ikBlockScalar, ikCollection ikCompactScalar, ikMultilineFlowScalar, ikBlockScalar, ikCollection
MapParseState = enum
mpInitial, mpKey, mpValue, mpNeedBlock
proc level(ctx: var Context): var DumperLevel = ctx.levels[^1] proc level(ctx: var Context): var DumperLevel = ctx.levels[^1]
@ -595,7 +599,7 @@ proc startItem(
of dFlowSequenceItem: of dFlowSequenceItem:
ctx.append(',') ctx.append(',')
ctx.whitespace(true) ctx.whitespace(true)
if not ctx.options.condenseFlow: if not ctx.level.singleLine:
ctx.newline() ctx.newline()
t.write(repeat(' ', ctx.indentation)) t.write(repeat(' ', ctx.indentation))
except CatchableError as ce: except CatchableError as ce:
@ -644,6 +648,49 @@ proc nextItem(
else: else:
result = s.next() result = s.next()
proc checkSingleLine(
ctx : var Context,
s : YamlStream,
item : Event,
mapping: bool
): (bool, MapParseState) =
let style = if mapping: item.mapStyle else: item.seqStyle
if ctx.options.newlines == nlNone: return (true, mpInitial)
if not ctx.options.condenseFlow: return (false, mpNeedBlock)
if (ctx.levels.len > 0 and ctx.state.isFlow) or style == csFlow or
(style == csAny and ctx.options.containers != cBlock):
result[1] = mpInitial
var length = 0
while not mapping or result[1] != mpNeedBlock:
let next = s.next()
ctx.cached.addLast(next)
case next.kind
of yamlScalar:
length += 2 + next.scalarContent.len
if next.scalarStyle in [ssFolded, ssLiteral]:
length = high(int)
result[1] = mpNeedBlock
break
case result[1]
of mpInitial: result[1] = mpKey
of mpKey: result[1] = mpValue
else: result[1] = mpNeedBlock
of yamlAlias:
length += 6
case result[1]
of mpInitial: result[1] = mpKey
of mpKey: result[1] = mpValue
else: result[1] = mpNeedBlock
of yamlEndSeq, yamlEndMap: break
else:
length = high(int)
result[1] = mpNeedBlock
break
result[0] = (not mapping or result[1] != mpNeedBlock) and
(ctx.options.maxLineLength.isNone or
length < ctx.options.maxLineLength.get() - ctx.indentation - 2)
else: result = (false, mpNeedBlock)
proc doPresent( proc doPresent(
ctx: var Context, ctx: var Context,
s : YamlStream, s : YamlStream,
@ -651,12 +698,11 @@ proc doPresent(
YamlPresenterJsonError, YamlPresenterOutputError, YamlPresenterJsonError, YamlPresenterOutputError,
YamlStreamError YamlStreamError
].} = ].} =
var var unclosedDoc = false
cached = initDeQue[Event]() ctx.cached = initDeQue[Event]()
unclosedDoc = false
ctx.wroteDirectivesEnd = false ctx.wroteDirectivesEnd = false
while true: while true:
let item = nextItem(cached, s) let item = nextItem(ctx.cached, s)
case item.kind case item.kind
of yamlStartStream: discard of yamlStartStream: discard
of yamlEndStream: break of yamlEndStream: break
@ -801,6 +847,7 @@ proc doPresent(
e.parent = ce e.parent = ce
raise e raise e
of yamlStartSeq: of yamlStartSeq:
let (singleLine, _) = ctx.checkSingleLine(s, item, false)
var nextState: DumperState var nextState: DumperState
if (ctx.levels.len > 0 and ctx.state.isFlow) or item.seqStyle == csFlow: if (ctx.levels.len > 0 and ctx.state.isFlow) or item.seqStyle == csFlow:
nextState = dFlowSequenceStart nextState = dFlowSequenceStart
@ -810,31 +857,13 @@ proc doPresent(
else: else:
case ctx.options.containers case ctx.options.containers
of cMixed: of cMixed:
var length = 0 nextState = if singleLine: dFlowSequenceStart else: dBlockSequenceItem
while true:
let next = s.next()
cached.addLast(next)
case next.kind
of yamlScalar:
length += 2 + next.scalarContent.len
if next.scalarStyle in [ssFolded, ssLiteral]:
length = high(int)
break
of yamlAlias: length += 6
of yamlEndSeq: break
else:
length = high(int)
break
nextState = if length <= 60: dFlowSequenceStart else: dBlockSequenceItem
of cFlow: nextState = dFlowSequenceStart of cFlow: nextState = dFlowSequenceStart
of cBlock: of cBlock:
let next = s.peek() let next = s.peek()
if next.kind == yamlEndSeq: nextState = dFlowSequenceStart nextState = if next.kind == yamlEndSeq: dFlowSequenceStart else: dBlockSequenceItem
else: nextState = dBlockSequenceItem
var indentation = 0 var indentation = 0
var singleLine = ctx.options.condenseFlow or ctx.options.newlines == nlNone
var wroteAnything = false var wroteAnything = false
if ctx.levels.len > 0: wroteAnything = ctx.state == dBlockImplicitMapKey if ctx.levels.len > 0: wroteAnything = ctx.state == dBlockImplicitMapKey
@ -844,6 +873,8 @@ proc doPresent(
else: else:
ctx.startItem(ikCollection) ctx.startItem(ikCollection)
indentation = ctx.indentation + ctx.options.indentationStep indentation = ctx.indentation + ctx.options.indentationStep
if nextState.isFlow and not ctx.state.isFlow:
inc(indentation, ctx.options.indentationStep)
let wroteAttrs = ctx.writeTagAndAnchor(item.seqProperties) let wroteAttrs = ctx.writeTagAndAnchor(item.seqProperties)
if wroteAttrs or (ctx.wroteDirectivesEnd and ctx.levels.len == 0): if wroteAttrs or (ctx.wroteDirectivesEnd and ctx.levels.len == 0):
@ -854,12 +885,9 @@ proc doPresent(
if wroteAttrs or ctx.wroteDirectivesEnd: ctx.safeNewline() if wroteAttrs or ctx.wroteDirectivesEnd: ctx.safeNewline()
ctx.safeWrite('[') ctx.safeWrite('[')
if ctx.levels.len > 0 and not ctx.options.condenseFlow and
ctx.state in [dBlockExplicitMapKey, dBlockMapValue,
dBlockImplicitMapKey, dBlockSequenceItem]:
if ctx.options.newlines != nlNone: singleLine = false
ctx.levels.add (nextState, indentation, singleLine, wroteAnything) ctx.levels.add (nextState, indentation, singleLine, wroteAnything)
of yamlStartMap: of yamlStartMap:
let (singleLine, mps) = ctx.checkSingleLine(s, item, true)
var nextState: DumperState var nextState: DumperState
if (ctx.levels.len > 0 and ctx.state.isFlow) or item.mapStyle == csFlow: if (ctx.levels.len > 0 and ctx.state.isFlow) or item.mapStyle == csFlow:
nextState = dFlowMapStart nextState = dFlowMapStart
@ -869,27 +897,6 @@ proc doPresent(
else: else:
case ctx.options.containers case ctx.options.containers
of cMixed: of cMixed:
type MapParseState = enum
mpInitial, mpKey, mpValue, mpNeedBlock
var mps: MapParseState = mpInitial
while mps != mpNeedBlock:
let next = s.next()
cached.addLast(next)
case next.kind
of yamlScalar:
case mps
of mpInitial: mps = mpKey
of mpKey: mps = mpValue
else: mps = mpNeedBlock
if next.scalarStyle in [ssFolded, ssLiteral]:
mps = mpNeedBlock
of yamlAlias:
case mps
of mpInitial: mps = mpKey
of mpKey: mps = mpValue
else: mps = mpNeedBlock
of yamlEndMap: break
else: mps = mpNeedBlock
if mps == mpNeedBlock: if mps == mpNeedBlock:
nextState = dBlockMapValue nextState = dBlockMapValue
elif ctx.levels.len == 0 or ctx.state == dBlockSequenceItem and item.emptyProperties: elif ctx.levels.len == 0 or ctx.state == dBlockSequenceItem and item.emptyProperties:
@ -903,8 +910,6 @@ proc doPresent(
else: nextState = dBlockMapValue else: nextState = dBlockMapValue
var indentation = 0 var indentation = 0
var singleLine = ctx.options.condenseFlow or ctx.options.newlines == nlNone
var wroteAnything = false var wroteAnything = false
if ctx.levels.len > 0: wroteAnything = ctx.state == dBlockImplicitMapKey if ctx.levels.len > 0: wroteAnything = ctx.state == dBlockImplicitMapKey
@ -914,6 +919,8 @@ proc doPresent(
else: else:
ctx.startItem(ikCollection) ctx.startItem(ikCollection)
indentation = ctx.indentation + ctx.options.indentationStep indentation = ctx.indentation + ctx.options.indentationStep
if nextState.isFlow and not ctx.state.isFlow:
inc(indentation, ctx.options.indentationStep)
let wroteAttrs = ctx.writeTagAndAnchor(item.properties) let wroteAttrs = ctx.writeTagAndAnchor(item.properties)
if wroteAttrs or (ctx.wroteDirectivesEnd and ctx.levels.len == 0): if wroteAttrs or (ctx.wroteDirectivesEnd and ctx.levels.len == 0):
@ -924,10 +931,6 @@ proc doPresent(
if wroteAttrs or ctx.wroteDirectivesEnd: ctx.safeNewline() if wroteAttrs or ctx.wroteDirectivesEnd: ctx.safeNewline()
ctx.safeWrite('{') ctx.safeWrite('{')
if ctx.levels.len > 0 and not ctx.options.condenseFlow and
ctx.state in [dBlockExplicitMapKey, dBlockMapValue,
dBlockImplicitMapKey, dBlockSequenceItem]:
if ctx.options.newlines != nlNone: singleLine = false
ctx.levels.add (nextState, indentation, singleLine, wroteAnything) ctx.levels.add (nextState, indentation, singleLine, wroteAnything)
of yamlEndSeq: of yamlEndSeq:
yAssert ctx.levels.len > 0 yAssert ctx.levels.len > 0
@ -937,7 +940,7 @@ proc doPresent(
try: try:
if not level.singleLine: if not level.singleLine:
ctx.newline() ctx.newline()
ctx.target.write(repeat(' ', ctx.indentation)) ctx.target.write(repeat(' ', level.indentation - ctx.options.indentationStep))
ctx.target.write(']') ctx.target.write(']')
except CatchableError as ce: except CatchableError as ce:
var e = newException(YamlPresenterOutputError, "") var e = newException(YamlPresenterOutputError, "")
@ -954,7 +957,7 @@ proc doPresent(
try: try:
if not level.singleLine: if not level.singleLine:
ctx.safeNewline() ctx.safeNewline()
ctx.target.write(repeat(' ', ctx.indentation)) ctx.target.write(repeat(' ', level.indentation - ctx.options.indentationStep))
ctx.append('}') ctx.append('}')
except CatchableError as ce: except CatchableError as ce:
var e = newException(YamlPresenterOutputError, "") var e = newException(YamlPresenterOutputError, "")