Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Plain Style support #557

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
72 changes: 43 additions & 29 deletions lib/js-yaml/dumper.js
Expand Up @@ -10,6 +10,7 @@ var DEFAULT_SAFE_SCHEMA = require('./schema/default_safe');
var _toString = Object.prototype.toString;
var _hasOwnProperty = Object.prototype.hasOwnProperty;

var CHAR_BOM = 0xFEFF;
var CHAR_TAB = 0x09; /* Tab */
var CHAR_LINE_FEED = 0x0A; /* LF */
var CHAR_CARRIAGE_RETURN = 0x0D; /* CR */
Expand Down Expand Up @@ -186,47 +187,60 @@ function isWhitespace(c) {
function isPrintable(c) {
return (0x00020 <= c && c <= 0x00007E)
|| ((0x000A1 <= c && c <= 0x00D7FF) && c !== 0x2028 && c !== 0x2029)
|| ((0x0E000 <= c && c <= 0x00FFFD) && c !== 0xFEFF /* BOM */)
|| ((0x0E000 <= c && c <= 0x00FFFD) && c !== CHAR_BOM)
|| (0x10000 <= c && c <= 0x10FFFF);
}

// [34] ns-char ::= nb-char - s-white
// [27] nb-char ::= c-printable - b-char - c-byte-order-mark
// [26] b-char ::= b-line-feed | b-carriage-return
// [24] b-line-feed ::= #xA /* LF */
// [25] b-carriage-return ::= #xD /* CR */
// [3] c-byte-order-mark ::= #xFEFF
function isNsChar(c) {
return isPrintable(c) && !isWhitespace(c)
// byte-order-mark
&& c !== 0xFEFF
// b-char
// Including s-white (for some reason, examples doesn't match specs in this aspect)
// ns-char ::= c-printable - b-line-feed - b-carriage-return - c-byte-order-mark
function isNsCharOrWhitespace(c) {
return isPrintable(c)
&& c !== CHAR_BOM
// - b-char
&& c !== CHAR_CARRIAGE_RETURN
&& c !== CHAR_LINE_FEED;
}

// Simplified test for values allowed after the first character in plain style.
function isPlainSafe(c, prev) {
// Uses a subset of nb-char - c-flow-indicator - ":" - "#"
// where nb-char ::= c-printable - b-char - c-byte-order-mark.
return isPrintable(c) && c !== 0xFEFF
// - c-flow-indicator
&& c !== CHAR_COMMA
&& c !== CHAR_LEFT_SQUARE_BRACKET
&& c !== CHAR_RIGHT_SQUARE_BRACKET
&& c !== CHAR_LEFT_CURLY_BRACKET
&& c !== CHAR_RIGHT_CURLY_BRACKET
// - ":" - "#"
// /* An ns-char preceding */ "#"
&& c !== CHAR_COLON
&& ((c !== CHAR_SHARP) || (prev && isNsChar(prev)));
// [127] ns-plain-safe(c) ::= c = flow-out ⇒ ns-plain-safe-out
// c = flow-in ⇒ ns-plain-safe-in
// c = block-key ⇒ ns-plain-safe-out
// c = flow-key ⇒ ns-plain-safe-in
// [128] ns-plain-safe-out ::= ns-char
// [129] ns-plain-safe-in ::= ns-char - c-flow-indicator
// [130] ns-plain-char(c) ::= ( ns-plain-safe(c) - “:” - “#” )
// | ( /* An ns-char preceding */ “#” )
// | ( “:” /* Followed by an ns-plain-safe(c) */ )
function isPlainSafe(state, c, prev) {
var cIsNsCharOrWhitespace = isNsCharOrWhitespace(c);
var cIsNsChar = cIsNsCharOrWhitespace && !isWhitespace(c);
return (
// ns-plain-safe
state.flowLevel < 0 ? // c = flow-in
cIsNsCharOrWhitespace
: cIsNsCharOrWhitespace
// - c-flow-indicator
&& c !== CHAR_COMMA
&& c !== CHAR_LEFT_SQUARE_BRACKET
&& c !== CHAR_RIGHT_SQUARE_BRACKET
&& c !== CHAR_LEFT_CURLY_BRACKET
&& c !== CHAR_RIGHT_CURLY_BRACKET
)
// ns-plain-char
&& c !== CHAR_SHARP // false on '#'
&& !(prev === CHAR_COLON && !cIsNsChar) // false on ': '
|| (isNsCharOrWhitespace(prev) && !isWhitespace(prev) && c === CHAR_SHARP) // change to true on '[^ ]#'
|| (prev === CHAR_COLON && cIsNsChar); // change to true on ':[^ ]'
}

// Simplified test for values allowed as the first character in plain style.
function isPlainSafeFirst(c) {
// Uses a subset of ns-char - c-indicator
// where ns-char = nb-char - s-white.
return isPrintable(c) && c !== 0xFEFF
// No support of ( ( “?” | “:” | “-” ) /* Followed by an ns-plain-safe(c)) */ ) part
return isPrintable(c) && c !== CHAR_BOM
&& !isWhitespace(c) // - s-white
// - (c-indicator ::=
// “-” | “?” | “:” | “,” | “[” | “]” | “{” | “}”
Expand Down Expand Up @@ -273,7 +287,7 @@ var STYLE_PLAIN = 1,
// STYLE_PLAIN or STYLE_SINGLE => no \n are in the string.
// STYLE_LITERAL => no lines are suitable for folding (or lineWidth is -1).
// STYLE_FOLDED => a line > lineWidth and can be folded (and lineWidth != -1).
function chooseScalarStyle(string, singleLineOnly, indentPerLevel, lineWidth, testAmbiguousType) {
function chooseScalarStyle(state, string, singleLineOnly, indentPerLevel, lineWidth, testAmbiguousType) {
var i;
var char, prev_char;
var hasLineBreak = false;
Expand All @@ -292,7 +306,7 @@ function chooseScalarStyle(string, singleLineOnly, indentPerLevel, lineWidth, te
return STYLE_DOUBLE;
}
prev_char = i > 0 ? string.charCodeAt(i - 1) : null;
plain = plain && isPlainSafe(char, prev_char);
plain = plain && isPlainSafe(state, char, prev_char);
}
} else {
// Case: block styles permitted.
Expand All @@ -312,7 +326,7 @@ function chooseScalarStyle(string, singleLineOnly, indentPerLevel, lineWidth, te
return STYLE_DOUBLE;
}
prev_char = i > 0 ? string.charCodeAt(i - 1) : null;
plain = plain && isPlainSafe(char, prev_char);
plain = plain && isPlainSafe(state, char, prev_char);
}
// in case the end is missing a \n
hasFoldableLine = hasFoldableLine || (shouldTrackWidth &&
Expand Down Expand Up @@ -372,7 +386,7 @@ function writeScalar(state, string, level, iskey) {
return testImplicitResolving(state, string);
}

switch (chooseScalarStyle(string, singleLineOnly, state.indent, lineWidth, testAmbiguity)) {
switch (chooseScalarStyle(state, string, singleLineOnly, state.indent, lineWidth, testAmbiguity)) {
case STYLE_PLAIN:
return string;
case STYLE_SINGLE:
Expand Down
18 changes: 18 additions & 0 deletions test/issues/0521,557.js
@@ -0,0 +1,18 @@
'use strict';


var assert = require('assert');
var yaml = require('../../');
var readFileSync = require('fs').readFileSync;


test('Don\'t quote strings with # without need', function () {
var required = readFileSync(require('path').join(__dirname, '/0521,557.yml'), 'utf8');
var data = {
'http://example.com/page#anchor': 'no:quotes#required',
'parameter#fallback': 'quotes #required',
'quotes: required': 'Visit [link](http://example.com/foo#bar)'
};
var actual = yaml.safeDump(data);
assert.equal(actual, required);
});
3 changes: 3 additions & 0 deletions test/issues/0521,557.yml
@@ -0,0 +1,3 @@
http://example.com/page#anchor: no:quotes#required
parameter#fallback: 'quotes #required'
'quotes: required': Visit [link](http://example.com/foo#bar)
23 changes: 0 additions & 23 deletions test/issues/0521.js

This file was deleted.

3 changes: 0 additions & 3 deletions test/issues/0521.yml

This file was deleted.