Improved presenter.

* added maxLineLength to PresentationOptions. Fixes #119
 * write a newline to the end of the document.
   required for the output files to be POSIX-compliant text files.
   also improves QoL when dumping to stdout.
This commit is contained in:
Felix Krause 2022-09-07 15:11:59 +02:00
parent a5552a1a18
commit 5f7677d914
3 changed files with 57 additions and 42 deletions

12
flake.lock generated
View File

@ -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": {

View File

@ -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

View File

@ -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)