Skip to content

Commit

Permalink
sort_keys default=false; pprint remember comments
Browse files Browse the repository at this point in the history
1. The default value for the sort_keys setting
    is now false (fix issue closed a while back)
2. When the pretty_print_style setting is PPrint,
    JsonTools now uses a PPrint-style algorithm
    that still remembers comments in the same way
    as the Google-style algorithm.
  • Loading branch information
molsonkiko committed Jan 24, 2024
1 parent 57452ad commit 7af51d2
Show file tree
Hide file tree
Showing 9 changed files with 278 additions and 68 deletions.
10 changes: 6 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

1. A [RemesPath user-defined language (UDL) file](/RemesPath%20UDL.xml), providing some very basic syntax highlighting. It is buggy, but that is because the UDL system is inherently buggy, not because I did anything wrong (as far as I know).
2. Add [`and` and `or` non-vectorized functions in RemesPath](/docs/RemesPath.md#non-vectorized-functions), which both use conditional excution.
3. [PPrint-style pretty-printing that remembers comments](/docs/README.md#remember_comments)

### Changed

1. The [`ifelse` vectorized function in RemesPath](/docs/RemesPath.md#vectorized-functions) now uses conditional execution.
2. Add optional arguments [to `stringify` non-vectorized function in RemesPath](/docs/RemesPath.md#non-vectorized-functions), so that users can control the format of the output.
3. Make dark mode icons darker.
4. __This change only affects the code base, not the public API:__ changed almost all snake_case variable names to camelCase. [RemesPath functions still use snake_case](/JsonToolsNppPlugin/JSONTools/RemesPathFunctions.cs) (e.g., `s_mul` and `group_by` still have those names), and all the settings in [Settings.cs](/JsonToolsNppPlugin/Utils/Settings.cs) (e.g., `use_npp_styling`) that were previously snake_case are still snake_case.
5. All [regular expressions](/docs/RemesPath.md#regular-expressions) are now multiline, meaning that `^` and `$` now match the start and end of *lines* respectively, rather than the start and end of the *document.*
2. Default value for [`sort_keys` setting](/docs/README.md#sort_keys) is now `false`, meaning keys are left in their original order by default. This will not change existing settings.
3. Add optional arguments [to `stringify` non-vectorized function in RemesPath](/docs/RemesPath.md#non-vectorized-functions), so that users can control the format of the output.
4. Make dark mode icons darker.
5. *This change only affects the code base, not the public API:* changed almost all snake_case variable names to camelCase. [RemesPath functions still use snake_case](/JsonToolsNppPlugin/JSONTools/RemesPathFunctions.cs) (e.g., `s_mul` and `group_by` still have those names), and all the settings in [Settings.cs](/JsonToolsNppPlugin/Utils/Settings.cs) (e.g., `use_npp_styling`) that were previously snake_case are still snake_case.
6. __All [RemesPath regular expressions](/docs/RemesPath.md#regular-expressions) are now multiline__, meaning that `^` and `$` now match the start and end of *lines* respectively, rather than the start and end of the *document.*

### Fixed

Expand Down
144 changes: 131 additions & 13 deletions JsonToolsNppPlugin/JSONTools/JNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -564,29 +564,31 @@ public string ToStringWithCommentsAndChangePositions(List<Comment> comments, boo

/// <summary>
/// for non-iterables, pretty-printing with comments is the same as compactly printing with comments.<br></br>
/// For iterables, pretty-printing with comments means Google-style pretty-printing,<br></br>
/// For iterables, pretty-printing with comments means Google-style pretty-printing (<i>unless prettyPrintStyle is PPrint</i>),<br></br>
/// with each comment having the same position relative to each JSON element and each other comment
/// that those comments did when the JSON was parsed.
/// that those comments did when the JSON was parsed.<br></br>
/// If prettyPrintStyle is PPrint, the algorith works as illustrated in PrettyPrintWithCommentsHelper below.
/// </summary>
public string PrettyPrintWithComments(List<Comment> comments, int indent = 4, bool sortKeys = true, char indentChar = ' ')
public string PrettyPrintWithComments(List<Comment> comments, int indent = 4, bool sortKeys = true, char indentChar = ' ', PrettyPrintStyle prettyPrintStyle=PrettyPrintStyle.Google)
{
return PrettyPrintWithComentsStarter(comments, false, indent, sortKeys, indentChar);
return PrettyPrintWithComentsStarter(comments, false, indent, sortKeys, indentChar, prettyPrintStyle);
}

/// <summary>
/// As PrettyPrintWithComments, but changes the position of each JNode to wherever it is in the UTF-8 encoded form of the returned string
/// </summary>
public string PrettyPrintWithCommentsAndChangePositions(List<Comment> comments, int indent = 4, bool sortKeys = true, char indentChar = ' ')
public string PrettyPrintWithCommentsAndChangePositions(List<Comment> comments, int indent = 4, bool sortKeys = true, char indentChar = ' ', PrettyPrintStyle prettyPrintStyle=PrettyPrintStyle.Google)
{
return PrettyPrintWithComentsStarter(comments, true, indent, sortKeys, indentChar);
return PrettyPrintWithComentsStarter(comments, true, indent, sortKeys, indentChar, prettyPrintStyle);
}

private string PrettyPrintWithComentsStarter(List<Comment> comments, bool changePositions, int indent, bool sortKeys, char indentChar)
private string PrettyPrintWithComentsStarter(List<Comment> comments, bool changePositions, int indent, bool sortKeys, char indentChar, PrettyPrintStyle prettyPrintStyle)
{
comments.Sort((x, y) => x.position.CompareTo(y.position));
bool pprint = prettyPrintStyle == PrettyPrintStyle.PPrint;
var sb = new StringBuilder();
(int commentIdx, int extraUtf8_bytes) = Comment.AppendAllCommentsBeforePosition(sb, comments, 0, 0, position, "");
(commentIdx, _) = PrettyPrintWithCommentsHelper(comments, commentIdx, indent, sortKeys, 0, sb, changePositions, extraUtf8_bytes, indentChar);
(commentIdx, _) = PrettyPrintWithCommentsHelper(comments, commentIdx, indent, sortKeys, 0, sb, changePositions, extraUtf8_bytes, indentChar, pprint);
sb.Append(NL);
Comment.AppendAllCommentsBeforePosition(sb, comments, commentIdx, 0, int.MaxValue, ""); // add all comments after the last JNode
return sb.ToString();
Expand Down Expand Up @@ -616,11 +618,49 @@ private string PrettyPrintWithComentsStarter(List<Comment> comments, bool change
///}
/// // comment at the end
///</code>
/// If pprint, the algorithm instead works like this:<br></br>
/// // comment at start<br></br>
/// [<br></br>
/// // comment before first element<br></br>
/// ["short", {"iterables": "get", "printed on": 1.0}, "line"],<br></br>
/// {<br></br>
/// "but": [<br></br>
/// "this",<br></br>
/// /* has a comment in it */<br></br>
/// "and gets more lines",<br></br>
/// true<br></br>
/// ]<br></br>
/// },<br></br>
/// [<br></br>
/// "and this array is too long",<br></br>
/// "so it goes Google-style",<br></br>
/// "even though it has",<br></br>
/// [0.0, "comments"]<br></br>
/// ]<br></br>
/// ]<br></br>
///</summary>
public virtual (int commentIdx, int extraUtf8_bytes) PrettyPrintWithCommentsHelper(List<Comment> comments, int commentIdx, int indent, bool sortKeys, int depth, StringBuilder sb, bool changePositions, int extraUtf8_bytes, char indentChar)
public virtual (int commentIdx, int extraUtf8_bytes) PrettyPrintWithCommentsHelper(List<Comment> comments, int commentIdx, int indent, bool sortKeys, int depth, StringBuilder sb, bool changePositions, int extraUtf8_bytes, char indentChar, bool pprint)
{
return (commentIdx, ToStringHelper(sortKeys, ": ", ", ", sb, changePositions, extraUtf8_bytes, int.MaxValue));
}

public virtual (int commentIdx, int extraUtf8_bytes) PPrintWithCommentsHelper(List<Comment> comments, int commentIdx, int indent, bool sortKeys, int depth, StringBuilder sb, bool changePositions, int extraUtf8_bytes, char indentChar, int maxLineEnd)
{
return (commentIdx, ToStringHelper(sortKeys, ": ", ", ", sb, changePositions, extraUtf8_bytes, int.MaxValue));
}

/// <summary>
/// return -1 if and only if either of the following is true:<br></br>
/// * compressing this JSON (non-minimal whitespace) would JSON would push sbLength over maxSbLen<br></br>
/// * this JNode has a position greater than maxInitialPosition<br></br>
/// If neither is true, return sbLength plus the length of this JSON's compressed string repr (non-minimal whitespace)
/// </summary>
public virtual int CompressedLengthAndPositionsWithinThresholds(int sbLength, int maxInitialPosition, int maxSbLen)
{
if (position >= maxInitialPosition || sbLength >= maxSbLen)
return -1;
return sbLength + ToString().Length;
}
#endregion
///<summary>
/// A magic method called behind the scenes when sorting things.<br></br>
Expand Down Expand Up @@ -1253,7 +1293,7 @@ public override int PPrintHelper(int indent, int depth, bool sortKeys, StringBui
}

/// <inheritdoc/>
public override (int commentIdx, int extraUtf8_bytes) PrettyPrintWithCommentsHelper(List<Comment> comments, int commentIdx, int indent, bool sortKeys, int depth, StringBuilder sb, bool changePositions, int extraUtf8_bytes, char indentChar)
public override (int commentIdx, int extraUtf8_bytes) PrettyPrintWithCommentsHelper(List<Comment> comments, int commentIdx, int indent, bool sortKeys, int depth, StringBuilder sb, bool changePositions, int extraUtf8_bytes, char indentChar, bool pprint)
{
if (changePositions) position = sb.Length + extraUtf8_bytes;
int ctr = 0;
Expand All @@ -1272,8 +1312,11 @@ public override (int commentIdx, int extraUtf8_bytes) PrettyPrintWithCommentsHel
{
JNode v = children[k];
(commentIdx, extraUtf8_bytes) = Comment.AppendAllCommentsBeforePosition(sb, comments, commentIdx, extraUtf8_bytes, v.position, extraDent);
int pprintLineEnd = sb.Length + PPRINT_LINE_LENGTH;
extraUtf8_bytes += AppendKeyAndGetUtf8Extra(sb, extraDent, k, "\": ");
(commentIdx, extraUtf8_bytes) = v.PrettyPrintWithCommentsHelper(comments, commentIdx, indent, sortKeys, depth + 1, sb, changePositions, extraUtf8_bytes, indentChar);
(commentIdx, extraUtf8_bytes) = pprint
? v.PPrintWithCommentsHelper(comments, commentIdx, indent, sortKeys, depth + 1, sb, changePositions, extraUtf8_bytes, indentChar, pprintLineEnd)
: v.PrettyPrintWithCommentsHelper(comments, commentIdx, indent, sortKeys, depth + 1, sb, changePositions, extraUtf8_bytes, indentChar, false);
if (++ctr < children.Count)
sb.Append(',');
sb.Append(NL);
Expand All @@ -1282,6 +1325,43 @@ public override (int commentIdx, int extraUtf8_bytes) PrettyPrintWithCommentsHel
return (commentIdx, extraUtf8_bytes);
}

public override (int commentIdx, int extraUtf8_bytes) PPrintWithCommentsHelper(List<Comment> comments, int commentIdx, int indent, bool sortKeys, int depth, StringBuilder sb, bool changePositions, int extraUtf8_bytes, char indentChar, int maxSbLen)
{
if (Length > PPRINT_LINE_LENGTH / 8) // an non-minimal-whitespace-compressed object has at least 8 chars per element ("\"a\": 1, ")
goto printOnMultipleLines;
int ogSbLen = sb.Length;
int nextCommentPos = commentIdx >= comments.Count ? int.MaxValue : comments[commentIdx].position;
if (CompressedLengthAndPositionsWithinThresholds(ogSbLen, nextCommentPos, maxSbLen) == -1)
{
// child is too long or has a comment inside; need to print on multiple lines
goto printOnMultipleLines;
}
// child is small enough when compact and has no comments inside, so use compact repr
return (commentIdx, ToStringHelper(sortKeys, ": ", ", ", sb, changePositions, extraUtf8_bytes, int.MaxValue));
printOnMultipleLines:
return PrettyPrintWithCommentsHelper(comments, commentIdx, indent, sortKeys, depth, sb, changePositions, extraUtf8_bytes, indentChar, true);
}

/// <inheritdoc/>
public override int CompressedLengthAndPositionsWithinThresholds(int sbLength, int maxInitialPosition, int maxSbLen)
{
if (position >= maxInitialPosition || sbLength >= maxSbLen)
return -1;
int ctr = 0;
sbLength += 1; // opening '{'
foreach (string key in children.Keys)
{
JNode child = children[key];
sbLength += StrToString(key, true).Length + 2; // json-encoded key, then ": "
sbLength = child.CompressedLengthAndPositionsWithinThresholds(sbLength, maxInitialPosition, maxSbLen);
if (sbLength == -1)
return -1;
if (ctr < children.Count - 1)
sbLength += 2; // ", "
}
return sbLength;
}

/// <summary>
/// if this JObject represents an ini file, all the values must be strings. Calling this method ensures that this is so.
/// </summary>
Expand Down Expand Up @@ -1565,7 +1645,7 @@ public override int PPrintHelper(int indent, int depth, bool sortKeys, StringBui
return childUtf8_extra;
}

public override (int commentIdx, int extraUtf8_bytes) PrettyPrintWithCommentsHelper(List<Comment> comments, int commentIdx, int indent, bool sortKeys, int depth, StringBuilder sb, bool changePositions, int extraUtf8_bytes, char indentChar)
public override (int commentIdx, int extraUtf8_bytes) PrettyPrintWithCommentsHelper(List<Comment> comments, int commentIdx, int indent, bool sortKeys, int depth, StringBuilder sb, bool changePositions, int extraUtf8_bytes, char indentChar, bool pprint)
{
if (changePositions) position = sb.Length + extraUtf8_bytes;
sb.Append('[');
Expand All @@ -1576,8 +1656,11 @@ public override (int commentIdx, int extraUtf8_bytes) PrettyPrintWithCommentsHel
foreach (JNode v in children)
{
(commentIdx, extraUtf8_bytes) = Comment.AppendAllCommentsBeforePosition(sb, comments, commentIdx, extraUtf8_bytes, v.position, extraDent);
int maxLineEnd = sb.Length + PPRINT_LINE_LENGTH;
sb.Append(extraDent);
(commentIdx, extraUtf8_bytes) = v.PrettyPrintWithCommentsHelper(comments, commentIdx, indent, sortKeys, depth + 1, sb, changePositions, extraUtf8_bytes, indentChar);
(commentIdx, extraUtf8_bytes) = pprint
? v.PPrintWithCommentsHelper(comments, commentIdx, indent, sortKeys, depth + 1, sb, changePositions, extraUtf8_bytes, indentChar, maxLineEnd)
: v.PrettyPrintWithCommentsHelper(comments, commentIdx, indent, sortKeys, depth + 1, sb, changePositions, extraUtf8_bytes, indentChar, false);
if (++ctr < children.Count)
sb.Append(',');
sb.Append(NL);
Expand All @@ -1586,6 +1669,41 @@ public override (int commentIdx, int extraUtf8_bytes) PrettyPrintWithCommentsHel
return (commentIdx, extraUtf8_bytes);
}

public override (int commentIdx, int extraUtf8_bytes) PPrintWithCommentsHelper(List<Comment> comments, int commentIdx, int indent, bool sortKeys, int depth, StringBuilder sb, bool changePositions, int extraUtf8_bytes, char indentChar, int maxSbLen)
{
if (Length > PPRINT_LINE_LENGTH / 3) // an non-minimal-whitespace-compressed array has at least 3 chars per element ("1, ")
goto printOnMultipleLines;
int ogSbLen = sb.Length;
int nextCommentPos = commentIdx >= comments.Count ? int.MaxValue : comments[commentIdx].position;
if (CompressedLengthAndPositionsWithinThresholds(ogSbLen, nextCommentPos, maxSbLen) == -1)
{
// child is too long or has a comment inside; need to print on multiple lines
goto printOnMultipleLines;
}
// child is small enough when compact and has no comments inside, so use compact repr
return (commentIdx, ToStringHelper(sortKeys, ": ", ", ", sb, changePositions, extraUtf8_bytes, int.MaxValue));
printOnMultipleLines:
return PrettyPrintWithCommentsHelper(comments, commentIdx, indent, sortKeys, depth, sb, changePositions, extraUtf8_bytes, indentChar, true);
}

/// <inheritdoc />
public override int CompressedLengthAndPositionsWithinThresholds(int sbLength, int maxInitialPosition, int maxSbLen)
{
if (position >= maxInitialPosition || sbLength >= maxSbLen)
return -1;
sbLength += 1; // opening '['
for (int ii = 0; ii < children.Count; ii++)
{
JNode child = children[ii];
sbLength = child.CompressedLengthAndPositionsWithinThresholds(sbLength, maxInitialPosition, maxSbLen);
if (sbLength == -1)
return -1;
if (ii < children.Count - 1)
sbLength += 2; // ", "
}
return sbLength;
}

/// <summary>
/// Returns true if and only if other is a JArray such that
/// other[i] == this[i] for all i &#60; this.Length.<br></br>
Expand Down
2 changes: 1 addition & 1 deletion JsonToolsNppPlugin/Main.cs
Original file line number Diff line number Diff line change
Expand Up @@ -749,7 +749,7 @@ public static void PrettyPrintJson()
int indent = settings.tab_indent_pretty_print ? 1 : settings.indent_pretty_print;
Func<JNode, string> formatter;
if (UseComments(info))
formatter = x => x.PrettyPrintWithCommentsAndChangePositions(info.comments, indent, settings.sort_keys, settings.tab_indent_pretty_print ? '\t' : ' ');
formatter = x => x.PrettyPrintWithCommentsAndChangePositions(info.comments, indent, settings.sort_keys, settings.tab_indent_pretty_print ? '\t' : ' ', settings.pretty_print_style);
else
formatter = PrettyPrintFromSettings;
if (isJsonLines)
Expand Down
4 changes: 2 additions & 2 deletions JsonToolsNppPlugin/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,5 @@
// Build Number
// Revision
//
[assembly: AssemblyVersion("6.1.1.11")]
[assembly: AssemblyFileVersion("6.1.1.11")]
[assembly: AssemblyVersion("6.1.1.12")]
[assembly: AssemblyFileVersion("6.1.1.12")]
Loading

0 comments on commit 7af51d2

Please sign in to comment.