Repository: nvim-lua/plenary.nvim (lua)

Issue:
Can we have in `json_strip_comments()` additional functionallity that removes trailing commas?
See JSON5 capabilities from https://github.com/Joakker/lua-json5?tab=readme-ov-file#so-why-should-i-use-this-instead-of-the-builtin-json_decode:

> The advantage this package has over regular json, however, is that you get json5 features, such as comments, trailing commas and more flexible string literals.

So basically JSON5 added capabilities/syntax:
1.  comments
2. trailing commas
3. more flexible string literals (don't know what it means)

This package have a support for 1 via the function `json_strip_comments()`, but doesn't have a support for removing trailing commas, even though it seems that all the complex code is there, and with a little extra effort we can have a reg expression or such that detects the patterns `,(whitespace)}` and `,(whitespace)]` easily.
Not sure about 3, what it means and if we want it.

Basically, the nice function we could have is a transformer from JSON5 to JSON.

Pull request description:
This adds an additional feature to the `json_strip_comments` function that adds more compatibility for converting a JSON string to something compatible with `vim.fn.json_decode`. I also added tests to make sure things like commas in strings are fully ignored and that it doesn't mess up the already existing comment stripping.

Resolves #612

Patch:
diff --git a/lua/plenary/json.lua b/lua/plenary/json.lua
index cf4358dc..05581f7b 100644
--- a/lua/plenary/json.lua
+++ b/lua/plenary/json.lua
@@ -33,20 +33,26 @@ local M = {}
 -- The resulting string can then be used by `vim.fn.json_decode`
 --
 ---@param jsonString string
----@param options table
+---@param options? table
 ---  * whitespace:
 ---     - defaults to true
 ---     - when true, comments will be replaced by whitespace
 ---     - when false, comments will be stripped
+---  * trailing_commas:
+---     - defaults to false
+---     - when true, trailing commas will be included
+---     - when false, trailing commas will be removed
 function M.json_strip_comments(jsonString, options)
   options = options or {}
   local strip = options.whitespace == false and stripWithoutWhitespace or stripWithWhitespace
+  local omitTrailingCommas = not options.trailing_commas
 
   local insideString = false
   local insideComment = false
   local offset = 1
   local result = ""
   local skip = false
+  local lastComma = 0
 
   for i = 1, #jsonString, 1 do
     if skip then
@@ -89,6 +95,16 @@ function M.json_strip_comments(jsonString, options)
           insideComment = false
           result = result .. strip(jsonString, offset, i)
           offset = i + 1
+        elseif omitTrailingCommas and not insideComment then
+          if currentCharacter == "," then
+            lastComma = i
+          elseif (currentCharacter == "]" or currentCharacter == "}") and lastComma > 0 then
+            result = result .. slice(jsonString, offset, lastComma - 1) .. slice(jsonString, lastComma + 1, i)
+            offset = i + 1
+            lastComma = 0
+          elseif currentCharacter:match "%S" then
+            lastComma = 0
+          end
         end
       end
     end


Test patch:
diff --git a/tests/plenary/json_spec.lua b/tests/plenary/json_spec.lua
index 955668a9..88c00c25 100644
--- a/tests/plenary/json_spec.lua
+++ b/tests/plenary/json_spec.lua
@@ -67,4 +67,21 @@ describe("json", function()
       [[{"x":"x \"sed -e \\\"s/^.\\\\{46\\\\}T//\\\" -e \\\"s/#033/\\\\x1b/g\\\"\""}]]
     )
   end)
+
+  it("trailing commas", function()
+    eq(json.json_strip_comments '{"a":"b",}', '{"a":"b"}')
+    eq(json.json_strip_comments '{"a":{"b":"c",},}', '{"a":{"b":"c"}}')
+    eq(json.json_strip_comments '{"a":["b","c",],}', '{"a":["b","c"]}')
+  end)
+
+  it("trailing commas - ignored in strings and comments", function()
+    eq(json.json_strip_comments '{"a":"b,}"}', '{"a":"b,}"}')
+  end)
+
+  it("trailing commas - left when disabled in options", function()
+    local options = { trailing_commas = true }
+    eq(json.json_strip_comments('{"a":"b",}', options), '{"a":"b",}')
+    eq(json.json_strip_comments('{"a":{"b":"c",},}', options), '{"a":{"b":"c",},}')
+    eq(json.json_strip_comments('{"a":["b","c",],}', options), '{"a":["b","c",],}')
+  end)
 end)