diff --git a/flake.lock b/flake.lock index b0ce74a..8317275 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nix-filter": { "locked": { - "lastModified": 1653590866, - "narHash": "sha256-E4yKIrt/S//WfW5D9IhQ1dVuaAy8RE7EiCMfnbrOC78=", + "lastModified": 1661201956, + "narHash": "sha256-RizGJH/buaw9A2+fiBf9WnXYw4LZABB5kMAZIEE5/T8=", "owner": "numtide", "repo": "nix-filter", - "rev": "3e81a637cdf9f6e9b39aeb4d6e6394d1ad158e16", + "rev": "3b821578685d661a10b563cba30b1861eec05748", "type": "github" }, "original": { @@ -40,11 +40,11 @@ }, "utils": { "locked": { - "lastModified": 1653893745, - "narHash": "sha256-0jntwV3Z8//YwuOjzhV2sgJJPt+HY6KhU7VZUL0fKZQ=", + "lastModified": 1659877975, + "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=", "owner": "numtide", "repo": "flake-utils", - "rev": "1ed9fb1935d260de5fe1c2f7ee0ebaae17ed2fa1", + "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0", "type": "github" }, "original": { diff --git a/test/tserialization.nim b/test/tserialization.nim index c0bbcde..46b15b8 100644 --- a/test/tserialization.nim +++ b/test/tserialization.nim @@ -147,7 +147,7 @@ suite "Serialization": test "Dump integer without fixed length": var input = -4247 var output = dump(input, tsNone, asTidy, blockOnly) - assertStringEqual yamlDirs & "\n\"-4247\"", output + assertStringEqual yamlDirs & "\n\"-4247\"\n", output when sizeof(int) == sizeof(int64): input = int(int32.high) + 1 @@ -232,7 +232,7 @@ suite "Serialization": test "Dump string sequence": var input = @["a", "b"] var output = dump(input, tsNone, asTidy, blockOnly) - assertStringEqual yamlDirs & "\n- a\n- b", output + assertStringEqual yamlDirs & "\n- a\n- b\n", output test "Load char set": let input = "- a\n- b" @@ -245,7 +245,7 @@ suite "Serialization": test "Dump char set": var input = {'a', 'b'} var output = dump(input, tsNone, asTidy, blockOnly) - assertStringEqual yamlDirs & "\n- a\n- b", output + assertStringEqual yamlDirs & "\n- a\n- b\n", output test "Load array": let input = "- 23\n- 42\n- 47" @@ -258,7 +258,7 @@ suite "Serialization": test "Dump array": let input = [23'i32, 42'i32, 47'i32] var output = dump(input, tsNone, asTidy, blockOnly) - assertStringEqual yamlDirs & "\n- 23\n- 42\n- 47", output + assertStringEqual yamlDirs & "\n- 23\n- 42\n- 47\n", output test "Load Option": let input = "- Some\n- !!null ~" @@ -271,7 +271,7 @@ suite "Serialization": test "Dump Option": let input = [none(int32), some(42'i32), none(int32)] let output = dump(input, tsNone, asTidy, blockOnly) - assertStringEqual yamlDirs & "\n- !!null ~\n- 42\n- !!null ~", output + assertStringEqual yamlDirs & "\n- !!null ~\n- 42\n- !!null ~\n", output test "Load Table[int, string]": let input = "23: dreiundzwanzig\n42: zweiundvierzig" @@ -286,7 +286,7 @@ suite "Serialization": input[23] = "dreiundzwanzig" input[42] = "zweiundvierzig" var output = dump(input, tsNone, asTidy, blockOnly) - assertStringEqual(yamlDirs & "\n23: dreiundzwanzig\n42: zweiundvierzig", + assertStringEqual(yamlDirs & "\n23: dreiundzwanzig\n42: zweiundvierzig\n", output) test "Load OrderedTable[tuple[int32, int32], string]": @@ -321,7 +321,7 @@ suite "Serialization": " ? \n" & " a: 13\n" & " b: 47\n" & - " : dreizehnsiebenundvierzig", output) + " : dreizehnsiebenundvierzig\n", output) test "Load Sequences in Sequence": let input = " - [1, 2, 3]\n - [4, 5]\n - [6]" @@ -335,7 +335,7 @@ suite "Serialization": test "Dump Sequences in Sequence": let input = @[@[1.int32, 2.int32, 3.int32], @[4.int32, 5.int32], @[6.int32]] var output = dump(input, tsNone) - assertStringEqual yamlDirs & "\n- [1, 2, 3]\n- [4, 5]\n- [6]", output + assertStringEqual yamlDirs & "\n- [1, 2, 3]\n- [4, 5]\n- [6]\n", output test "Load Enum": let input = @@ -350,7 +350,7 @@ suite "Serialization": test "Dump Enum": let input = @[tlRed, tlGreen, tlYellow] var output = dump(input, tsNone, asTidy, blockOnly) - assertStringEqual yamlDirs & "\n- tlRed\n- tlGreen\n- tlYellow", output + assertStringEqual yamlDirs & "\n- tlRed\n- tlGreen\n- tlYellow\n", output test "Load Tuple": let input = "str: value\ni: 42\nb: true" @@ -363,7 +363,7 @@ suite "Serialization": test "Dump Tuple": let input = (str: "value", i: 42.int32, b: true) var output = dump(input, tsNone) - assertStringEqual yamlDirs & "\nstr: value\ni: 42\nb: true", output + assertStringEqual yamlDirs & "\nstr: value\ni: 42\nb: true\n", output test "Load Tuple - unknown field": let input = "str: value\nfoo: bar\ni: 42\nb: true" @@ -410,7 +410,7 @@ suite "Serialization": let input = Person(firstnamechar: 'P', surname: "Pan", age: 12) var output = dump(input, tsNone, asTidy, blockOnly) assertStringEqual(yamlDirs & - "\nfirstnamechar: P\nsurname: Pan\nage: 12", output) + "\nfirstnamechar: P\nsurname: Pan\nage: 12\n", output) test "Load custom object - unknown field": let input = " firstnamechar: P\n surname: Pan\n age: 12\n occupation: free" @@ -442,7 +442,7 @@ suite "Serialization": let input = @["one", "two"] var output = dump(input, tsAll, asTidy, blockOnly) assertStringEqual(yamlDirs & "!n!system:seq(" & - "tag:yaml.org;2002:str) \n- !!str one\n- !!str two", output) + "tag:yaml.org;2002:str) \n- !!str one\n- !!str two\n", output) test "Load custom object with explicit root tag": let input = @@ -457,7 +457,7 @@ suite "Serialization": let input = Person(firstnamechar: 'P', surname: "Pan", age: 12) var output = dump(input, tsRootOnly, asTidy, blockOnly) assertStringEqual(yamlDirs & - "!n!custom:Person \nfirstnamechar: P\nsurname: Pan\nage: 12", output) + "!n!custom:Person \nfirstnamechar: P\nsurname: Pan\nage: 12\n", output) test "Load custom variant object": let input = @@ -491,7 +491,7 @@ suite "Serialization": " - \n" & " kind: akDog\n" & " - \n" & - " barkometer: 13", output + " barkometer: 13\n", output test "Load custom variant object - missing field": let input = "[{name: Bastet}, {kind: akCat}]" @@ -517,7 +517,7 @@ suite "Serialization": test "Dump non-variant object with transient fields": let input = NonVariantWithTransient(a: "a", b: "b", c: "c", d: "d") let output = dump(input, tsNone, asTidy, blockOnly) - assertStringEqual yamlDirs & "\nb: b\nd: d", output + assertStringEqual yamlDirs & "\nb: b\nd: d\n", output test "Load variant object with transient fields": let input = "[[gStorable: gs, kind: deA, cStorable: cs], [gStorable: a, kind: deC]]" @@ -554,7 +554,7 @@ suite "Serialization": " - \n" & " gStorable: a\n" & " - \n" & - " kind: deC", output + " kind: deC\n", output test "Load object with ignored key": let input = "[{x: 1, y: 2}, {x: 3, z: 4, y: 5}, {z: [1, 2, 3], x: 4, y: 5}]" @@ -590,7 +590,7 @@ suite "Serialization": " value: b\n" & " next: \n" & " value: c\n" & - " next: *a", output + " next: *a\n", output test "Load cyclic data structure": let input = yamlDirs & """!n!system:seq(example.net:Node) @@ -652,4 +652,4 @@ suite "Serialization": assertStringEqual yamlDirs & "!n!system:seq(test:BetterInt) \n" & "- !test:BetterInt 1\n" & "- !test:BetterInt 9_998_887\n" & - "- !test:BetterInt 98_312", output + "- !test:BetterInt 98_312\n", output diff --git a/yaml/presenter.nim b/yaml/presenter.nim index ad9c5d9..4bf32e0 100644 --- a/yaml/presenter.nim +++ b/yaml/presenter.nim @@ -10,7 +10,7 @@ ## ## This is the presenter API, used for generating YAML character streams. -import streams, deques, strutils +import std / [streams, deques, strutils, options] import data, taglib, stream, private/internal, hints, parser type @@ -85,6 +85,7 @@ type indentationStep*: int newlines*: NewLineStyle outputVersion*: OutputYamlVersion + maxLineLength*: Option[int] YamlPresenterJsonError* = object of ValueError ## Exception that may be raised by the YAML presenter when it is @@ -116,17 +117,19 @@ type const defaultPresentationOptions* = PresentationOptions(style: psDefault, indentationStep: 2, - newlines: nlOSDefault) + newlines: nlOSDefault, maxLineLength: some(80)) proc defineOptions*(style: PresentationStyle = psDefault, indentationStep: int = 2, newlines: NewLineStyle = nlOSDefault, - outputVersion: OutputYamlVersion = ov1_2): + outputVersion: OutputYamlVersion = ov1_2, + maxLineLength: Option[int] = some(80)): PresentationOptions {.raises: [].} = ## Define a set of options for presentation. Convenience proc that requires ## you to only set those values that should not equal the default. PresentationOptions(style: style, indentationStep: indentationStep, - newlines: newlines, outputVersion: outputVersion) + newlines: newlines, outputVersion: outputVersion, + maxLineLength: maxLineLength) proc state(c: Context): DumperState = c.levels[^1] @@ -147,7 +150,8 @@ proc searchHandle(c: Context, tag: string): result.handle = item.handle proc inspect(scalar: string, indentation: int, - words, lines: var seq[tuple[start, finish: int]]): + words, lines: var seq[tuple[start, finish: int]], + lineLength: Option[int]): ScalarStyle {.raises: [].} = var inLine = false @@ -177,7 +181,8 @@ proc inspect(scalar: string, indentation: int, of '\l': canUsePlain = false # we don't use multiline plain scalars curWord.finish = i - 1 - if curWord.finish - curWord.start + 1 > 80 - indentation: + if lineLength.isSome and + curWord.finish - curWord.start + 1 > lineLength.get() - indentation: return if canUsePlain: sPlain else: sDoubleQuoted words.add(curWord) inWord = false @@ -186,7 +191,8 @@ proc inspect(scalar: string, indentation: int, if not inLine: curLine.start = i inLine = false curLine.finish = i - 1 - if curLine.finish - curLine.start + 1 > 80 - indentation: + if lineLength.isSome and + curLine.finish - curLine.start + 1 > lineLength.get() - indentation: canUseLiteral = false lines.add(curLine) else: @@ -197,7 +203,8 @@ proc inspect(scalar: string, indentation: int, inLine = true if not inWord: if not multipleSpaces: - if curWord.finish - curWord.start + 1 > 80 - indentation: + if lineLength.isSome and + curWord.finish - curWord.start + 1 > lineLength.get() - indentation: return if canUsePlain: sPlain else: sDoubleQuoted words.add(curWord) curWord.start = i @@ -205,15 +212,17 @@ proc inspect(scalar: string, indentation: int, multipleSpaces = false if inWord: curWord.finish = scalar.len - 1 - if curWord.finish - curWord.start + 1 > 80 - indentation: + if lineLength.isSome and + curWord.finish - curWord.start + 1 > lineLength.get() - indentation: return if canUsePlain: sPlain else: sDoubleQuoted words.add(curWord) if inLine: curLine.finish = scalar.len - 1 - if curLine.finish - curLine.start + 1 > 80 - indentation: + if lineLength.isSome and + curLine.finish - curLine.start + 1 > lineLength.get() - indentation: canUseLiteral = false lines.add(curLine) - if scalar.len <= 80 - indentation: + if lineLength.isNone or scalar.len <= lineLength.get() - indentation: result = if canUsePlain: sPlain else: sDoubleQuoted elif canUseLiteral: result = sLiteral elif canUseFolded: result = sFolded @@ -227,7 +236,7 @@ template append(target: ptr[string], val: string | char) = target[].add(val) proc writeDoubleQuoted(c: Context, scalar: string, indentation: int, - newline: string) + newline: string, lineLength: Option[int]) {.raises: [YamlPresenterOutputError].} = var curPos = indentation let t = c.target @@ -235,7 +244,7 @@ proc writeDoubleQuoted(c: Context, scalar: string, indentation: int, t.append('"') curPos.inc() for c in scalar: - if curPos == 79: + if lineLength.isSome and curPos == lineLength.get() - 1: t.append('\\') t.append(newline) t.append(repeat(' ', indentation)) @@ -316,17 +325,19 @@ proc writeFolded(c: Context, scalar: string, indentation, indentStep: int, newline: string) {.raises: [YamlPresenterOutputError].} = let t = c.target + let lineLength = ( + if c.options.maxLineLength.isSome: c.options.maxLineLength.get() else: 1024) try: t.append(">") if scalar[^1] != '\l': t.append('-') if scalar[0] in [' ', '\t']: t.append($indentStep) - var curPos = 80 + var curPos = lineLength for word in words: if word.start > 0 and scalar[word.start - 1] == '\l': t.append(newline & newline) t.append(repeat(' ', indentation + indentStep)) curPos = indentation + indentStep - elif curPos + (word.finish - word.start + 1) > 80: + elif curPos + (word.finish - word.start + 1) > lineLength: t.append(newline) t.append(repeat(' ', indentation + indentStep)) curPos = indentation + indentStep @@ -528,18 +539,21 @@ proc doPresent(c: var Context, s: var YamlStream) = else: c.writeDoubleQuotedJson(item.scalarContent) elif c.options.style == psCanonical: c.writeDoubleQuoted(item.scalarContent, - indentation + c.options.indentationStep, newline) + indentation + c.options.indentationStep, newline, + c.options.maxLineLength) else: var words, lines = newSeq[tuple[start, finish: int]]() case item.scalarContent.inspect( - indentation + c.options.indentationStep, words, lines) + indentation + c.options.indentationStep, words, lines, + c.options.maxLineLength) of sLiteral: c.writeLiteral(item.scalarContent, indentation, c.options.indentationStep, lines, newline) of sFolded: c.writeFolded(item.scalarContent, indentation, c.options.indentationStep, words, newline) of sPlain: c.safeWrite(item.scalarContent) of sDoubleQuoted: c.writeDoubleQuoted(item.scalarContent, - indentation + c.options.indentationStep, newline) + indentation + c.options.indentationStep, newline, + c.options.maxLineLength) of yamlAlias: if c.options.style == psJson: raise newException(YamlPresenterJsonError, @@ -727,6 +741,7 @@ proc doPresent(c: var Context, s: var YamlStream) = indentation -= c.options.indentationStep of yamlEndDoc: firstDoc = false + c.safeWrite(newline) proc present*(s: var YamlStream, target: Stream, options: PresentationOptions = defaultPresentationOptions)