From a923438f6781469c3d9d9a0264a0c852682d3d3d Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 14 Apr 2024 12:01:28 +0200 Subject: [PATCH] refactor: Outsource functions into own modules --- lua/jsonfly/parsers.lua | 142 +++++++++++++++++ lua/jsonfly/utils.lua | 64 ++++++++ lua/telescope/_extensions/jsonfly.lua | 220 ++------------------------ 3 files changed, 218 insertions(+), 208 deletions(-) create mode 100644 lua/jsonfly/parsers.lua create mode 100644 lua/jsonfly/utils.lua diff --git a/lua/jsonfly/parsers.lua b/lua/jsonfly/parsers.lua new file mode 100644 index 0000000..d86d3bc --- /dev/null +++ b/lua/jsonfly/parsers.lua @@ -0,0 +1,142 @@ +---@class EntryPosition +---@field line_number number +---@field key_start number +---@field value_start number +-- +---@class Entry +---@field key string +---@field value Entry|table|number|string|boolean|nil +---@field position EntryPosition + +local M = {} + +---@param t table +---@return Entry[] +function M:get_entries_from_lua_json(t) + local keys = {} + + for k, raw_value in pairs(t) do + ---@type Entry + local entry = { + key = k, + value = raw_value, + position = { + line_number = raw_value.newlines, + key_start = raw_value.key_start, + value_start = raw_value.value_start, + } + } + table.insert(keys, entry) + + local v = raw_value.value + + if type(v) == "table" then + local sub_keys = M:get_entries_from_lua_json(v) + + for _, sub_key in ipairs(sub_keys) do + ---@type Entry + local entry = { + key = k .. "." .. sub_key.key, + value = sub_key, + position = sub_key.position, + } + + table.insert(keys, entry) + end + end + end + + return keys +end + +---@param result Symbol +---@return string|number|table|boolean|nil +function M:parse_lsp_value(result) + if result.kind == 2 then + local value = {} + + for _, child in ipairs(result.children) do + value[child.name] = M:parse_lsp_value(child) + end + + return value + elseif result.kind == 16 then + return tonumber(result.detail) + elseif result.kind == 15 then + return result.detail + elseif result.kind == 18 then + local value = {} + + for i, child in ipairs(result.children) do + value[i] = M:parse_lsp_value(child) + end + + return value + elseif result.kind == 13 then + return nil + elseif result.kind == 17 then + return result.detail == "true" + end +end + + +---@class Symbol +---@field name string +---@field kind number 2 = Object, 16 = Number, 15 = String, 18 = Array, 13 = Null, 17 = Boolean +---@field range Range +---@field selectionRange Range +---@field detail string +---@field children Symbol[] +-- +---@class Range +---@field start Position +---@field ["end"] Position +-- +---@class Position +---@field line number +---@field character number +-- +---@param symbols Symbol[] +---@return Entry[] +function M:get_entries_from_lsp_symbols(symbols) + local keys = {} + + for _, symbol in ipairs(symbols) do + local key = symbol.name + + ---@type Entry + local entry = { + key = key, + value = M:parse_lsp_value(symbol), + position = { + line_number = symbol.range.start.line + 2, + key_start = symbol.range.start.character + 2, + -- The LSP doesn't return the start of the value, so we'll just assume it's 3 characters after the key + -- We assume a default JSON file like: + -- `"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 + 4 + } + } + table.insert(keys, entry) + + if symbol.kind == 2 then + local sub_keys = M:get_entries_from_lsp_symbols(symbol.children) + + for _, sub_key in ipairs(sub_keys) do + ---@type Entry + local entry = { + key = key .. "." .. sub_key.key, + value = sub_key.value, + position = sub_key.position, + } + + table.insert(keys, entry) + end + end + end + + return keys +end + +return M diff --git a/lua/jsonfly/utils.lua b/lua/jsonfly/utils.lua new file mode 100644 index 0000000..1e2ffbb --- /dev/null +++ b/lua/jsonfly/utils.lua @@ -0,0 +1,64 @@ +local M = {} + +function M:truncate_overflow(value, max_length, overflow_marker) + if vim.fn.strdisplaywidth(value) > max_length then + return value:sub(1, max_length - vim.fn.strdisplaywidth(overflow_marker)) .. overflow_marker + end + + return value +end + +---@param value any +---@param opts Options +function M:create_display_preview(value, opts) + local t = type(value) + local conceal + + if opts.conceal == "auto" then + conceal = vim.o.conceallevel > 0 + else + conceal = opts.conceal + end + + if t == "table" then + local preview_table = {} + + for k, v in pairs(value) do + table.insert(preview_table, k .. ": " .. M:create_display_preview(v, opts)) + end + + return "{ " .. table.concat(preview_table, ", ") .. " }", "other" + elseif t == "nil" then + return "null", "null" + elseif t == "number" then + return tostring(value), "number" + elseif t == "string" then + if conceal then + return value, "string" + else + return "\"" .. value .. "\"", "string" + end + elseif t == "boolean" then + return value and "true" or "false", "boolean" + end +end + +---@param key string +---@param replacement string +---@return string +---Replaces all previous keys with the replacement +---Example: replace_previous_keys("a.b.c", "x") => "xxx.c" +function M:replace_previous_keys(key, replacement) + for i = #key, 1, -1 do + if key:sub(i, i) == "." then + local len = i - 1 + local before = replacement:rep(len) + + return before .. "." .. key:sub(i + 1) + end + end + + return key +end + +return M diff --git a/lua/telescope/_extensions/jsonfly.lua b/lua/telescope/_extensions/jsonfly.lua index aa923ac..657f080 100644 --- a/lua/telescope/_extensions/jsonfly.lua +++ b/lua/telescope/_extensions/jsonfly.lua @@ -10,6 +10,7 @@ ---@field highlights Highlights - Highlight groups for different types ---@field jump_behavior "key_start"|"value_start" - Behavior for jumping to the location, "key_start" == Jump to the start of the key, "value_start" == Jump to the start of the value, Default: "key_start" ---@field subkeys_display "normal"|"waterfall" - Display subkeys in a normal or waterfall style, Default: "normal" +---@field backend "lua"|"lsp" - Backend to use for parsing JSON, "lua" = Use our own Lua parser to parse the JSON, "lsp" = Use your LSP to parse the JSON (currently only https://github.com/Microsoft/vscode-json-languageservice is supported). If the "lsp" backend is selected but the LSP fails, it will fallback to the "lua" backend, Default: "lsp" --- ---@class Highlights ---@field number string - Highlight group for numbers, Default: "@number.json" @@ -18,215 +19,17 @@ ---@field null string - Highlight group for null values, Default: "@constant.builtin.json" ---@field other string - Highlight group for other types, Default: "@label.json" ----- Types below are for internal use only ---- --- ----@class EntryPosition ----@field line_number number ----@field key_start number ----@field value_start number --- ----@class Entry ----@field key string ----@field value Entry|table|number|string|boolean|nil ----@field position EntryPosition +local parsers = require"jsonfly.parsers" +local json = require"jsonfly.json" +local utils = require"jsonfly.utils" local json = require"jsonfly.json" local finders = require "telescope.finders" local pickers = require "telescope.pickers" -local conf = require("telescope.config").values +local conf = require"telescope.config".values local make_entry = require "telescope.make_entry" local entry_display = require "telescope.pickers.entry_display" ----@param t table ----@return Entry[] -local function get_entries_from_lua_json(t) - local keys = {} - - for k, raw_value in pairs(t) do - ---@type Entry - local entry = { - key = k, - value = raw_value, - position = { - line_number = raw_value.newlines, - key_start = raw_value.key_start, - value_start = raw_value.value_start, - } - } - table.insert(keys, entry) - - local v = raw_value.value - - if type(v) == "table" then - local sub_keys = get_entries_from_lua_json(v) - - for _, sub_key in ipairs(sub_keys) do - ---@type Entry - local entry = { - key = k .. "." .. sub_key.key, - value = sub_key, - position = sub_key.position, - } - - table.insert(keys, entry) - end - end - end - - return keys -end - ----@param result Symbol ----@return string|number|table|boolean|nil -local function parse_lsp_value(result) - if result.kind == 2 then - local value = {} - - for _, child in ipairs(result.children) do - value[child.name] = parse_lsp_value(child) - end - - return value - elseif result.kind == 16 then - return tonumber(result.detail) - elseif result.kind == 15 then - return result.detail - elseif result.kind == 18 then - local value = {} - - for i, child in ipairs(result.children) do - value[i] = parse_lsp_value(child) - end - - return value - elseif result.kind == 13 then - return nil - elseif result.kind == 17 then - return result.detail == "true" - end -end - - ----@class Symbol ----@field name string ----@field kind number 2 = Object, 16 = Number, 15 = String, 18 = Array, 13 = Null, 17 = Boolean ----@field range Range ----@field selectionRange Range ----@field detail string ----@field children Symbol[] --- ----@class Range ----@field start Position ----@field ["end"] Position --- ----@class Position ----@field line number ----@field character number --- ----@param symbols Symbol[] ----@return Entry[] -local function get_entries_from_lsp_symbols(symbols) - local keys = {} - - for _, symbol in ipairs(symbols) do - local key = symbol.name - - ---@type Entry - local entry = { - key = key, - value = parse_lsp_value(symbol), - position = { - line_number = symbol.range.start.line + 2, - key_start = symbol.range.start.character + 2, - -- The LSP doesn't return the start of the value, so we'll just assume it's 3 characters after the key - -- We assume a default JSON file like: - -- `"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 + 4 - } - } - table.insert(keys, entry) - - if symbol.kind == 2 then - local sub_keys = get_entries_from_lsp_symbols(symbol.children) - - for _, sub_key in ipairs(sub_keys) do - ---@type Entry - local entry = { - key = key .. "." .. sub_key.key, - value = sub_key.value, - position = sub_key.position, - } - - table.insert(keys, entry) - end - end - end - - return keys -end - -local function truncate_overflow(value, max_length, overflow_marker) - if vim.fn.strdisplaywidth(value) > max_length then - return value:sub(1, max_length - vim.fn.strdisplaywidth(overflow_marker)) .. overflow_marker - end - - return value -end - ----@param value any ----@param opts Options -local function create_display_preview(value, opts) - local t = type(value) - local conceal - - if opts.conceal == "auto" then - conceal = vim.o.conceallevel > 0 - else - conceal = opts.conceal - end - - if t == "table" then - local preview_table = {} - - for k, v in pairs(value) do - table.insert(preview_table, k .. ": " .. create_display_preview(v, opts)) - end - - return "{ " .. table.concat(preview_table, ", ") .. " }", "other" - elseif t == "nil" then - return "null", "null" - elseif t == "number" then - return tostring(value), "number" - elseif t == "string" then - if conceal then - return value, "string" - else - return "\"" .. value .. "\"", "string" - end - elseif t == "boolean" then - return value and "true" or "false", "boolean" - end -end - ----@param key string ----@param replacement string ----@return string ----Replaces all previous keys with the replacement ----Example: replace_previous_keys("a.b.c", "x") => "xxx.c" -local function replace_previous_keys(key, replacement) - for i = #key, 1, -1 do - if key:sub(i, i) == "." then - local len = i - 1 - local before = replacement:rep(len) - - return before .. "." .. key:sub(i + 1) - end - end - - return key -end - ---@type Options local opts = { key_max_length = 50, @@ -244,6 +47,7 @@ local opts = { }, jump_behavior = "key_start", subkeys_display = "normal", + backend = "lsp", } return require"telescope".register_extension { @@ -258,7 +62,7 @@ return require"telescope".register_extension { local content = table.concat(content_lines, "\n") local parsed = json:decode(content) - local keys = get_entries_from_lua_json(parsed) + local keys = parsers:get_entries_from_lua_json(parsed) local displayer = entry_display.create { separator = " ", @@ -275,7 +79,7 @@ return require"telescope".register_extension { "textDocument/documentSymbol", params, function(_, result) - local keys = get_entries_from_lsp_symbols(result) + local keys = parsers:get_entries_from_lsp_symbols(result) pickers.new(opts, { prompt_title = opts.prompt_title, @@ -290,14 +94,14 @@ return require"telescope".register_extension { value = current_buf, ordinal = entry.key, display = function(_) - local preview, hl_group_key = create_display_preview(entry.value, opts) + local preview, hl_group_key = utils:create_display_preview(entry.value, opts) - local key = opts.subkeys_display == "normal" and entry.key or replace_previous_keys(entry.key, " ") + local key = opts.subkeys_display == "normal" and entry.key or utils:replace_previous_keys(entry.key, " ") return displayer { { depth, "TelescopeResultsNumber"}, { - truncate_overflow( + utils:truncate_overflow( key, opts.key_max_length, opts.overflow_marker @@ -305,7 +109,7 @@ return require"telescope".register_extension { "@property.json", }, { - truncate_overflow( + utils:truncate_overflow( preview, opts.max_length, opts.overflow_marker