fix(lsp): add more robust validation in semantic tokens
- Wrap token generation in pcall to catch any errors - Add validation for all token parameters (line, startChar, length) - Validate deltaStart is non-negative - Return empty tokens if any error occurs - This should prevent the 'Invalid line: out of range' errors
This commit is contained in:
+35
-36
@@ -563,7 +563,9 @@ function handlers.textDocument_semanticTokensFull(params)
|
|||||||
return { data = {} }
|
return { data = {} }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Protected token collection
|
||||||
local tokens = {}
|
local tokens = {}
|
||||||
|
local ok, err = pcall(function()
|
||||||
local lines = {}
|
local lines = {}
|
||||||
for line in doc.content:gmatch("([^\n]*)\n") do
|
for line in doc.content:gmatch("([^\n]*)\n") do
|
||||||
table.insert(lines, line)
|
table.insert(lines, line)
|
||||||
@@ -571,11 +573,21 @@ function handlers.textDocument_semanticTokensFull(params)
|
|||||||
|
|
||||||
local total_lines = #lines
|
local total_lines = #lines
|
||||||
|
|
||||||
local function add_token(line, startChar, length, tokenType)
|
local function add_token(line, startChar, length)
|
||||||
-- Validate line number is within bounds
|
-- Validate all parameters
|
||||||
|
if not line or not startChar or not length then
|
||||||
|
return
|
||||||
|
end
|
||||||
if line < 0 or line >= total_lines then
|
if line < 0 or line >= total_lines then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
if startChar < 0 or length <= 0 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if startChar + length > 10000 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
local deltaLine = line
|
local deltaLine = line
|
||||||
local deltaStart = startChar
|
local deltaStart = startChar
|
||||||
if #tokens >= 4 then
|
if #tokens >= 4 then
|
||||||
@@ -587,6 +599,10 @@ function handlers.textDocument_semanticTokensFull(params)
|
|||||||
else
|
else
|
||||||
deltaLine = line - prevLine
|
deltaLine = line - prevLine
|
||||||
end
|
end
|
||||||
|
-- Validate deltaStart is non-negative
|
||||||
|
if deltaStart < 0 then
|
||||||
|
return
|
||||||
|
end
|
||||||
end
|
end
|
||||||
table.insert(tokens, deltaLine)
|
table.insert(tokens, deltaLine)
|
||||||
table.insert(tokens, deltaStart)
|
table.insert(tokens, deltaStart)
|
||||||
@@ -601,55 +617,38 @@ function handlers.textDocument_semanticTokensFull(params)
|
|||||||
|
|
||||||
if doc.variables then
|
if doc.variables then
|
||||||
for var_name, info in pairs(doc.variables) do
|
for var_name, info in pairs(doc.variables) do
|
||||||
-- Validate variable line number is within bounds
|
if info and info.line and info.line >= 0 and info.line < total_lines and info.line == lineNum then
|
||||||
if info.line and info.line >= 0 and info.line < total_lines and info.line == lineNum then
|
|
||||||
local startChar = line:find(var_name, 1, true)
|
local startChar = line:find(var_name, 1, true)
|
||||||
if startChar then
|
if startChar then
|
||||||
add_token(lineNum, startChar - 1, #var_name, "variable")
|
add_token(lineNum, startChar - 1, #var_name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local keywords = {
|
local keywords = {
|
||||||
"IF",
|
"IF", "THEN", "ELSIF", "ELSE", "END_IF",
|
||||||
"THEN",
|
"FOR", "TO", "BY", "DO", "END_FOR",
|
||||||
"ELSIF",
|
"WHILE", "END_WHILE",
|
||||||
"ELSE",
|
"REPEAT", "UNTIL", "END_REPEAT",
|
||||||
"END_IF",
|
"CASE", "OF", "END_CASE",
|
||||||
"FOR",
|
"VAR_INPUT", "VAR_OUTPUT", "VAR_IN_OUT", "VAR_TEMP", "VAR", "END_VAR",
|
||||||
"TO",
|
"BEGIN", "EXIT", "CONTINUE", "RETURN",
|
||||||
"BY",
|
"TRUE", "FALSE",
|
||||||
"DO",
|
|
||||||
"END_FOR",
|
|
||||||
"WHILE",
|
|
||||||
"END_WHILE",
|
|
||||||
"REPEAT",
|
|
||||||
"UNTIL",
|
|
||||||
"END_REPEAT",
|
|
||||||
"CASE",
|
|
||||||
"OF",
|
|
||||||
"END_CASE",
|
|
||||||
"VAR_INPUT",
|
|
||||||
"VAR_OUTPUT",
|
|
||||||
"VAR_IN_OUT",
|
|
||||||
"VAR_TEMP",
|
|
||||||
"VAR",
|
|
||||||
"END_VAR",
|
|
||||||
"BEGIN",
|
|
||||||
"EXIT",
|
|
||||||
"CONTINUE",
|
|
||||||
"RETURN",
|
|
||||||
"TRUE",
|
|
||||||
"FALSE",
|
|
||||||
}
|
}
|
||||||
for _, kw in ipairs(keywords) do
|
for _, kw in ipairs(keywords) do
|
||||||
local startPos = line:find("%f[%a]" .. kw .. "%f[%A]")
|
local startPos = line:find("%f[%a]" .. kw .. "%f[%A]")
|
||||||
if startPos then
|
if startPos then
|
||||||
add_token(lineNum, startPos - 1, #kw, "keyword")
|
add_token(lineNum, startPos - 1, #kw)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- If any error occurred, return empty tokens
|
||||||
|
if not ok then
|
||||||
|
return { data = {} }
|
||||||
|
end
|
||||||
|
|
||||||
return { data = tokens }
|
return { data = tokens }
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user