Skip to content

feat(lua): expose lua-cjson as vim.json #14871

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

Merged
merged 3 commits into from
Sep 26, 2021
Merged

feat(lua): expose lua-cjson as vim.json #14871

merged 3 commits into from
Sep 26, 2021

Conversation

mjlbach
Copy link
Contributor

@mjlbach mjlbach commented Jun 20, 2021

This adds a direct to/from lua json encode/decode using lua-cjson. There are performance benefits to having direct conversion from json to the lua datastructures, and this avoids any issue calling in luv callbacks.

  • replace cjson.null with vim.NIL
  • add to lua interpreter
  • figure out where to put license/if having the license info in the file header s fine
  • address fpconv warnings (silence)
  • address justin's concern with circular import
  • add real benchmark
  • add more tests

@github-actions github-actions bot added the build building and installing Neovim using the provided scripts label Jun 20, 2021
@lithammer
Copy link
Contributor

lithammer commented Jun 21, 2021

I'm curious what the rationale for this is. What does it offer that json_encode/json_decode doesn't?

@hrsh7th
Copy link
Contributor

hrsh7th commented Jun 21, 2021

In my knowledge, vim.fn.json_encode/json_decode needs vim.schedule and the return value needs conversion between vim and lua.

@mjlbach
Copy link
Contributor Author

mjlbach commented Jun 21, 2021

@hrsh7th is correct, I'd also like to add in my benchmark lua-cjson was able to decode the same file in ~%30 of the time.

@Sh3Rm4n
Copy link
Contributor

Sh3Rm4n commented Jun 22, 2021

And what is the rationale to embed / vendor the lua_cjson code? Is it just for testing? Is it similar to treesitter which was first vendored, and after it matured a bit outsourced again to be able to use the upstream version?

@bfredl
Copy link
Member

bfredl commented Jun 22, 2021

To make it work directly with our lua mpack typesystem, i e vim.NIL and vim.empty_dict()

@bfredl
Copy link
Member

bfredl commented Jul 18, 2021

I'm looking into handling NULL and empty tables correctly (according to our conventions).

@bfredl
Copy link
Member

bfredl commented Jul 18, 2021

Added to interpreter to vim.json (could be made lazy, later). Initial support for vim.NIL and vim.empty_dict is prototyped.

@github-actions github-actions bot added lsp lua stdlib and removed lua stdlib lsp labels Jul 18, 2021
@clason clason added dependencies build dependencies (LuaJIT, LibUV, etc.) enhancement feature request lua stdlib performance performance, latency, cpu/memory usage and removed build building and installing Neovim using the provided scripts labels Aug 3, 2021
@kkharji

This comment has been minimized.

@justinmk
Copy link
Member

justinmk commented Sep 9, 2021

How feasible is it to remove the old Vimscript implemenation of json_encode() / json_decode() and make it a wrapper around this? Even if it makes Vimscript slightly slower, the code/maintenance reduction is attractive...

@mjlbach
Copy link
Contributor Author

mjlbach commented Sep 9, 2021

I'll pick this PR back up this weekend.

It's feasible but we should also consider benchmarking it to see if the conversion cost to vim data structures is high.

@justinmk justinmk changed the title [WIP] embed lua-cjson (take 2) lua: embed lua-cjson (take 2) Sep 9, 2021
@mjlbach
Copy link
Contributor Author

mjlbach commented Sep 23, 2021

I rebased, cleaned up some conflicts, and moved the source of cjson as requested. I'll squash the commits when everything is ready. I guess tests are left, and silencing some of the missing/strict prototype warnings.

@mjlbach
Copy link
Contributor Author

mjlbach commented Sep 23, 2021

I silenced the warnings (not sure if this was the cleanest way) and added some basic tests.

@mjlbach mjlbach requested a review from bfredl September 23, 2021 08:16
@justinmk
Copy link
Member

justinmk commented Sep 23, 2021

@mjlbach
Copy link
Contributor Author

mjlbach commented Sep 23, 2021

Yes, once people feel this is in a mergeable state I will clean up the commit history. There will be two commits:

  • feat: add lua-cjson as an external dependency
  • feat: expose lua-cjson as vim.json (this will include benchmarks, the test, and the modified code)

@mjlbach mjlbach changed the title lua: embed lua-cjson (take 2) feat: add and expose lua-cjson as vim.json Sep 23, 2021
@mjlbach mjlbach marked this pull request as ready for review September 23, 2021 17:45
@mjlbach
Copy link
Contributor Author

