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
This commit is contained in:
2026-02-18 13:03:14 +01:00
parent 4429280524
commit 96c0c4584f
6 changed files with 201 additions and 91 deletions
+32 -16
View File
@@ -296,7 +296,7 @@ function handlers.textDocument_hover(params)
contents = { kind = "markdown", value = detail },
range = {
start = { line = params.position.line, character = word_start - 1 },
endPos = { line = params.position.line, character = word_end - 1 },
["end"] = { line = params.position.line, character = word_end - 1 },
},
}
end
@@ -315,7 +315,7 @@ function handlers.textDocument_hover(params)
contents = { kind = "markdown", value = detail },
range = {
start = { line = params.position.line, character = word_start - 1 },
endPos = { line = params.position.line, character = word_end - 1 },
["end"] = { line = params.position.line, character = word_end - 1 },
},
}
end
@@ -449,7 +449,7 @@ function handlers.textDocument_definition(params)
uri = uri,
range = {
start = { line = pos.line, character = pos.character },
endPos = { line = pos.line, character = pos.character + #word },
["end"] = { line = pos.line, character = pos.character + #word },
},
}
end
@@ -478,10 +478,10 @@ function handlers.textDocument_documentSymbol(params)
table.insert(symbols, {
name = func.kind:upper() .. " " .. func.name,
kind = kind_map[func.kind] or 14,
range = { start = { line = func.start, character = 0 }, endPos = { line = func.final, character = 0 } },
range = { start = { line = func.start, character = 0 }, ["end"] = { line = func.final, character = 0 } },
selectionRange = {
start = { line = func.start, character = 0 },
endPos = { line = func.start, character = #func.name },
["end"] = { line = func.start, character = #func.name },
},
containerName = nil,
})
@@ -500,11 +500,11 @@ function handlers.textDocument_documentSymbol(params)
detail = var_info.type or "VAR",
range = {
start = { line = var_info.line, character = 0 },
endPos = { line = var_info.line, character = #var_name },
["end"] = { line = var_info.line, character = #var_name },
},
selectionRange = {
start = { line = var_info.line, character = var_info.character },
endPos = { line = var_info.line, character = var_info.character + #var_name },
["end"] = { line = var_info.line, character = var_info.character + #var_name },
},
containerName = nil,
})
@@ -526,11 +526,11 @@ function handlers.textDocument_documentSymbol(params)
detail = detail,
range = {
start = { line = type_info.start_line or 0, character = 0 },
endPos = { line = type_info.start_line or 0, character = #type_name },
["end"] = { line = type_info.start_line or 0, character = #type_name },
},
selectionRange = {
start = { line = type_info.start_line or 0, character = 0 },
endPos = { line = type_info.start_line or 0, character = #type_name },
["end"] = { line = type_info.start_line or 0, character = #type_name },
},
containerName = nil,
})
@@ -656,6 +656,10 @@ function handlers.textDocument_semanticTokensRange(params)
return handlers.textDocument_semanticTokensFull(params)
end
-- Aliases for method names with slashes converted to underscores
handlers["textDocument_semanticTokens_full"] = handlers.textDocument_semanticTokensFull
handlers["textDocument_semanticTokens_range"] = handlers.textDocument_semanticTokensRange
function handlers.textDocument_inlayHint(params)
local uri = params.textDocument.uri
local doc = documents[uri]
@@ -708,7 +712,7 @@ function handlers.workspace_symbol(params)
uri = uri,
range = {
start = { line = line, character = col },
endPos = { line = line, character = col + length },
["end"] = { line = line, character = col + length },
},
},
})
@@ -848,17 +852,26 @@ local function handle_message(message)
if message.method == "$/cancelRequest" then
return nil
end
local handler = handlers[message.method]
-- Convert method name from "textDocument/didOpen" to "textDocument_didOpen"
local method_name = message.method:gsub("/", "_")
local handler = handlers[method_name]
if handler then
local ok, result = pcall(handler, message.params)
if ok then
return { id = message.id, result = result }
else
return { id = message.id, error = { code = -32603, message = tostring(result) } }
-- Only return response for requests (have id), not notifications
if message.id then
if ok then
return { id = message.id, result = result }
else
return { id = message.id, error = { code = -32603, message = tostring(result) } }
end
end
else
return { id = message.id, error = { code = -32601, message = "Method not found: " .. message.method } }
-- Only return error for requests (have id)
if message.id then
return { id = message.id, error = { code = -32601, message = "Method not found: " .. message.method } }
end
end
return nil
end
local function main()
@@ -871,6 +884,9 @@ local function main()
if not line then
break
end
-- Strip trailing \r (Windows line endings)
line = line:gsub("\r$", "")
if line:match("^Content%-Length:%s*(%d+)") then
content_length = tonumber(line:match("^Content%-Length:%s*(%d+)"))
elseif line == "" then