Compare commits

..

6 Commits

Author SHA1 Message Date
2346a75cae
feat(dev): Add flake.nix 2025-06-06 12:55:07 +02:00
0e5158a8d4
chore: apply stylua 2025-06-06 12:54:16 +02:00
895e9adfdf
fix: merge main 2025-06-06 12:43:58 +02:00
Myzel394
d713d9ef38
Merge pull request #20 from kletobias/custom-jsonfly
Fix: Handle Dictionary Responses in jsonfly.lua for Neovim 0.10.1
2024-09-30 16:22:17 +02:00
Myzel394
8fe79c2783
fix: Fix fallback behavior; apply stylua 2024-09-30 16:18:19 +02:00
Tobias Klein
cbff2a5659 fix(jsonfly.lua): resolve function buf_request_all to work with new nvim
syntax and version changes not accounted for.
Refactored LSP request handling in jsonfly.lua to combine results from multiple LSP responses and handle empty results more gracefully. Improved code readability and maintainability by restructuring the nested if-else blocks.
2024-09-29 15:55:36 +02:00
9 changed files with 1286 additions and 1168 deletions

61
flake.lock generated Normal file
View File

@ -0,0 +1,61 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1748929857,
"narHash": "sha256-lcZQ8RhsmhsK8u7LIFsJhsLh/pzR9yZ8yqpTzyGdj+Q=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "c2a03962b8e24e669fb37b7df10e7c79531ff1a4",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs",
"utils": "utils"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

43
flake.nix Normal file
View File

@ -0,0 +1,43 @@
{
description = "jsonfly";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
utils.url = "github:numtide/flake-utils";
};
outputs = { nixpkgs, utils, ... } @ inputs:
utils.lib.eachDefaultSystem(system:
let
pkgs = nixpkgs.legacyPackages.${system};
logo = pkgs.writeText "logo.txt" ''
'';
in
{
devShells.default = pkgs.mkShell {
packages = with pkgs; [
stylua
# If this ever fails, just remove it. It's just for the logo.
lolcat
];
shellHook = ''
cat ${logo} | lolcat
echo "";
echo "Welcome to the jsonfly.nvim development environment!";
echo "";
'';
};
}
);
}

10
justfile Normal file
View File

@ -0,0 +1,10 @@
#!/usr/bin/env just --justfile
set dotenv-load := true
_default:
just --list -u
lint:
stylua lua

View File

@ -1,47 +1,42 @@
local M = {};
local M = {}
local _cache = {};
local _cache = {}
---@param buffer integer
function M:cache_buffer(buffer, value)
_cache[buffer] = value;
_cache[buffer] = value
end
---@param buffer integer
function M:invalidate_buffer(buffer)
_cache[buffer] = nil;
_cache[buffer] = nil
end
---@param buffer integer
---@return string[]|nil
function M:get_cache(buffer)
return _cache[buffer];
return _cache[buffer]
end
local _listening_buffers = {};
local _listening_buffers = {}
---@param buffer integer
function M:register_listeners(buffer)
if _listening_buffers[buffer] then
return;
return
end
_listening_buffers[buffer] = true;
_listening_buffers[buffer] = true
vim.api.nvim_buf_attach(
buffer,
false,
{
vim.api.nvim_buf_attach(buffer, false, {
on_lines = function()
self:invalidate_buffer(buffer)
end,
on_detach = function()
self:invalidate_buffer(buffer)
_listening_buffers[buffer] = nil;
_listening_buffers[buffer] = nil
end,
}
);
})
end
return M;
return M

View File