mjlbach commented Sep 23, 2021

I need to add the real benchmarks, but I cleaned up the commits (let me know if this is consistent with what you wanted, each commit should build). There's still the open warnings from fpconv to take care of, and Justin had some concerns about a circular dependency, but I pointed out where the included functions are used.


eq(
{ hello = 'world'},
exec_lua([[return vim.json.decode('{"hello":"world"}')]])
Copy link
Member

@justinmk justinmk Sep 24, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(not a blocker, but at least mentioning for reference...)

i kinda assumed more tests were on the way. Maybe copy a few from json_functions_spec.lua ?

Or parameterize (most of) the tests in json_functions_spec.lua, then in this file, something like:

local json_spec = require('test.functional.json_functions_spec')
local json_encode = function(o)
  exec_lua([[return vim.json.encode(...)]], o)
end
local json_decode = function(o) ... end
json_spec.run(json_encode, json_decode)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking through the tests, I think parameterizing them is going to be non-trivial, seems like many of them deal with vimscript specific behavior, or eval-ing a string representation of the vim command.

@justinmk justinmk changed the title feat: add and expose lua-cjson as vim.json feat(lua): expose lua-cjson as vim.json Sep 24, 2021
mjlbach and others added 2 commits September 26, 2021 00:35
Derived from the openresty lua-cjson fork at commit openresty/lua-cjson@3d93d29
* add vim.json.encode and vim.json.decode
* use vim.NIL instead of cjson.null
* resolve strict-prototypes warnings

* The following benchmark shows an approximately 2.5x (750 ms vs 300 ms) improvement in deserialization performance over
  vim.fn.json_decode on a medium package.json

  ```lua
  local uv = vim.loop
  local function readfile(path)
    return
  end

  local json_url = "https://raw.githubusercontent.com/rust-analyzer/rust-analyzer/b24c8d5c89ee93d1172b4127564f5da3b0c88dad/editors/code/package.json"
  io.popen(string.format('curl -v -f -L -O %q &> /dev/null', json_url))

  local json_string = io.open('package.json'):read '*a'

  uv.update_time()
  local start = uv.hrtime()

  for i = 1,1000 do
    vim.fn.json_decode(json_string)
  end

  uv.update_time()
  print(string.format("Deserialization time vim.fn.json_decode: %s ms", (uv.hrtime() - start) * (1e-6)))

  uv.update_time()
  local start = uv.hrtime()

  for i = 1,1000 do
    vim.json.decode(json_string)
  end
  uv.update_time()
  print(string.format("Deserialization time vim.json.decode: %s ms", (uv.hrtime() - start) * (1e-6)))
  ```

Co-Authored-By: Björn Linse <[email protected]>
@mjlbach
Copy link
Contributor Author

mjlbach commented Sep 26, 2021

Added the benchmark, added some tests from json functional test. Ready for review and final comments. The commit lint just doesn't like the embedded URL which is over 100 chars long.

@clason
Copy link
Member

clason commented Sep 26, 2021

Functional test failure:

2021-09-26T07:48:44.1162119Z [  ERROR   ] 1 error, listed below:
2021-09-26T07:48:44.1162872Z [  ERROR   ]  @ : test/functional/lua/json_spec.lua
2021-09-26T07:48:44.1164625Z test/functional/lua/json_spec.lua:66: nesting of [[...]] is deprecated near '['

It doesn't like the [[], 1]}}] in the string; you'll either have to use [=[ ... ]=] for the outer delimiter or add whitespace (if stylua will let you).

@clason
Copy link
Member

clason commented Sep 26, 2021

@mjlbach I took the liberty of fixing the test with the minimal (whitespace) change. (I did what I had to, because I could!)

Hope you don't mind; I assumed you'd squash the test commit anyway. Feel free to drop the commit and make a different change.

@mjlbach
Copy link
Contributor Author

mjlbach commented Sep 26, 2021

I actually wasn't planning on squashing the test + lua-cjson commits together, but keeping them separate to delineate the c-side changes/modifications.

I just squashed the test commit together so if there aren't any objects I'll merge this after @bfredl or @justinmk approves. I think it's ok to ignore the commit linter in this case.

@bfredl
Copy link
Member

bfredl commented Sep 26, 2021

otherwise LGTM :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
dependencies build dependencies (LuaJIT, LibUV, etc.) enhancement feature request lua stdlib performance performance, latency, cpu/memory usage
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants