Files
tia-lsp/src/json.lua
T
lazar 96c0c4584f fix(lsp): Fix LSP server attachment and JSON parsing issues
- Replace lspconfig.scl_lsp registration with vim.lsp.start() + FileType autocmd
  for Neovim 0.11+ compatibility
- Fix Lua reserved keyword 'end' in tables (use bracket notation ['end'])
- Fix JSON decoder pattern matching bugs (s:find -> s:sub:match)
- Add LSP method name conversion (textDocument/didOpen -> textDocument_didOpen)
- Handle request vs notification correctly (only respond to requests with id)
- Strip CRLF line endings for Windows compatibility
- Add semantic token handler aliases
- Update documentation with implementation notes
2026-02-18 13:03:14 +01:00

217 lines
4.3 KiB
Lua

local json = {}
local function is_array(t)
local i = 0
for _ in pairs(t) do
i = i + 1
if t[i] == nil then
return false
end
end
return true
end
local function escape(s)
s = s:gsub("\\", "\\\\")
s = s:gsub('"', '\\"')
s = s:gsub("\n", "\\n")
s = s:gsub("\r", "\\r")
s = s:gsub("\t", "\\t")
return s
end
function json.encode(data)
local t = type(data)
if t == "nil" then
return "null"
elseif t == "boolean" then
return data and "true" or "false"
elseif t == "number" then
return tostring(data)
elseif t == "string" then
return '"' .. escape(data) .. '"'
elseif t == "table" then
local parts = {}
if is_array(data) then
for _, v in ipairs(data) do
table.insert(parts, json.encode(v))
end
return "[" .. table.concat(parts, ",") .. "]"
else
for k, v in pairs(data) do
table.insert(parts, '"' .. escape(k) .. '":' .. json.encode(v))
end
return "{" .. table.concat(parts, ",") .. "}"
end
else
return "null"
end
end
local function skip_whitespace(s, i)
while i <= #s and s:sub(i, i):match("%s") do
i = i + 1
end
return i
end
local function parse_string(s, i)
local result = {}
i = i + 1
while i <= #s do
local c = s:sub(i, i)
if c == '"' then
return table.concat(result), i + 1
elseif c == "\\" and i < #s then
local next_c = s:sub(i + 1, i + 1)
if next_c == "n" then
table.insert(result, "\n")
elseif next_c == "r" then
table.insert(result, "\r")
elseif next_c == "t" then
table.insert(result, "\t")
elseif next_c == '"' then
table.insert(result, '"')
elseif next_c == "\\" then
table.insert(result, "\\")
else
table.insert(result, next_c)
end
i = i + 2
else
table.insert(result, c)
i = i + 1
end
end
return nil, i
end
local function parse_number(s, i)
local start = i
if s:sub(i, i) == "-" then
i = i + 1
end
while i <= #s and s:sub(i, i):match("%d") do
i = i + 1
end
if s:sub(i, i) == "." then
i = i + 1
while i <= #s and s:sub(i, i):match("%d") do
i = i + 1
end
end
if s:sub(i, i):match("[eE]") then
i = i + 1
if s:sub(i, i):match("[+-]") then
i = i + 1
end
while i <= #s and s:sub(i, i):match("%d") do
i = i + 1
end
end
return tonumber(s:sub(start, i - 1)), i
end
local parse_value
local function parse_array(s, i)
local arr = {}
i = i + 1
while i <= #s do
i = skip_whitespace(s, i)
if s:sub(i, i) == "]" then
return arr, i + 1
end
local val
val, i = parse_value(s, i)
table.insert(arr, val)
i = skip_whitespace(s, i)
if s:sub(i, i) == "]" then
return arr, i + 1
end
if s:sub(i, i) == "," then
i = i + 1
end
end
return nil, i
end
local function parse_object(s, i)
local obj = {}
i = i + 1
while i <= #s do
i = skip_whitespace(s, i)
if s:sub(i, i) == "}" then
return obj, i + 1
end
if s:sub(i, i) == '"' then
local key
key, i = parse_string(s, i)
i = skip_whitespace(s, i)
if s:sub(i, i) ~= ":" then
return nil, i
end
i = i + 1
local val
val, i = parse_value(s, i)
obj[key] = val
i = skip_whitespace(s, i)
if s:sub(i, i) == "}" then
return obj, i + 1
end
if s:sub(i, i) == "," then
i = i + 1
end
else
return nil, i
end
end
return nil, i
end
parse_value = function(s, i)
i = skip_whitespace(s, i)
if i > #s then
return nil, i
end
local c = s:sub(i, i)
if c == "{" then
return parse_object(s, i)
elseif c == "[" then
return parse_array(s, i)
elseif c == '"' then
return parse_string(s, i)
elseif c == "t" then
if s:sub(i, i + 3) == "true" then
return true, i + 4
end
return nil, i
elseif c == "f" then
if s:sub(i, i + 4) == "false" then
return false, i + 5
end
return nil, i
elseif c == "n" then
if s:sub(i, i + 3) == "null" then
return nil, i + 4
end
return nil, i
elseif c == "-" or c:match("%d") then
return parse_number(s, i)
else
return nil, i
end
end
function json.decode(s)
if type(s) ~= "string" then
return nil, "invalid input"
end
local data, i = parse_value(s, 1)
return data, i
end
return json