molsonkiko / JsonToolsNppPlugin

A Notepad++ plugin providing tools for JSON like linting, querying, a tree view, and CSV conversion.
Apache License 2.0
70 stars 9 forks source link

Add option(s) to force PPrint to print inline jsons for same layout without considering its length? #58

Closed powof2 closed 2 months ago

powof2 commented 3 months ago

Hi there,

This is pretty:

{
    "employees": [
        {
            "id": 213,
            "name": "franc",
            "another": [[{"abc": "test"}, {"xyz": "nnnbbbbbbbbbbbbbbbb"}]] // this is perfectly inlined
        }
    ]
}

With longer xyz, it becomes this (which is not what i wanted)

{
    "employees": [
        {
            "id": 213,
            "name": "franc",
            "another": [
                [{"abc": "test"}, {"xyz": "nnnbbbbbbbbbbbbbbbbbbbbbbbbbb"}] // becomes 3 lines with longer 'xyz', not good but still ok
            ]
        }
    ]
}

With even longer xyz:

{
    "employees": [
        {
            "id": 213,
            "name": "franc",
            "another": [
                [
                    {"abc": "test"},
                    {
                        "xyz": "nnnbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" // not pretty anymore
                    }
                ]
            ]
        }
    ]
}

With above longest example, what i expected:

{
    "employees": [
        {
            "id": 213,
            "name": "franc",
            "another": [[{"abc": "test"}, { "xyz": "nnnbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" } ]]
        }
    ]
}

Can this be done by adding options to ignore value length for same layout when PPrint? or maybe add options to PPrint by indent levels?

Thank you, for the great tool.

molsonkiko commented 3 months ago

@powof2

My short answer

No, I will not add additional config options for the PPrint pretty-print style. The algorithm will remain exactly as documented in this section of the documentation.

How you can do this with current JsonTools v7.1

This may not work if you have JSON with comments or JSON that is otherwise noncompliant, because the Select all children tree node option cannot parse JSON that does not adhere to the original JSON spec.

  1. Pretty-print your JSON using normal settings.
  2. Open the tree view.
  3. Enter the query @.employees[:].another. This query will find all the another children of objects that are children of an array that is the child of the employees child of the root object, which is the path to all elements like the one you wanted to pretty-print.
  4. Right-click the root node of the treeview. A context menu will appear.
  5. Left-click the Select all children option from the context menu that appeared.
  6. Compress JSON using normal settings. This will compress the JSON that was selected in step 5, while leaving the rest of the document untouched.

My longer answer

I don't want to add lots of complicated bespoke pretty-printing config options, but I am willing to add a new pretty-printing method that uses a RemesPath query to determine whether to compress or pretty-print JSON. I might call this method RemesPrint.

I believe that this is a reasonably low-maintenance (for me) and powerful (for users) way to define arbitrary pretty-printing rules.

My hypothetical query-based RemesPrint method would have a user-defined RemesPrintCallback function that takes json (a JSON element), path (the path to that element), and the indent of that element as input, and returns a boolean. If and only if the result of this callback function is true, the JSON is compressed.

For example, here's some JSON where most of the elements are tagged with the parameters for each call to the RemesPrintCallback function, and the indent settings are set to indent_pretty_print=2 and tab_indent_pretty_print=False.

{
  "employees": [
    {
      "id": 213, // json = 213; path = ["employees", 0, "id"]; indent = "      "
      "name": "franc", // json = "franc"; path = ["employees", 0, "name"]; indent = "      " 
      "another": [
        [
          { // json = {"abc": "test"}; path = ["employees", 0, "another", 0, 0]; indent = "          "
            "abc": "test" // json = "test"; path = ["employees", 0, "another", 0, 0, "abc"]; indent = "            "
          },
          { // json = {"foo": "bar"}; path = ["employees", 0, "another", 0, 1]; indent = "          "
            "foo": "bar" // json = "bar"; path = ["employees", 0, "another", 0, 1, "foo"]; indent = "            "
          }
        ]
      ]
    },
    {
      "id": [ // json = ["blah"]; path = ["employees", 1, "id"]; indent = "      "
        "blah" // json = "blah"; path = ["employees", 1, "id", 0]; indent = "        "
      ]
    }
  ]
}

A callback function (in pseudocode) that would work the same as the current PPrint algorithm is as follows:

var len_indent = s_len(indent);
var len_compress = s_len(str(json));
return len_compress + len_indent < 80; 

Another callback (again in pseudocode) that would work the same as the current PPrint algorithm, except that all elements of an array at path .employees[:].another would be compressed, would be as follows:

var len_indent = s_len(indent);
var len_compress = s_len(str(json));
if len_compress + len_indent < 80;
    return true;
endif;
return and(len(path) >= 3, path[0] == `employees`, type(path[1]) == `integer`, path[2] == `another`);
powof2 commented 3 months ago

JmesPath sounds promising. In this case, maybe we can choose some commonly used variables to make custom expression as callback, anytime when PPrint (or RemesPrint), eval this expression to determine whether to perform compression or not, this way, users don't have to actually write code. (like expressions in Excel)。 This is just an idea at the moment. I'll check out RemesPath first。

molsonkiko commented 2 months ago

I am closing this issue, and have no intention of implementing any feature that I previously mentioned considering. See #61 for a relevant tutorial.