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:
@@ -104,6 +104,19 @@ function M.clear_cache()
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Lua Reserved Keywords
|
||||||
|
|
||||||
|
Lua reserved keywords (like `end`, `for`, `in`, etc.) cannot be used as table keys directly. Use bracket notation:
|
||||||
|
```lua
|
||||||
|
-- WRONG: causes syntax error
|
||||||
|
local range = { start = pos1, end = pos2 }
|
||||||
|
|
||||||
|
-- CORRECT: use bracket notation
|
||||||
|
local range = { start = pos1, ["end"] = pos2 }
|
||||||
|
```
|
||||||
|
|
||||||
|
This is especially important for LSP range objects which require an `end` field per the LSP specification.
|
||||||
|
|
||||||
## Error Handling
|
## Error Handling
|
||||||
|
|
||||||
### Return Pattern
|
### Return Pattern
|
||||||
@@ -166,3 +179,34 @@ Find the last `:=`, `=>`, `=`, `<>`, `>=`, `<=`, `>`, `<`, `AND`, `OR`, `NOT` an
|
|||||||
| `:SCLMultilineParams` | Fill multiline parameters for FB/Function call |
|
| `:SCLMultilineParams` | Fill multiline parameters for FB/Function call |
|
||||||
| `:LspSCLFormat` | Format current SCL file |
|
| `:LspSCLFormat` | Format current SCL file |
|
||||||
| `:SCLGeneratePlcJson` | Generate plc.data.json from data_types/ |
|
| `:SCLGeneratePlcJson` | Generate plc.data.json from data_types/ |
|
||||||
|
|
||||||
|
## LSP Server Implementation Notes
|
||||||
|
|
||||||
|
### Method Name Conversion
|
||||||
|
LSP methods like `textDocument/didOpen` are converted to handler names like `textDocument_didOpen`:
|
||||||
|
```lua
|
||||||
|
local method_name = message.method:gsub("/", "_")
|
||||||
|
local handler = handlers[method_name]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Request vs Notification
|
||||||
|
- Requests have `id` field and require a response
|
||||||
|
- Notifications have no `id` and should not receive a response
|
||||||
|
```lua
|
||||||
|
if message.id then
|
||||||
|
-- Only send response for requests
|
||||||
|
return { id = message.id, result = result }
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### Line Ending Handling
|
||||||
|
The LSP server strips CRLF (`\r\n`) line endings for Windows compatibility:
|
||||||
|
```lua
|
||||||
|
line = line:gsub("\r$", "")
|
||||||
|
```
|
||||||
|
|
||||||
|
### JSON Parser
|
||||||
|
The standalone JSON parser in `src/json.lua` handles:
|
||||||
|
- Objects, arrays, strings, numbers, booleans, null
|
||||||
|
- Escape sequences in strings
|
||||||
|
- Whitespace skipping
|
||||||
|
|||||||
@@ -253,10 +253,24 @@ myVar.temperature := 25.5;
|
|||||||
END_FUNCTION_BLOCK
|
END_FUNCTION_BLOCK
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
The plugin uses a standalone LSP server (`src/main.lua`) that communicates via JSON-RPC over stdio. The server handles:
|
||||||
|
- Text document synchronization
|
||||||
|
- Diagnostics (linting)
|
||||||
|
- Formatting
|
||||||
|
- Completion, hover, go-to-definition
|
||||||
|
- Semantic tokens
|
||||||
|
|
||||||
|
The Neovim plugin (`lua/scl_lsp/init.lua`) manages:
|
||||||
|
- LSP client lifecycle via `vim.lsp.start()`
|
||||||
|
- FileType autocmd for lazy loading
|
||||||
|
- blink.cmp integration
|
||||||
|
- Workspace scanning for UDTs/DBs
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
- **Neovim 0.9+**
|
- **Neovim 0.11+** (uses `vim.lsp.start()`)
|
||||||
- **nvim-lspconfig** - LSP client
|
|
||||||
- **nvim-treesitter** - Syntax highlighting
|
- **nvim-treesitter** - Syntax highlighting
|
||||||
- **blink.cmp** - Optional, for completion
|
- **blink.cmp** - Optional, for completion
|
||||||
- **Lua 5.1+** - LSP server runtime
|
- **Lua 5.1+** - LSP server runtime
|
||||||
|
|||||||
+17
-2
@@ -1,6 +1,21 @@
|
|||||||
-- SCL Diagnostics - Linter functionality for the LSP server
|
-- SCL Diagnostics - Linter functionality for the LSP server
|
||||||
local M = {}
|
local M = {}
|
||||||
local parser = require("scl_lsp.parser")
|
|
||||||
|
-- Get script path for loading parser (same pattern as main.lua)
|
||||||
|
local script_path = debug.getinfo(1, "S").source:gsub("^@", ""):match("(.*/)") or ""
|
||||||
|
if script_path == "" then
|
||||||
|
script_path = "."
|
||||||
|
end
|
||||||
|
if script_path:sub(1, 1) ~= "/" then
|
||||||
|
local cwd = io.popen("pwd")
|
||||||
|
if cwd then
|
||||||
|
script_path = cwd:read("*l") .. "/" .. script_path
|
||||||
|
cwd:close()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
package.path = package.path .. ";" .. script_path .. "/?.lua"
|
||||||
|
|
||||||
|
local parser = dofile(script_path .. "/parser.lua")
|
||||||
|
|
||||||
local DiagnosticSeverity = {
|
local DiagnosticSeverity = {
|
||||||
Error = 1,
|
Error = 1,
|
||||||
@@ -19,7 +34,7 @@ end
|
|||||||
local function range_to_lsp(start_line, start_char, end_line, end_char)
|
local function range_to_lsp(start_line, start_char, end_line, end_char)
|
||||||
return {
|
return {
|
||||||
start = position_to_lsp(start_line, start_char),
|
start = position_to_lsp(start_line, start_char),
|
||||||
end = position_to_lsp(end_line, end_char),
|
["end"] = position_to_lsp(end_line, end_char),
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -8,7 +8,7 @@ end
|
|||||||
local function range(start_line, start_char, end_line, end_char)
|
local function range(start_line, start_char, end_line, end_char)
|
||||||
return {
|
return {
|
||||||
start = position(start_line, start_char),
|
start = position(start_line, start_char),
|
||||||
end = position(end_line, end_char),
|
["end"] = position(end_line, end_char),
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
+91
-70
@@ -13,7 +13,7 @@ end
|
|||||||
|
|
||||||
local function escape(s)
|
local function escape(s)
|
||||||
s = s:gsub("\\", "\\\\")
|
s = s:gsub("\\", "\\\\")
|
||||||
s = s:gsub("\"", "\\\"")
|
s = s:gsub('"', '\\"')
|
||||||
s = s:gsub("\n", "\\n")
|
s = s:gsub("\n", "\\n")
|
||||||
s = s:gsub("\r", "\\r")
|
s = s:gsub("\r", "\\r")
|
||||||
s = s:gsub("\t", "\\t")
|
s = s:gsub("\t", "\\t")
|
||||||
@@ -29,7 +29,7 @@ function json.encode(data)
|
|||||||
elseif t == "number" then
|
elseif t == "number" then
|
||||||
return tostring(data)
|
return tostring(data)
|
||||||
elseif t == "string" then
|
elseif t == "string" then
|
||||||
return "\"" .. escape(data) .. "\""
|
return '"' .. escape(data) .. '"'
|
||||||
elseif t == "table" then
|
elseif t == "table" then
|
||||||
local parts = {}
|
local parts = {}
|
||||||
if is_array(data) then
|
if is_array(data) then
|
||||||
@@ -39,7 +39,7 @@ function json.encode(data)
|
|||||||
return "[" .. table.concat(parts, ",") .. "]"
|
return "[" .. table.concat(parts, ",") .. "]"
|
||||||
else
|
else
|
||||||
for k, v in pairs(data) do
|
for k, v in pairs(data) do
|
||||||
table.insert(parts, "\"" .. escape(k) .. "\":" .. json.encode(v))
|
table.insert(parts, '"' .. escape(k) .. '":' .. json.encode(v))
|
||||||
end
|
end
|
||||||
return "{" .. table.concat(parts, ",") .. "}"
|
return "{" .. table.concat(parts, ",") .. "}"
|
||||||
end
|
end
|
||||||
@@ -49,7 +49,7 @@ function json.encode(data)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function skip_whitespace(s, i)
|
local function skip_whitespace(s, i)
|
||||||
while i <= #s and s:find("^%s", i) do
|
while i <= #s and s:sub(i, i):match("%s") do
|
||||||
i = i + 1
|
i = i + 1
|
||||||
end
|
end
|
||||||
return i
|
return i
|
||||||
@@ -60,7 +60,7 @@ local function parse_string(s, i)
|
|||||||
i = i + 1
|
i = i + 1
|
||||||
while i <= #s do
|
while i <= #s do
|
||||||
local c = s:sub(i, i)
|
local c = s:sub(i, i)
|
||||||
if c == "\"" then
|
if c == '"' then
|
||||||
return table.concat(result), i + 1
|
return table.concat(result), i + 1
|
||||||
elseif c == "\\" and i < #s then
|
elseif c == "\\" and i < #s then
|
||||||
local next_c = s:sub(i + 1, i + 1)
|
local next_c = s:sub(i + 1, i + 1)
|
||||||
@@ -70,9 +70,9 @@ local function parse_string(s, i)
|
|||||||
table.insert(result, "\r")
|
table.insert(result, "\r")
|
||||||
elseif next_c == "t" then
|
elseif next_c == "t" then
|
||||||
table.insert(result, "\t")
|
table.insert(result, "\t")
|
||||||
elseif next_c == "\\\"" then
|
elseif next_c == '"' then
|
||||||
table.insert(result, "\"")
|
table.insert(result, '"')
|
||||||
elseif next_c == "\\\\" then
|
elseif next_c == "\\" then
|
||||||
table.insert(result, "\\")
|
table.insert(result, "\\")
|
||||||
else
|
else
|
||||||
table.insert(result, next_c)
|
table.insert(result, next_c)
|
||||||
@@ -91,28 +91,85 @@ local function parse_number(s, i)
|
|||||||
if s:sub(i, i) == "-" then
|
if s:sub(i, i) == "-" then
|
||||||
i = i + 1
|
i = i + 1
|
||||||
end
|
end
|
||||||
while i <= #s and s:find("^%d", s:sub(i, i)) do
|
while i <= #s and s:sub(i, i):match("%d") do
|
||||||
i = i + 1
|
i = i + 1
|
||||||
end
|
end
|
||||||
if s:sub(i, i) == "." then
|
if s:sub(i, i) == "." then
|
||||||
i = i + 1
|
i = i + 1
|
||||||
while i <= #s and s:find("^%d", s:sub(i, i)) do
|
while i <= #s and s:sub(i, i):match("%d") do
|
||||||
i = i + 1
|
i = i + 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if s:sub(i, i):find("^[eE]") then
|
if s:sub(i, i):match("[eE]") then
|
||||||
i = i + 1
|
i = i + 1
|
||||||
if s:sub(i, i):find("^[+-]") then
|
if s:sub(i, i):match("[+-]") then
|
||||||
i = i + 1
|
i = i + 1
|
||||||
end
|
end
|
||||||
while i <= #s and s:find("^%d", s:sub(i, i)) do
|
while i <= #s and s:sub(i, i):match("%d") do
|
||||||
i = i + 1
|
i = i + 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return tonumber(s:sub(start, i - 1)), i
|
return tonumber(s:sub(start, i - 1)), i
|
||||||
end
|
end
|
||||||
|
|
||||||
local function parse_value(s, i)
|
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)
|
i = skip_whitespace(s, i)
|
||||||
if i > #s then
|
if i > #s then
|
||||||
return nil, i
|
return nil, i
|
||||||
@@ -121,66 +178,30 @@ local function parse_value(s, i)
|
|||||||
local c = s:sub(i, i)
|
local c = s:sub(i, i)
|
||||||
|
|
||||||
if c == "{" then
|
if c == "{" then
|
||||||
local obj = {}
|
return parse_object(s, i)
|
||||||
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
|
|
||||||
elseif c == "[" then
|
elseif c == "[" then
|
||||||
local arr = {}
|
return parse_array(s, i)
|
||||||
i = i + 1
|
elseif c == '"' then
|
||||||
while i <= #s do
|
return parse_string(s, i)
|
||||||
i = skip_whitespace(s, i)
|
elseif c == "t" then
|
||||||
if s:sub(i, i) == "]" then
|
if s:sub(i, i + 3) == "true" then
|
||||||
return arr, i + 1
|
return true, i + 4
|
||||||
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
|
end
|
||||||
return nil, i
|
return nil, i
|
||||||
elseif c == "\"" then
|
elseif c == "f" then
|
||||||
return parse_string(s, i)
|
if s:sub(i, i + 4) == "false" then
|
||||||
elseif s:sub(i, i + 3) == "null" then
|
return false, i + 5
|
||||||
return nil, i + 4
|
end
|
||||||
elseif s:sub(i, i + 3) == "true" then
|
return nil, i
|
||||||
return true, i + 4
|
elseif c == "n" then
|
||||||
elseif s:sub(i, i + 4) == "false" then
|
if s:sub(i, i + 3) == "null" then
|
||||||
return false, i + 5
|
return nil, i + 4
|
||||||
else
|
end
|
||||||
|
return nil, i
|
||||||
|
elseif c == "-" or c:match("%d") then
|
||||||
return parse_number(s, i)
|
return parse_number(s, i)
|
||||||
|
else
|
||||||
|
return nil, i
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
+32
-16
@@ -296,7 +296,7 @@ function handlers.textDocument_hover(params)
|
|||||||
contents = { kind = "markdown", value = detail },
|
contents = { kind = "markdown", value = detail },
|
||||||
range = {
|
range = {
|
||||||
start = { line = params.position.line, character = word_start - 1 },
|
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
|
end
|
||||||
@@ -315,7 +315,7 @@ function handlers.textDocument_hover(params)
|
|||||||
contents = { kind = "markdown", value = detail },
|
contents = { kind = "markdown", value = detail },
|
||||||
range = {
|
range = {
|
||||||
start = { line = params.position.line, character = word_start - 1 },
|
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
|
end
|
||||||
@@ -449,7 +449,7 @@ function handlers.textDocument_definition(params)
|
|||||||
uri = uri,
|
uri = uri,
|
||||||
range = {
|
range = {
|
||||||
start = { line = pos.line, character = pos.character },
|
start = { line = pos.line, character = pos.character },
|
||||||
endPos = { line = pos.line, character = pos.character + #word },
|
["end"] = { line = pos.line, character = pos.character + #word },
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
@@ -478,10 +478,10 @@ function handlers.textDocument_documentSymbol(params)
|
|||||||
table.insert(symbols, {
|
table.insert(symbols, {
|
||||||
name = func.kind:upper() .. " " .. func.name,
|
name = func.kind:upper() .. " " .. func.name,
|
||||||
kind = kind_map[func.kind] or 14,
|
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 = {
|
selectionRange = {
|
||||||
start = { line = func.start, character = 0 },
|
start = { line = func.start, character = 0 },
|
||||||
endPos = { line = func.start, character = #func.name },
|
["end"] = { line = func.start, character = #func.name },
|
||||||
},
|
},
|
||||||
containerName = nil,
|
containerName = nil,
|
||||||
})
|
})
|
||||||
@@ -500,11 +500,11 @@ function handlers.textDocument_documentSymbol(params)
|
|||||||
detail = var_info.type or "VAR",
|
detail = var_info.type or "VAR",
|
||||||
range = {
|
range = {
|
||||||
start = { line = var_info.line, character = 0 },
|
start = { line = var_info.line, character = 0 },
|
||||||
endPos = { line = var_info.line, character = #var_name },
|
["end"] = { line = var_info.line, character = #var_name },
|
||||||
},
|
},
|
||||||
selectionRange = {
|
selectionRange = {
|
||||||
start = { line = var_info.line, character = var_info.character },
|
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,
|
containerName = nil,
|
||||||
})
|
})
|
||||||
@@ -526,11 +526,11 @@ function handlers.textDocument_documentSymbol(params)
|
|||||||
detail = detail,
|
detail = detail,
|
||||||
range = {
|
range = {
|
||||||
start = { line = type_info.start_line or 0, character = 0 },
|
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 = {
|
selectionRange = {
|
||||||
start = { line = type_info.start_line or 0, character = 0 },
|
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,
|
containerName = nil,
|
||||||
})
|
})
|
||||||
@@ -656,6 +656,10 @@ function handlers.textDocument_semanticTokensRange(params)
|
|||||||
return handlers.textDocument_semanticTokensFull(params)
|
return handlers.textDocument_semanticTokensFull(params)
|
||||||
end
|
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)
|
function handlers.textDocument_inlayHint(params)
|
||||||
local uri = params.textDocument.uri
|
local uri = params.textDocument.uri
|
||||||
local doc = documents[uri]
|
local doc = documents[uri]
|
||||||
@@ -708,7 +712,7 @@ function handlers.workspace_symbol(params)
|
|||||||
uri = uri,
|
uri = uri,
|
||||||
range = {
|
range = {
|
||||||
start = { line = line, character = col },
|
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
|
if message.method == "$/cancelRequest" then
|
||||||
return nil
|
return nil
|
||||||
end
|
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
|
if handler then
|
||||||
local ok, result = pcall(handler, message.params)
|
local ok, result = pcall(handler, message.params)
|
||||||
if ok then
|
-- Only return response for requests (have id), not notifications
|
||||||
return { id = message.id, result = result }
|
if message.id then
|
||||||
else
|
if ok then
|
||||||
return { id = message.id, error = { code = -32603, message = tostring(result) } }
|
return { id = message.id, result = result }
|
||||||
|
else
|
||||||
|
return { id = message.id, error = { code = -32603, message = tostring(result) } }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
else
|
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
|
end
|
||||||
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local function main()
|
local function main()
|
||||||
@@ -871,6 +884,9 @@ local function main()
|
|||||||
if not line then
|
if not line then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
-- Strip trailing \r (Windows line endings)
|
||||||
|
line = line:gsub("\r$", "")
|
||||||
|
|
||||||
if line:match("^Content%-Length:%s*(%d+)") then
|
if line:match("^Content%-Length:%s*(%d+)") then
|
||||||
content_length = tonumber(line:match("^Content%-Length:%s*(%d+)"))
|
content_length = tonumber(line:match("^Content%-Length:%s*(%d+)"))
|
||||||
elseif line == "" then
|
elseif line == "" then
|
||||||
|
|||||||
Reference in New Issue
Block a user