@ -1,18 +1,18 @@
local utils = require"jsonfly.utils"
local utils = require("jsonfly.utils")
-- This string will be used to position the cursor properly.
-- Once everything is set, the cursor searches for this string and jumps to it.
-- After that, it will be removed immediately.
local CURSOR_SEARCH_HELPER = "_jsonFfFfFfLyY0904857CursorHelperRrRrRrR"
local M = {};
local M = {}
-- https://stackoverflow.com/a/24823383/9878135
function table.slice(tbl, first, last, step)
local sliced = {}
for i = first or 1, last or #tbl, step or 1 do
sliced[#sliced+1] = tbl[i]
sliced[#sliced + 1] = tbl[i]
end
return sliced
@ -46,17 +46,17 @@ end
---@return number|nil
local function find_best_fitting_entry(entries, keys)
local entry_index
local current_indexes = {1, #entries}
local current_indexes = { 1, #entries }
for kk=1, #keys do
for kk = 1, #keys do
local key = keys[kk]
local start_index = current_indexes[1]
local end_index = current_indexes[2]
current_indexes = {nil, nil}
current_indexes = { nil, nil }
for ii=start_index, end_index do
for ii = start_index, end_index do
if check_key_equal(entries[ii], key, kk) then
if current_indexes[1] == nil then
current_indexes[1] = ii
@ -84,7 +84,7 @@ local function write_keys(keys, index, lines)
local key = keys[index]
if index == #keys then
lines[#lines + 1] = "\"" .. key.key .. "\": \"" .. CURSOR_SEARCH_HELPER .. "\""
lines[#lines + 1] = '"' .. key.key .. '": "' .. CURSOR_SEARCH_HELPER .. '"'
return
end
@ -100,7 +100,7 @@ local function write_keys(keys, index, lines)
lines[#lines + 1] = "}"
elseif key.type == "key" then
lines[#lines + 1] = "\"" .. key.key .. "\":"
lines[#lines + 1] = '"' .. key.key .. '":'
write_keys(keys, index + 1, lines)
elseif key.type == "array_wrapper" then
@ -117,7 +117,7 @@ local function write_keys(keys, index, lines)
elseif key.type == "array_index" then
local amount = tonumber(key.key)
-- Write previous empty array objects
for _=1, amount do
for _ = 1, amount do
lines[#lines + 1] = "{},"
end
@ -131,22 +131,17 @@ local function add_comma(buffer, insertion_line)
local BUFFER_SIZE = 5
-- Find next non-empty character in reverse
for ii=insertion_line, 0, -BUFFER_SIZE do
local previous_lines = vim.api.nvim_buf_get_lines(
buffer,
math.max(0, ii - BUFFER_SIZE),
ii,
false
)
for ii = insertion_line, 0, -BUFFER_SIZE do
local previous_lines = vim.api.nvim_buf_get_lines(buffer, math.max(0, ii - BUFFER_SIZE), ii, false)
if #previous_lines == 0 then
return
end
for jj=#previous_lines, 1, -1 do
for jj = #previous_lines, 1, -1 do
local line = previous_lines[jj]
for char_index=#line, 1, -1 do
for char_index = #line, 1, -1 do
local char = line:sub(char_index, char_index)
if char ~= " " and char ~= "\t" and char ~= "\n" and char ~= "\r" then
@ -156,14 +151,7 @@ local function add_comma(buffer, insertion_line)
-- Insert comma at position
local line_number = math.max(0, ii - BUFFER_SIZE) + jj - 1
vim.api.nvim_buf_set_text(
buffer,
line_number,
char_index,
line_number,
char_index,
{","}
)
vim.api.nvim_buf_set_text(buffer, line_number, char_index, line_number, char_index, { "," })
return
end
end
@ -179,16 +167,10 @@ local function expand_empty_object(buffer, line_number)
local position_closing_bracket = string.find(line, "[%}%]]")
local remaining_line = string.sub(line, position_closing_bracket + 1)
vim.api.nvim_buf_set_lines(
buffer,
line_number,
line_number + 1,
false,
{
vim.api.nvim_buf_set_lines(buffer, line_number, line_number + 1, false, {
"{",
"}" .. remaining_line
}
)
"}" .. remaining_line,
})
return line_number + 1
end
@ -202,7 +184,7 @@ local function get_key_descriptor_index(keys, input_key_depth)
local depth = 0
local index = 0
for ii=1, #keys do
for ii = 1, #keys do
if keys[ii].type == "key" or keys[ii].type == "array_index" then
depth = depth + 1
end
@ -220,13 +202,13 @@ end
---@param keys string[]
---@return integer|nil - The index of the entry
local function get_entry_by_keys(entries, keys)
for ii=1, #entries do
for ii = 1, #entries do
local entry = entries[ii]
local splitted = utils:split_by_char(entry.key, ".")
local found = true
for jj=1, #keys do
for jj = 1, #keys do
if keys[jj] ~= splitted[jj] then
found = false
break
@ -244,7 +226,7 @@ end
local function flatten_key_description(keys)
local flat_keys = {}
for ii=1, #keys do
for ii = 1, #keys do
if keys[ii].type == "key" then
flat_keys[#flat_keys + 1] = keys[ii].key
elseif keys[ii].type == "array_index" then
@ -272,7 +254,7 @@ end
---@param entries Entry[] - Entries, they must be children of a top level array
---Counts how many top level children an array has
local function count_array_children(entries)
for ii=1, #entries do
for ii = 1, #entries do
if string.match(entries[ii].key, "^%d+$") then
return ii
end
@ -294,11 +276,11 @@ function M:jump_to_cursor_helper(buffer)
position[2],
position[1] - 1,
position[2] + #CURSOR_SEARCH_HELPER,
{""}
{ "" }
)
-- -- Go into insert mode
vim.cmd [[execute "normal a"]]
vim.cmd([[execute "normal a"]])
end
-- TODO: Handle top level empty arrays
@ -307,7 +289,7 @@ end
---@param buffer number
function M:insert_new_key(entries, keys, buffer)
-- Close current buffer
vim.cmd [[quit!]]
vim.cmd([[quit!]])
local input_key = flatten_key_description(keys)
---@type boolean
@ -326,8 +308,8 @@ function M:insert_new_key(entries, keys, buffer)
position = {
key_start = 1,
line_number = 1,
value_start = 1
}
value_start = 1,
},
}
should_add_comma = false
else
@ -353,8 +335,8 @@ function M:insert_new_key(entries, keys, buffer)
position = {
key_start = 1,
line_number = 1,
value_start = 1
}
value_start = 1,
},
}
else
local existing_input_keys_depth = #utils:split_by_char(entry.key, ".") + 1
@ -372,7 +354,7 @@ function M:insert_new_key(entries, keys, buffer)
write_keys(remaining_keys, 1, _writes)
local writes = {}
for ii=1, #_writes do
for ii = 1, #_writes do
if _writes[ii] == true then
-- Unwrap table
writes[#writes] = writes[#writes][1]
@ -382,8 +364,8 @@ function M:insert_new_key(entries, keys, buffer)
end
-- Hacky way to jump to end of object
vim.api.nvim_win_set_cursor(0, {entry.position.line_number, entry.position.value_start})
vim.cmd [[execute "normal %"]]
vim.api.nvim_win_set_cursor(0, { entry.position.line_number, entry.position.value_start })
vim.cmd([[execute "normal %"]])
local changes = #writes
local start_line = vim.api.nvim_win_get_cursor(0)[1] - 1
@ -403,10 +385,10 @@ function M:insert_new_key(entries, keys, buffer)
vim.api.nvim_buf_set_lines(buffer, start_line, start_line, false, writes)
-- Format lines
vim.api.nvim_win_set_cursor(0, {start_line, 1})
vim.api.nvim_win_set_cursor(0, { start_line, 1 })
vim.cmd('execute "normal =' .. changes .. 'j"')
M:jump_to_cursor_helper(buffer)
end
return M;
return M

View File

@ -1,4 +1,4 @@
local VERSION = '20161109.21' -- version history at end of file
local VERSION = "20161109.21" -- version history at end of file
local AUTHOR_NOTE = "-[ JSON.lua package by Jeffrey Friedl (http://regex.info/blog/lua/json) version 20161109.21 ]-"
local OBJDEF = {
@ -6,13 +6,21 @@ local OBJDEF = {
AUTHOR_NOTE = AUTHOR_NOTE,
}
local default_pretty_indent = " "
local default_pretty_options = { pretty = true, align_keys = false, indent = default_pretty_indent }
local isArray = { __tostring = function() return "JSON array" end } isArray.__index = isArray
local isObject = { __tostring = function() return "JSON object" end } isObject.__index = isObject
local isArray = {
__tostring = function()
return "JSON array"
end,
}
isArray.__index = isArray
local isObject = {
__tostring = function()
return "JSON object"
end,
}
isObject.__index = isObject
function OBJDEF:newArray(tbl)
return setmetatable(tbl or {}, isArray)
@ -22,36 +30,56 @@ function OBJDEF:newObject(tbl)
return setmetatable(tbl or {}, isObject)
end
local function getnum(op)
return type(op) == 'number' and op or op.N
return type(op) == "number" and op or op.N
end
local isNumber = {
__tostring = function(T) return T.S end,
__unm = function(op) return getnum(op) end,
__tostring = function(T)
return T.S
end,
__unm = function(op)
return getnum(op)
end,
__concat = function(op1, op2) return tostring(op1) .. tostring(op2) end,
__add = function(op1, op2) return getnum(op1) + getnum(op2) end,
__sub = function(op1, op2) return getnum(op1) - getnum(op2) end,
__mul = function(op1, op2) return getnum(op1) * getnum(op2) end,
__div = function(op1, op2) return getnum(op1) / getnum(op2) end,
__mod = function(op1, op2) return getnum(op1) % getnum(op2) end,
__pow = function(op1, op2) return getnum(op1) ^ getnum(op2) end,
__lt = function(op1, op2) return getnum(op1) < getnum(op2) end,
__eq = function(op1, op2) return getnum(op1) == getnum(op2) end,
__le = function(op1, op2) return getnum(op1) <= getnum(op2) end,
__concat = function(op1, op2)
return tostring(op1) .. tostring(op2)
end,
__add = function(op1, op2)
return getnum(op1) + getnum(op2)
end,
__sub = function(op1, op2)
return getnum(op1) - getnum(op2)
end,
__mul = function(op1, op2)
return getnum(op1) * getnum(op2)
end,
__div = function(op1, op2)
return getnum(op1) / getnum(op2)
end,
__mod = function(op1, op2)
return getnum(op1) % getnum(op2)
end,
__pow = function(op1, op2)
return getnum(op1) ^ getnum(op2)
end,
__lt = function(op1, op2)
return getnum(op1) < getnum(op2)
end,
__eq = function(op1, op2)
return getnum(op1) == getnum(op2)
end,
__le = function(op1, op2)
return getnum(op1) <= getnum(op2)
end,
}
isNumber.__index = isNumber
function OBJDEF:asNumber(item)
if getmetatable(item) == isNumber then
-- it's already a JSON number object.
return item
elseif type(item) == 'table' and type(item.S) == 'string' and type(item.N) == 'number' then
elseif type(item) == "table" and type(item.S) == "string" and type(item.N) == "number" then
-- it's a number-object table that lost its metatable, so give it one
return setmetatable(item, isNumber)
else
@ -65,7 +93,7 @@ function OBJDEF:asNumber(item)
end
function OBJDEF:forceString(item)
if type(item) == 'table' and type(item.S) == 'string' then
if type(item) == "table" and type(item.S) == "string" then
return item.S
else
return tostring(item)
@ -73,30 +101,26 @@ function OBJDEF:forceString(item)
end
function OBJDEF:forceNumber(item)
if type(item) == 'table' and type(item.N) == 'number' then
if type(item) == "table" and type(item.N) == "number" then
return item.N
else
return tonumber(item)
end
end
local function unicode_codepoint_as_utf8(codepoint)
--
-- codepoint is a number
--
if codepoint <= 127 then
return string.char(codepoint)
elseif codepoint <= 2047 then
--
-- 110yyyxx 10xxxxxx <-- useful notation from http://en.wikipedia.org/wiki/Utf8
--
local highpart = math.floor(codepoint / 0x40)
local lowpart = codepoint - (0x40 * highpart)
return string.char(0xC0 + highpart,
0x80 + lowpart)
return string.char(0xC0 + highpart, 0x80 + lowpart)
elseif codepoint <= 65535 then
--
-- 1110yyyy 10yyyyxx 10xxxxxx
@ -114,18 +138,16 @@ local function unicode_codepoint_as_utf8(codepoint)
-- Check for an invalid character (thanks Andy R. at Adobe).
-- See table 3.7, page 93, in http://www.unicode.org/versions/Unicode5.2.0/ch03.pdf#G28070
--
if ( highpart == 0xE0 and midpart < 0xA0 ) or
( highpart == 0xED and midpart > 0x9F ) or
( highpart == 0xF0 and midpart < 0x90 ) or
( highpart == 0xF4 and midpart > 0x8F )
if
(highpart == 0xE0 and midpart < 0xA0)
or (highpart == 0xED and midpart > 0x9F)
or (highpart == 0xF0 and midpart < 0x90)
or (highpart == 0xF4 and midpart > 0x8F)
then
return "?"
else
return string.char(highpart,
midpart,
lowpart)
return string.char(highpart, midpart, lowpart)
end
else
--
-- 11110zzz 10zzyyyy 10yyyyxx 10xxxxxx
@ -137,10 +159,7 @@ local function unicode_codepoint_as_utf8(codepoint)
local midB = math.floor(remainder / 0x40)
local lowpart = remainder - 0x40 * midB
return string.char(0xF0 + highpart,
0x80 + midA,
0x80 + midB,
0x80 + lowpart)
return string.char(0xF0 + highpart, 0x80 + midA, 0x80 + midB, 0x80 + lowpart)
end
end
@ -187,8 +206,7 @@ local function grok_number(self, text, start, options)
--
-- Grab the integer part
--
local integer_part = text:match('^-?[1-9]%d*', start)
or text:match("^-?0", start)
local integer_part = text:match("^-?[1-9]%d*", start) or text:match("^-?0", start)
if not integer_part then
self:onDecodeError("expected number", text, start, options.etc)
@ -200,14 +218,14 @@ local function grok_number(self, text, start, options)
--
-- Grab an optional decimal part
--
local decimal_part = text:match('^%.%d+', i) or ""
local decimal_part = text:match("^%.%d+", i) or ""
i = i + decimal_part:len()
--
-- Grab an optional exponential part
--
local exponent_part = text:match('^[eE][-+]?%d+', i) or ""
local exponent_part = text:match("^[eE][-+]?%d+", i) or ""
i = i + exponent_part:len()
@ -223,21 +241,19 @@ local function grok_number(self, text, start, options)
-- I suppose we should really look to see whether the exponent is actually big enough one
-- way or the other to trip stringification, but I'll be lazy about it until someone asks.
--
if (options.decodeIntegerStringificationLength
and
(integer_part:len() >= options.decodeIntegerStringificationLength or exponent_part:len() > 0))
or
(options.decodeDecimalStringificationLength
and
(decimal_part:len() >= options.decodeDecimalStringificationLength or exponent_part:len() > 0))
if
(
options.decodeIntegerStringificationLength
and (integer_part:len() >= options.decodeIntegerStringificationLength or exponent_part:len() > 0)
)
or (
options.decodeDecimalStringificationLength
and (decimal_part:len() >= options.decodeDecimalStringificationLength or exponent_part:len() > 0)
)
then
return full_number_text, i -- this returns the exact string representation seen in the original JSON
end
local as_number = tonumber(full_number_text)
if not as_number then
@ -248,10 +264,8 @@ local function grok_number(self, text, start, options)
return as_number, i
end
local function grok_string(self, text, start, options)
if text:sub(start,start) ~= '"' then
if text:sub(start, start) ~= '"' then
self:onDecodeError("expected string's opening quote", text, start, options.etc)
return nil, start -- in case the error method doesn't abort, return something sensible
end
@ -260,30 +274,33 @@ local function grok_string(self, text, start, options)
local text_len = text:len()
local VALUE = ""
while i <= text_len do
local c = text:sub(i,i)
local c = text:sub(i, i)
if c == '"' then
return VALUE, i + 1
end
if c ~= '\\' then
if c ~= "\\" then
VALUE = VALUE .. c
i = i + 1
elseif text:match('^\\b', i) then
elseif text:match("^\\b", i) then
VALUE = VALUE .. "\b"
i = i + 2
elseif text:match('^\\f', i) then
elseif text:match("^\\f", i) then
VALUE = VALUE .. "\f"
i = i + 2
elseif text:match('^\\n', i) then
elseif text:match("^\\n", i) then
VALUE = VALUE .. "\n"
i = i + 2
elseif text:match('^\\r', i) then
elseif text:match("^\\r", i) then
VALUE = VALUE .. "\r"
i = i + 2
elseif text:match('^\\t', i) then
elseif text:match("^\\t", i) then
VALUE = VALUE .. "\t"
i = i + 2
else
local hex = text:match('^\\u([0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF])', i)
local hex = text:match(
"^\\u([0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF])",
i
)
if hex then
i = i + 6 -- bypass what we just read
@ -292,7 +309,8 @@ local function grok_string(self, text, start, options)
local codepoint = tonumber(hex, 16)
if codepoint >= 0xD800 and codepoint <= 0xDBFF then
-- it's a hi surrogate... see whether we have a following low
local lo_surrogate = text:match('^\\u([dD][cdefCDEF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF])', i)
local lo_surrogate =
text:match("^\\u([dD][cdefCDEF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF])", i)
if lo_surrogate then
i = i + 6 -- bypass the low surrogate we just read
codepoint = 0x2400 + (codepoint - 0xD800) * 0x400 + tonumber(lo_surrogate, 16)
@ -301,11 +319,9 @@ local function grok_string(self, text, start, options)
end
end
VALUE = VALUE .. unicode_codepoint_as_utf8(codepoint)
else
-- just pass through what's escaped
VALUE = VALUE .. text:match('^\\(.)', i)
VALUE = VALUE .. text:match("^\\(.)", i)
i = i + 2
end
end
@ -326,7 +342,7 @@ end
-- Count newlines in `text` up to `start`
local function count_newlines(text, start)
local _, count = text:sub(1, start):gsub('\n', '\n')
local _, count = text:sub(1, start):gsub("\n", "\n")
return count
end
@ -339,7 +355,7 @@ local function get_relative_i(text, i)
local count = 0
for j = i, 1, -1 do
if text:sub(j, j) == '\n' then
if text:sub(j, j) == "\n" then
break
end
count = count + 1
@ -351,17 +367,16 @@ end
local grok_one -- assigned later
local function grok_object(self, text, start, options)
if text:sub(start,start) ~= '{' then
if text:sub(start, start) ~= "{" then
self:onDecodeError("expected '{'", text, start, options.etc)
return nil, start -- in case the error method doesn't abort, return something sensible
end
local i = skip_whitespace(text, start + 1) -- +1 to skip the '{'
local VALUE = self.strictTypes and self:newObject { } or { }
local VALUE = self.strictTypes and self:newObject({}) or {}
if text:sub(i,i) == '}' then
if text:sub(i, i) == "}" then
return VALUE, i + 1
end
local text_len = text:len()
@ -375,7 +390,7 @@ local function grok_object(self, text, start, options)
i = skip_whitespace(text, new_i)
if text:sub(i, i) ~= ':' then
if text:sub(i, i) ~= ":" then
self:onDecodeError("expected colon", text, i, options.etc)
return nil, i -- in case the error method doesn't abort, return something sensible
end
@ -397,13 +412,13 @@ local function grok_object(self, text, start, options)
--
i = skip_whitespace(text, new_i)
local c = text:sub(i,i)
local c = text:sub(i, i)
if c == '}' then
if c == "}" then
return VALUE, i + 1
end
if text:sub(i, i) ~= ',' then
if text:sub(i, i) ~= "," then
self:onDecodeError("expected comma or '}'", text, i, options.etc)
return nil, i -- in case the error method doesn't abort, return something sensible
end
@ -416,14 +431,14 @@ local function grok_object(self, text, start, options)
end
local function grok_array(self, text, start, options)
if text:sub(start,start) ~= '[' then
if text:sub(start, start) ~= "[" then
self:onDecodeError("expected '['", text, start, options.etc)
return nil, start -- in case the error method doesn't abort, return something sensible
end
local i = skip_whitespace(text, start + 1) -- +1 to skip the '['
local VALUE = self.strictTypes and self:newArray { } or { }
if text:sub(i,i) == ']' then
local VALUE = self.strictTypes and self:newArray({}) or {}
if text:sub(i, i) == "]" then
return VALUE, i + 1
end
@ -449,11 +464,11 @@ local function grok_array(self, text, start, options)
--
-- Expect now either ']' to end things, or a ',' to allow us to continue.
--
local c = text:sub(i,i)
if c == ']' then
local c = text:sub(i, i)
if c == "]" then
return VALUE, i + 1
end
if text:sub(i, i) ~= ',' then
if text:sub(i, i) ~= "," then
self:onDecodeError("expected comma or ']'", text, i, options.etc)
return nil, i -- in case the error method doesn't abort, return something sensible
end
@ -463,7 +478,6 @@ local function grok_array(self, text, start, options)
return nil, i -- in case the error method doesn't abort, return something sensible
end
grok_one = function(self, text, start, options)
-- Skip any whitespace
start = skip_whitespace(text, start)
@ -475,25 +489,18 @@ grok_one = function(self, text, start, options)
if text:find('^"', start) then
return grok_string(self, text, start, options.etc)
elseif text:find('^[-0123456789 ]', start) then
elseif text:find("^[-0123456789 ]", start) then
return grok_number(self, text, start, options)
elseif text:find('^%{', start) then
elseif text:find("^%{", start) then
return grok_object(self, text, start, options)
elseif text:find('^%[', start) then
elseif text:find("^%[", start) then
return grok_array(self, text, start, options)
elseif text:find('^true', start) then
elseif text:find("^true", start) then
return true, start + 4
elseif text:find('^false', start) then
elseif text:find("^false", start) then
return false, start + 5
elseif text:find('^null', start) then
elseif text:find("^null", start) then
return nil, start + 4
else
self:onDecodeError("can't parse JSON", text, start, options.etc)
return nil, 1 -- in case the error method doesn't abort, return something sensible
@ -504,7 +511,7 @@ function OBJDEF:decode(text, etc, options)
--
-- If the user didn't pass in a table of decode options, make an empty one.
--
if type(options) ~= 'table' then
if type(options) ~= "table" then
options = {}
end
@ -516,12 +523,12 @@ function OBJDEF:decode(text, etc, options)
options.etc = etc
end
if text:match('^%s*$') then
if text:match("^%s*$") then
-- an empty string is nothing, but not an error
return nil
end
if text:match('^%s*<') then
if text:match("^%s*<") then
-- Can't be JSON... we'll assume it's HTML
local error_message = "HTML passed to JSON:decode()"
self:onDecodeOfHTMLError(error_message, text, nil, options.etc)
@ -533,7 +540,7 @@ function OBJDEF:decode(text, etc, options)
-- Those are perfectly valid encodings for JSON (as per RFC 4627 section 3),
-- but this package can't handle them.
--
if text:sub(1,1):byte() == 0 or (text:len() >= 2 and text:sub(2,2):byte() == 0) then
if text:sub(1, 1):byte() == 0 or (text:len() >= 2 and text:sub(2, 2):byte() == 0) then
local error_message = "JSON package groks only UTF-8, sorry"
self:onDecodeError(error_message, text, nil, options.etc)
return nil, error_message -- in case the error method doesn't abort, return something sensible
@ -558,7 +565,6 @@ function OBJDEF:decode(text, etc, options)
local success, value, next_i = pcall(grok_one, self, text, 1, options)
if success then
local error_message = nil
if next_i ~= #text + 1 then
-- something's left over after we parsed the first thing.... whitespace is allowed.
@ -570,9 +576,7 @@ function OBJDEF:decode(text, etc, options)
end
end
return value, error_message
else
-- If JSON:onDecodeError() didn't abort out of the pcall, we'll have received
-- the error message here as "value", so pass it along as an assert.
local error_message = value
@ -584,7 +588,6 @@ function OBJDEF:decode(text, etc, options)
-- ...and if we're still here (because the assert didn't throw an error),
-- return a nil and throw the error message on as a second arg
return nil, error_message
end
end
@ -595,7 +598,7 @@ end
OBJDEF.__index = OBJDEF
function OBJDEF:new(args)
local new = { }
local new = {}
if args then
for key, val in pairs(args) do
@ -607,5 +610,3 @@ function OBJDEF:new(args)
end
return OBJDEF:new()

View File

@ -66,7 +66,7 @@ function M:get_entries_from_lua_json(t)
line_number = raw_value.line_number,
key_start = raw_value.key_start,
value_start = raw_value.value_start,
}
},
}
table.insert(keys, entry)
@ -75,7 +75,7 @@ function M:get_entries_from_lua_json(t)
if type(v) == "table" then
local sub_keys = M:get_entries_from_lua_json(v)
for index=1, #sub_keys do
for index = 1, #sub_keys do
local sub_key = sub_keys[index]
---@type Entry
@ -129,7 +129,6 @@ function M:parse_lsp_value(result)
end
end
---@class Symbol
---@field name string
---@field kind number 2 = Object, 16 = Number, 15 = String, 18 = Array, 13 = Null, 17 = Boolean
@ -151,7 +150,7 @@ end
function M:get_entries_from_lsp_symbols(symbols)
local keys = {}
for index=1, #symbols do
for index = 1, #symbols do
local symbol = symbols[index]
local key = symbol.name
@ -167,14 +166,14 @@ function M:get_entries_from_lsp_symbols(symbols)
-- `"my_key": "my_value"`
-- Since we get the end of the key, we can just add 4 to get the start of the value
value_start = symbol.selectionRange["end"].character + 3,
}
},
}
keys[#keys + 1] = entry
if CONTAINS_CHILDREN_TYPES[symbol.kind] then
local sub_keys = M:get_entries_from_lsp_symbols(symbol.children)
for jindex=1, #sub_keys do
for jindex = 1, #sub_keys do
---@type Entry
local entry = {
key = key .. "." .. sub_keys[jindex].key,

View File

@ -86,7 +86,7 @@ function M:create_display_preview(value, conceal, render_objects)
if conceal then
return value, "string"
else
return "\"" .. value .. "\"", "string"
return '"' .. value .. '"', "string"
end
elseif t == "boolean" then
return value and "true" or "false", "boolean"
@ -198,7 +198,7 @@ function M:extract_key_description(text)
{
key = text,
type = "key",
}
},
}
end
@ -208,7 +208,9 @@ end
---@param name string
---@return boolean
function M:is_module_available(name)
return pcall(function() require(name) end) == true
return pcall(function()
require(name)
end) == true
end
return M

View File

@ -26,19 +26,19 @@
---@field null string - Highlight group for null values, Default: "@constant.builtin.json"
---@field other string - Highlight group for other types, Default: "@label.json"
local parsers = require"jsonfly.parsers"
local utils = require"jsonfly.utils"
local cache = require"jsonfly.cache"
local insert = require"jsonfly.insert"
local parsers = require("jsonfly.parsers")
local utils = require("jsonfly.utils")
local cache = require("jsonfly.cache")
local insert = require("jsonfly.insert")
local json = require"jsonfly.json"
local finders = require "telescope.finders"
local pickers = require "telescope.pickers"
local json = require("jsonfly.json")
local finders = require("telescope.finders")
local pickers = require("telescope.pickers")
local conf = require("telescope.config").values
local make_entry = require "telescope.make_entry"
local entry_display = require "telescope.pickers.entry_display"
local make_entry = require("telescope.make_entry")
local entry_display = require("telescope.pickers.entry_display")
local action_state = require "telescope.actions.state"
local action_state = require("telescope.actions.state")
---@type Options
local DEFAULT_CONFIG = {
@ -61,7 +61,7 @@ local DEFAULT_CONFIG = {
backend = "lsp",
use_cache = 500,
commands = {
add_key = {"i", "<C-a>"},
add_key = { "i", "<C-a>" },
copy_jsonpath = {
"i",
"<C-j>",
@ -69,9 +69,9 @@ local DEFAULT_CONFIG = {
---@param prompt_bufnr number
function(path, prompt_bufnr)
vim.fn.setreg("+", path)
end
}
}
end,
},
},
}
local global_config = {}
@ -82,14 +82,14 @@ local function show_picker(entries, buffer, xopts)
local config = vim.tbl_deep_extend("force", global_config, xopts or {})
local filename = vim.api.nvim_buf_get_name(buffer)
local displayer = entry_display.create {
local displayer = entry_display.create({
separator = " ",
items = {
{ width = 1 },
global_config.key_exact_length and { width = global_config.key_max_length } or { remaining = true },
{ remaining = true },
},
}
})
---@type boolean
local conceal
@ -99,21 +99,18 @@ local function show_picker(entries, buffer, xopts)
conceal = global_config.conceal == true
end
pickers.new(config, {
pickers
.new(config, {
prompt_title = global_config.prompt_title,
attach_mappings = function(_, map)
map(
global_config.commands.add_key[1],
global_config.commands.add_key[2],
function(prompt_bufnr)
map(global_config.commands.add_key[1], global_config.commands.add_key[2], function(prompt_bufnr)
local current_picker = action_state.get_current_picker(prompt_bufnr)
local input = current_picker:_get_prompt()
local key_descriptor = utils:extract_key_description(input)
insert:insert_new_key(entries, key_descriptor, buffer)
end
)
end)
if global_config.commands.copy_jsonpath and utils:is_module_available("jsonpath") then
map(
@ -125,13 +122,16 @@ local function show_picker(entries, buffer, xopts)
local current_picker = action_state.get_current_picker(prompt_bufnr)
local selection = current_picker:get_selection()
local path = jsonpath.get(vim.treesitter.get_node({
local path = jsonpath.get(
vim.treesitter.get_node({
bufnr = buffer,
pos = {
selection.lnum - 1,
selection.index,
}
}), buffer)
},
}),
buffer
)
if path then
global_config.commands.copy_jsonpath[3](path, prompt_bufnr)
@ -142,7 +142,7 @@ local function show_picker(entries, buffer, xopts)
return true
end,
finder = finders.new_table {
finder = finders.new_table({
results = entries,
---@param entry Entry
entry_maker = function(entry)
@ -153,12 +153,17 @@ local function show_picker(entries, buffer, xopts)
value = buffer,
ordinal = entry.key,
display = function(_)
local preview, hl_group_key = utils:create_display_preview(entry.value, conceal, global_config.show_nested_child_preview)
local preview, hl_group_key = utils:create_display_preview(
entry.value,
conceal,
global_config.show_nested_child_preview
)
local key = global_config.subkeys_display == "normal" and entry.key or utils:replace_previous_keys(entry.key, " ")
local key = global_config.subkeys_display == "normal" and entry.key
or utils:replace_previous_keys(entry.key, " ")
return displayer {
{ depth, "TelescopeResultsNumber"},
return displayer({
{ depth, "TelescopeResultsNumber" },
{
utils:truncate_overflow(
key,
@ -175,26 +180,26 @@ local function show_picker(entries, buffer, xopts)
),
global_config.highlights[hl_group_key] or "TelescopeResultsString",
},
}
})
end,
bufnr = buffer,
filename = filename,
lnum = entry.position.line_number,
col = global_config.jump_behavior == "key_start"
and entry.position.key_start
col = global_config.jump_behavior == "key_start" and entry.position.key_start
-- Use length ("#" operator) as vim jumps to the bytes, not characters
or entry.position.value_start
or entry.position.value_start,
}, config)
end,
},
}),
previewer = conf.grep_previewer(config),
sorter = conf.generic_sorter(config),
sorting_strategy = "ascending",
}):find()
})
:find()
end
return require("telescope").register_extension {
return require("telescope").register_extension({
setup = function(extension_config)
global_config = vim.tbl_deep_extend("force", DEFAULT_CONFIG, extension_config or {})
end,
@ -231,30 +236,50 @@ return require("telescope").register_extension {
if global_config.backend == "lsp" then
local params = vim.lsp.util.make_position_params(xopts.winnr)
vim.lsp.buf_request_all(
current_buf,
"textDocument/documentSymbol",
params,
function(response)
if response == nil or #response == 0 then
-- Check i
local clients = vim.lsp.get_clients()
local any_support = false
for _, client in ipairs(clients) do
if client.supports_method("textDocument/documentSymbol") then
any_support = true
break
end
end
if any_support then
vim.lsp.buf_request_all(current_buf, "textDocument/documentSymbol", params, function(results)
if results == nil or vim.tbl_isempty(results) then
run_lua_parser()
return
end
local result = response[1].result
local combined_result = {}
local entries = parsers:get_entries_from_lsp_symbols(result)
for _, res in pairs(results) do
if res.result then
vim.list_extend(combined_result, res.result)
end
end
if vim.tbl_isempty(combined_result) then
run_lua_parser()
return
end
local entries = parsers:get_entries_from_lsp_symbols(combined_result)
if allow_cache then
cache:cache_buffer(current_buf, entries)
end
show_picker(entries, current_buf, xopts)
end)
end
)
else
end
run_lua_parser()
end
end
}
}
end,
},
})