diff --git a/test/tnative.nim b/test/tnative.nim index 5595eab..839b77a 100644 --- a/test/tnative.nim +++ b/test/tnative.nim @@ -676,14 +676,16 @@ suite "Serialization": flowChild: AsFlow(a: "abc", b: "abc", c: "abc"), 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" & " a: 'abc',\n" & " b: \"abc\",\n" & " c: abc\n" & " }\n" & "blockChild:\n" & - " a: \"a\\\nc\"\n" & + " a: \"a\\nc\"\n" & " b: \"abc\"\n" & " c: \"ab:\"\n", output \ No newline at end of file diff --git a/test/tpresenter.nim b/test/tpresenter.nim index 4458420..960ad25 100644 --- a/test/tpresenter.nim +++ b/test/tpresenter.nim @@ -53,7 +53,7 @@ suite "Presenter": test "Forced multiline flow sequence": 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": var input = inputSingle(startMapEvent(), scalarEvent("1"), scalarEvent("2"), endMapEvent()) diff --git a/yaml/dumping.nim b/yaml/dumping.nim index 1f728fa..490c483 100644 --- a/yaml/dumping.nim +++ b/yaml/dumping.nim @@ -33,7 +33,8 @@ proc setMinimalStyle*(dumper: var Dumper) = suppressAttrs: false, quoting: sqJson, condenseFlow: true, - explicitKeys: false + explicitKeys: false, + maxLineLength: none(int) ) dumper.serialization = SerializationOptions( tagStyle: tsNone, diff --git a/yaml/native.nim b/yaml/native.nim index b8504ce..5b0cef9 100644 --- a/yaml/native.nim +++ b/yaml/native.nim @@ -1243,7 +1243,6 @@ proc recGenFieldRepresenters( )) when `fieldAccessor`.hasCustomPragma(scalar): ctx.overridingScalarStyle = `fieldAccessor`.getCustomPragmaVal(scalar) - echo "set scalar style to ", $ctx.overridingScalarStyle when `fieldAccessor`.hasCustomPragma(collection): ctx.overridingCollectionStyle = `fieldAccessor`.getCustomPragmaVal(collection) ctx.representChild(`fieldAccessor`) @@ -1281,7 +1280,6 @@ proc recGenFieldRepresenters( )) when `itemAccessor`.hasCustomPragma(scalar): ctx.overridingScalarStyle = `itemAccessor`.getCustomPragmaVal(scalar) - echo "set scalar style to ", $ctx.overridingScalarStyle when `itemAccessor`.hasCustomPragma(collection): ctx.overridingCollectionStyle = `itemAccessor`.getCustomPragmaVal(collection) ctx.representChild(`itemAccessor`) @@ -1307,7 +1305,6 @@ proc recGenFieldRepresenters( )) when `childAccessor`.hasCustomPragma(scalar): ctx.overridingScalarStyle = `childAccessor`.getCustomPragmaVal(scalar) - echo "set scalar style to ", $ctx.overridingScalarStyle when `childAccessor`.hasCustomPragma(collection): ctx.overridingCollectionStyle = `childAccessor`.getCustomPragmaVal(collection) ctx.representChild(`childAccessor`) diff --git a/yaml/presenter.nim b/yaml/presenter.nim index 6a1bf12..e3d0c97 100644 --- a/yaml/presenter.nim +++ b/yaml/presenter.nim @@ -111,9 +111,13 @@ type needsWhitespace: int wroteDirectivesEnd: bool lastImplicitKeyLen: int + cached: DeQue[Event] ItemKind = enum ikCompactScalar, ikMultilineFlowScalar, ikBlockScalar, ikCollection + + MapParseState = enum + mpInitial, mpKey, mpValue, mpNeedBlock proc level(ctx: var Context): var DumperLevel = ctx.levels[^1] @@ -595,7 +599,7 @@ proc startItem( of dFlowSequenceItem: ctx.append(',') ctx.whitespace(true) - if not ctx.options.condenseFlow: + if not ctx.level.singleLine: ctx.newline() t.write(repeat(' ', ctx.indentation)) except CatchableError as ce: @@ -644,6 +648,49 @@ proc nextItem( else: 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( ctx: var Context, s : YamlStream, @@ -651,12 +698,11 @@ proc doPresent( YamlPresenterJsonError, YamlPresenterOutputError, YamlStreamError ].} = - var - cached = initDeQue[Event]() - unclosedDoc = false + var unclosedDoc = false + ctx.cached = initDeQue[Event]() ctx.wroteDirectivesEnd = false while true: - let item = nextItem(cached, s) + let item = nextItem(ctx.cached, s) case item.kind of yamlStartStream: discard of yamlEndStream: break @@ -801,6 +847,7 @@ proc doPresent( e.parent = ce raise e of yamlStartSeq: + let (singleLine, _) = ctx.checkSingleLine(s, item, false) var nextState: DumperState if (ctx.levels.len > 0 and ctx.state.isFlow) or item.seqStyle == csFlow: nextState = dFlowSequenceStart @@ -810,31 +857,13 @@ proc doPresent( else: case ctx.options.containers of cMixed: - var length = 0 - 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 + nextState = if singleLine: dFlowSequenceStart else: dBlockSequenceItem of cFlow: nextState = dFlowSequenceStart of cBlock: let next = s.peek() - if next.kind == yamlEndSeq: nextState = dFlowSequenceStart - else: nextState = dBlockSequenceItem + nextState = if next.kind == yamlEndSeq: dFlowSequenceStart else: dBlockSequenceItem var indentation = 0 - var singleLine = ctx.options.condenseFlow or ctx.options.newlines == nlNone - var wroteAnything = false if ctx.levels.len > 0: wroteAnything = ctx.state == dBlockImplicitMapKey @@ -844,6 +873,8 @@ proc doPresent( else: ctx.startItem(ikCollection) 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) if wroteAttrs or (ctx.wroteDirectivesEnd and ctx.levels.len == 0): @@ -854,12 +885,9 @@ proc doPresent( if wroteAttrs or ctx.wroteDirectivesEnd: ctx.safeNewline() 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) of yamlStartMap: + let (singleLine, mps) = ctx.checkSingleLine(s, item, true) var nextState: DumperState if (ctx.levels.len > 0 and ctx.state.isFlow) or item.mapStyle == csFlow: nextState = dFlowMapStart @@ -869,27 +897,6 @@ proc doPresent( else: case ctx.options.containers 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: nextState = dBlockMapValue elif ctx.levels.len == 0 or ctx.state == dBlockSequenceItem and item.emptyProperties: @@ -903,8 +910,6 @@ proc doPresent( else: nextState = dBlockMapValue var indentation = 0 - var singleLine = ctx.options.condenseFlow or ctx.options.newlines == nlNone - var wroteAnything = false if ctx.levels.len > 0: wroteAnything = ctx.state == dBlockImplicitMapKey @@ -914,6 +919,8 @@ proc doPresent( else: ctx.startItem(ikCollection) 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) if wroteAttrs or (ctx.wroteDirectivesEnd and ctx.levels.len == 0): @@ -924,10 +931,6 @@ proc doPresent( if wroteAttrs or ctx.wroteDirectivesEnd: ctx.safeNewline() 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) of yamlEndSeq: yAssert ctx.levels.len > 0 @@ -937,7 +940,7 @@ proc doPresent( try: if not level.singleLine: ctx.newline() - ctx.target.write(repeat(' ', ctx.indentation)) + ctx.target.write(repeat(' ', level.indentation - ctx.options.indentationStep)) ctx.target.write(']') except CatchableError as ce: var e = newException(YamlPresenterOutputError, "") @@ -954,7 +957,7 @@ proc doPresent( try: if not level.singleLine: ctx.safeNewline() - ctx.target.write(repeat(' ', ctx.indentation)) + ctx.target.write(repeat(' ', level.indentation - ctx.options.indentationStep)) ctx.append('}') except CatchableError as ce: var e = newException(YamlPresenterOutputError, "")