123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319 |
- local uv = require('luv')
- local json = require('dkjson')
- -- arg[1], project path
- -- arg[2], language id
- local pathToLspFiles = "/tmp/"
- local projectRoot = arg[1]
- local langId = arg[2]
- local function pickLsp(lid)
- switch = {
- ["c"] = function()
- return "ccls", {'-log-file=/tmp/ccls2.log', '-init={}'}
- end,
- ["h"] = function()
- return "ccls", {'-log-file=/tmp/ccls2.log', '-init={}'}
- end,
- ["hs"] = function()
- return "haskell-language-server-9.6.5", {"--lsp"}
- end,
- ["kt"] = function()
- return "kotlin-language-server", {}
- end,
- ["default"] = function()
- return nil, nil
- end
- }
- local v, a = (switch[lid] or switch["default"])()
- return v, a
- end
- local lspCmd, lspArgs = pickLsp(langId)
- if lspCmd == nil then
- os.exit(1)
- end
- local function watch_file(filename, callback)
- local last_modified = uv.fs_stat(filename).mtime.sec
- local timer = uv.new_timer()
- timer:start(1000, 1000, function()
- local current_modified = uv.fs_stat(filename).mtime.sec
- if current_modified ~= last_modified then
- callback()
- last_modified = current_modified
- end
- end)
- return timer
- end
- local function run_lsp_client()
- local stdin_pipe = uv.new_pipe()
- local stdout_pipe = uv.new_pipe()
- local stderr_pipe = uv.new_pipe()
- local stopper = uv.new_timer()
- local file = io.open(pathToLspFiles .. ".ait_lsp_log", "w")
- file:write("")
- file:close();
- local handle, pid = uv.spawn(lspCmd, {
- args = lspArgs,
- stdio = {stdin_pipe, stdout_pipe, stderr_pipe}
- }, function(code, signal)
- print("Process exited with code " .. code .. " and signal " .. signal)
- stdin_pipe:close()
- stdout_pipe:close()
- stderr_pipe:close()
- end)
- if not handle then
- print("Failed to spawn LSP process")
- return
- end
- stderr_pipe:read_start(function(err, data)
- if err then
- print("stderr error:", err)
- return
- end
- if data then
- print("stderr:", data)
- end
- end)
- local function send_request(request)
- local payload = json.encode(request)
- local request_str = string.format("Content-Length: %d\r\n\r\n%s", #payload, payload)
- stdin_pipe:write(request_str)
- end
- local function lsp_cmd(vars)
- local filePath
- local line
- local col
- local command
- if #vars > 3 then
- command = vars[1]
- filePath = vars[2]
- line = tonumber(vars[3]) - 1
- col = tonumber(vars[4]) - 1
- else
- command = "c"
- filePath = vars[1]
- line = tonumber(vars[2]) - 1
- col = tonumber(vars[3]) - 1
- end
- local fp = io.open(filePath)
- local contents = fp:read("*all");
- fp:close()
- local didOpen_request = {
- jsonrpc = "2.0",
- method = "textDocument/didOpen",
- params = {
- textDocument = {
- uri = "file://" .. filePath,
- languageId = langId,
- version = 1,
- text = contents
- }
- }
- }
- send_request(didOpen_request)
- local documentSymbol_request = {
- jsonrpc = "2.0",
- method = "textDocument/documentSymbol",
- id = 4,
- params = {
- textDocument = {
- uri = "file://" .. filePath,
- }
- }
- }
- send_request(documentSymbol_request)
- local request
- if command == "c" then
- request = {
- jsonrpc = "2.0",
- id = 5,
- method = "textDocument/completion",
- params = {
- textDocument = {
- uri = "file://" .. filePath
- },
- position = {
- line = line,
- character = col
- }
- }
- }
- elseif command == "h" then
- request = {
- jsonrpc = "2.0",
- id = 5,
- method = "textDocument/hover",
- params = {
- textDocument = {
- uri = "file://" .. filePath
- },
- position = {
- line = line,
- character = col
- }
- }
- }
- elseif command == "a" then
- local eline = tonumber(vars[4]) - 1
- local ecol = tonumber(vars[5]) - 1
- request = {
- jsonrpc = "2.0",
- id = 5,
- method = "textDocument/codeAction",
- params = {
- textDocument = {
- uri = "file://" .. filePath
- },
- range = {
- start = {
- line = line,
- character = col
- },
- ["end"] = {
- line = eline,
- character = ecol
- }
- },
- context = {
- diagnostics = {
- range = {
- start = {
- line = line, character = col
- },
- ["end"] = {
- line = eline, character = ecol
- }
- },
- severity = 1,
- },
- only = { "quickfix", "refactor" },
- triggerKind = 1
- }
- }
- }
- end
- send_request(request)
- end
- local buffer = ""
- stdout_pipe:read_start(function(err, data)
- file = io.open(pathToLspFiles .. ".ait_lsp_log", "a")
- file:write(data .. "\n")
- print(data)
- if err then
- print("stdout error:", err)
- return
- end
- local count = tonumber(data:match("Content%-Length: (%d+)"))
- local js = nil
- local start_index = data:find("{")
- if start_index and count ~= nil then
- js = data:sub(start_index)
- if js:find("result") then
- buffer = js
- -- else
- -- buffer = ""
- end
- end
- if buffer:match("result") then
- if js == nil then
- buffer = buffer .. data
- end
- local parsed = json.decode(buffer)
- if parsed then
- if parsed.result and parsed.result.items then
- local opts = io.open(pathToLspFiles .. ".ait_lsp_options", "w")
- for _, item in ipairs(parsed.result.items) do
- local str = item.label .. "\t" .. json.encode(item) .. "\n"
- opts:write(str)
- end
- opts:close()
- end
- buffer = ""
- end
- end
- file:close()
- end)
- local timer = uv.new_timer()
- timer:start(10000, 0, function()
- local initialize_request = {
- jsonrpc = "2.0",
- id = 1,
- method = "initialize",
- params = {
- processId = pid,
- rootPath = projectRoot,
- rootUri = "file://" .. projectRoot,
- capabilities = {
- textDocument = {
- completion = {
- completionItem = {
- snippetSupport = true
- }
- }
- },
- hover = {},
- codeAction = {
- dynamicRegistration = true,
- codeActionKinds = {
- "quickfix",
- "refactor",
- "refactor.extract",
- "refactor.inline",
- "refactor.rewrite",
- "source",
- "source.organizeImports"
- }
- }
- }
- }
- }
- send_request(initialize_request)
- local initialized_notification = {
- jsonrpc = "2.0",
- method = "initialized",
- params = {}
- }
- send_request(initialized_notification)
- timer:stop()
- end)
- local timer = watch_file(pathToLspFiles .. ".ait_lsp_cmd", function()
- local f = io.open(pathToLspFiles .. ".ait_lsp_cmd", "r")
- local cmd = f:read("*a")
- f:close()
- local vars = {}
- for word in string.gmatch(cmd, "%S+") do
- table.insert(vars, word)
- end
- lsp_cmd(vars)
- end)
- uv.run()
- end
- run_lsp_client()
|