Last active
February 28, 2021 22:07
-
-
Save justarandomgeek/4ae6be0d5e778e0c755583f57e33f706 to your computer and use it in GitHub Desktop.
sumneko plugin.lua for factorio
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--## | |
---@class diff | |
---@field start integer # The number of bytes at the beginning of the replacement | |
---@field finish integer # The number of bytes at the end of the replacement | |
---@field text string # What to replace | |
local replace_remotes | |
local type_list | |
---@param uri string # The uri of file | |
---@param text string # The content of file | |
---@return nil|diff[] | |
function OnSetText(uri, text) | |
if text:sub(1, 4)=="--##" then return end | |
local diffs = {} | |
---@type string|number | |
for start, name, finish in text:gmatch("require%s*%(?%s*['\"]()(.-)()['\"]%s*%)?") do | |
-- if name has slashes, convert to a dotted path | |
if name:match("[\\/]") then | |
name = name:gsub("%.lua$",""):gsub("[\\/]",".") | |
end | |
-- then convert the modname prefix, if any... | |
---@param match string | |
---@return string | |
name = name:gsub("^__(.-)__", function(match) | |
return match | |
end) | |
diffs[#diffs+1] = { | |
start = start, | |
finish = finish - 1, | |
text = name, | |
} | |
end | |
-- rename `global` so we can tell them apart! | |
local thismod = uri:match("mods[\\/]([^\\/]+)[\\/]") | |
if thismod then | |
local scenario = uri:match("scenarios[\\/]([^\\/]+)[\\/]") | |
if scenario then | |
thismod = thismod.."__"..scenario | |
end | |
thismod = thismod:gsub("[^a-zA-Z0-9_]","_") | |
local gname = "__"..thismod.."__global" | |
local replaced | |
---@type number | |
for start, finish in text:gmatch("[^a-zA-Z0-9_]()global()%s*[=.%[]") do | |
diffs[#diffs+1] = { | |
start = start, | |
finish = finish - 1, | |
text = gname, | |
} | |
replaced = true | |
end | |
-- and "define" it at the start of any file that used it | |
if replaced then | |
diffs[#diffs+1] = { | |
start = 1, | |
finish = 0, | |
text = gname.."={}\n", | |
} | |
end | |
end | |
replace_remotes(uri, text, diffs) | |
type_list(uri, text, diffs) | |
return diffs | |
end | |
---if str is a string wrapped in "" or '' get the string inside those quotes | |
---otherwise returns nil | |
---@param str string | |
---@return string|nil | |
local function try_get_source_string_contents(str) | |
return str:match("^[\"']") and str:sub(2, -2) | |
end | |
-- ---if str is a valid identifier, returns "." .. str, otherwise the [] equivalent | |
-- ---@param str string | |
-- ---@return string | |
-- local function use_string_to_index_into_table(str) | |
-- if str:match("^[a-zA-Z_][a-zA-Z0-9_]*$") then | |
-- return "." .. str | |
-- else | |
-- return '["' .. str .. '"]' | |
-- end | |
-- end | |
-- ---converts an identifier or string taken from source code | |
-- ---and converts it into a string that can be appended to something to index | |
-- ---into said something (most likely a table) | |
-- ---@param str string | |
-- ---@return string | |
-- local function use_source_to_index_into_table(str) | |
-- local str_contents = try_get_source_string_contents(str) | |
-- if str_contents then | |
-- return use_string_to_index_into_table(str_contents) | |
-- else | |
-- return "[" .. str .. "]" | |
-- end | |
-- end | |
---extends the text of a ChainDiffElem or setting it if it is nil | |
---@param elem ChainDiffElem | |
---@param text string | |
local function extend_chain_diff_elem_text(elem, text) | |
if elem.text then | |
elem.text = elem.text.. text | |
else | |
elem.text = text | |
end | |
end | |
---@param diffs diff[] | |
---@param start number | |
---@param finish number | |
---@param replacement string | |
local function add_diff(diffs, start, finish, replacement) | |
diffs[#diffs+1] = { | |
start = start, | |
finish = finish - 1, | |
text = replacement, | |
} | |
end | |
---@param chain_diff ChainDiffElem[] | |
---@param i_in_chain_diff number @ index of the elem in `chain_diff` that represents the source | |
---@param str string | |
local function modify_chain_diff_to_use_source_to_index_into_table(chain_diff, i_in_chain_diff, str) | |
local str_contents = try_get_source_string_contents(str) | |
if str_contents and str_contents:match("^[a-zA-Z_][a-zA-Z0-9_]*$") then | |
extend_chain_diff_elem_text(chain_diff[i_in_chain_diff - 1], ".") | |
chain_diff[i_in_chain_diff].text = str_contents | |
else | |
extend_chain_diff_elem_text(chain_diff[i_in_chain_diff - 1], "[") | |
extend_chain_diff_elem_text(chain_diff[i_in_chain_diff + 1], "]") | |
end | |
end | |
---@class ChainDiffElem | |
---@field i number @ index within the text of the file | |
---@field text nil|string @ text replacing from this elem's `i` including to the next elem's `i` excluding. When nil no diff will be created. If the last elem has `text` it will treat it as if there was another elem after with with the same `i` | |
---creates diffs according to the chain_diff. See ChainDiffElem class description for how it works | |
---@param chain_diff ChainDiffElem[] | |
---@param diffs diff[] | |
local function add_chain_diff(chain_diff, diffs) | |
local prev_chain_diff_elem = chain_diff[1] | |
if not prev_chain_diff_elem then return end | |
for i = 2, #chain_diff do | |
local chain_diff_elem = chain_diff[i] | |
if prev_chain_diff_elem.text then | |
diffs[#diffs+1] = { | |
start = prev_chain_diff_elem.i, | |
finish = chain_diff_elem.i - 1, -- finish is treated as including, which we don't want | |
text = prev_chain_diff_elem.text, | |
} | |
end | |
prev_chain_diff_elem = chain_diff_elem | |
end | |
if prev_chain_diff_elem.text then | |
diffs[#diffs+1] = { | |
start = prev_chain_diff_elem.i, | |
finish = prev_chain_diff_elem.i - 1, | |
text = prev_chain_diff_elem.text, | |
} | |
end | |
end | |
---@param uri string @ The uri of file | |
---@param text string @ The content of file | |
---@param diffs diff[] @ The diffs to add more diffs to | |
function replace_remotes(uri, text, diffs) | |
-- remote.add_interface | |
-- TODO: impl | |
-- ---@type string|number | |
-- for sadd, fadd, popen_parenth, sname, name, fname, picomma | |
-- in | |
-- text:gmatch("remote%s*%.%s*()add_interface()%s*()%(()%s*(.-)%s*()(),") | |
-- do | |
-- add_diff(diffs, sadd, fadd, "__all_remote_interfaces") | |
-- add_diff(diffs, popen_parenth, popen_parenth + 1, "") | |
-- add_diff(diffs, sname, fname, use_source_to_index_into_table(name)) | |
-- add_diff(diffs, picomma, picomma + 1, "=") | |
-- end | |
-- ---@type string|number | |
-- for start, finish in text:gmatch("()%}%)()") do | |
-- add_diff(diffs, start, finish, "}") | |
-- end | |
-- remote.call | |
-- this in particular needs to work as you're typing, not just once you're done | |
-- which segnificantly complicates things, like we can't use the commas as reliable anchors | |
-- s = start, f = finish, p = position, no prefix = an actual string capture | |
---@type string|number | |
for s_call, f_call, p_open_parenth, s_name | |
in | |
text:gmatch("remote%s*%.%s*()call()%s*()%(%s*()") | |
do | |
add_diff(diffs, s_call, f_call, "__all_remote_interfaces") | |
---@type ChainDiffElem[] | |
local chain_diff = {} | |
local open_parenth_diff = {i = p_open_parenth, text = ""} | |
chain_diff[1] = open_parenth_diff | |
---@type string|number|nil | |
local name, f_name, name_comma, s_param_2 = text:match("^([\"'][^\"']*[\"'])()%s*(,?)()", s_name) | |
if not name then | |
---@type string|number|nil | |
name, f_name, name_comma, s_param_2 = text:match("^([^%s,%)]*)()%s*(,?)()", s_name) | |
end | |
-- name cannot be nil anymore, it might just be empty if it's at the end of the file | |
chain_diff[2] = {i = s_name} | |
chain_diff[3] = {i = f_name} | |
modify_chain_diff_to_use_source_to_index_into_table(chain_diff, 2, name) | |
if name_comma ~= "" then | |
---@type string|number|nil | |
local s_func, func, f_func, func_comma, p_finish = text:match("^%s*()([\"'][^\"']*[\"'])()%s*(,?)()", s_param_2) | |
if not func then | |
---@type string|number|nil | |
s_func, func, f_func, func_comma, p_finish = text:match("^%s*()([^%s,%)]*)()%s*(,?)()", s_param_2) | |
end | |
-- func cannot be nil anymore, it might just be empty if it's at the end of the file | |
chain_diff[4] = {i = s_func} | |
local finish_chain_diff_elem = {i = f_func} | |
chain_diff[5] = finish_chain_diff_elem | |
modify_chain_diff_to_use_source_to_index_into_table(chain_diff, 4, func) | |
chain_diff[6] = {i = p_finish} | |
if func_comma == "" then | |
if text:match("^%s*%)", p_finish) then | |
extend_chain_diff_elem_text(finish_chain_diff_elem, "(") | |
else | |
extend_chain_diff_elem_text(finish_chain_diff_elem, "(0 ") -- missing closing parenthesis | |
end | |
else | |
if text:match("^%s*%)", p_finish) then | |
extend_chain_diff_elem_text(finish_chain_diff_elem, "(,") -- unexpected symbol near ',' | |
else | |
extend_chain_diff_elem_text(finish_chain_diff_elem, "(") | |
end | |
end | |
end | |
add_chain_diff(chain_diff, diffs) | |
end | |
end | |
--[[ ---@typelist ]] | |
---count how often the pattern matches in the given string | |
---@param s string | |
---@param pattern string | |
---@return integer | |
local function match_count(s, pattern) | |
local c = 0 | |
for _ in s:gmatch(pattern) do | |
c = c + 1 | |
end | |
return c | |
end | |
---@param uri string @ The uri of file | |
---@param text string @ The content of file | |
---@param diffs diff[] @ The diffs to add more diffs to | |
function type_list(uri, text, diffs) | |
---@type string|number | |
for s_typelist_str, typelist_str, s_next_line, next_line | |
in | |
text:gmatch("()---@typelist([^\n]*)\n()([^\n]*)") | |
do | |
---@type string[] | |
local types = {} | |
do | |
local open_count = 0 | |
---@type string | |
local current_type = nil | |
for part in typelist_str:gmatch("[^,]*") do | |
if current_type then | |
current_type = current_type .. "," .. part | |
else | |
current_type = part | |
end | |
open_count = open_count + match_count(part, "[%(<]") | |
open_count = open_count - match_count(part, "[%)>]") | |
if open_count == 0 then | |
---@type string | |
types[#types+1] = current_type | |
current_type = nil | |
elseif open_count < 0 then | |
goto continue | |
end | |
end | |
if current_type then | |
types[#types+1] = current_type | |
end | |
end | |
add_diff(diffs, s_typelist_str, s_next_line, "--") -- to prevent the wanring of a line with only spaces | |
local i = 0 | |
---@type number | |
for s_list_item in next_line:gmatch("()[^,]*") do | |
i = i + 1 | |
local current_type = types[i] | |
if not current_type then break end | |
local insert_position = s_next_line + s_list_item - 1 | |
add_diff(diffs, insert_position, insert_position, "\n---@type " .. current_type .. "\n") | |
end | |
::continue:: | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment