diff --git a/lua/compiler-explorer/async.lua b/lua/compiler-explorer/async.lua deleted file mode 100644 index 945f806..0000000 --- a/lua/compiler-explorer/async.lua +++ /dev/null @@ -1,48 +0,0 @@ -local M = {} - -local main_co_or_nil = coroutine.running() - -function M.wrap(func, argc) - assert(argc) - return function(...) - if coroutine.running() == main_co_or_nil then return func(...) end - return coroutine.yield(func, argc, ...) - end -end - -function M.void(func) - return function(...) - if coroutine.running() ~= main_co_or_nil then return func(...) end - - local co = coroutine.create(func) - - local function step(...) - local ret = { coroutine.resume(co, ...) } - local stat, err_or_fn, nargs = unpack(ret) - - if not stat then - error( - string.format( - "The coroutine failed with this message: %s\n%s", - err_or_fn, - debug.traceback(co) - ) - ) - end - - if coroutine.status(co) == "dead" then return end - - assert(vim.is_callable(err_or_fn), "type error :: expected func") - - local args = { select(4, unpack(ret)) } - args[nargs] = step - err_or_fn(unpack(args, 1, nargs)) - end - - step(...) - end -end - -M.scheduler = M.wrap(vim.schedule, 1) - -return M diff --git a/lua/compiler-explorer/health.lua b/lua/compiler-explorer/health.lua index e0cd287..1ad7841 100644 --- a/lua/compiler-explorer/health.lua +++ b/lua/compiler-explorer/health.lua @@ -2,15 +2,17 @@ local ce = require("compiler-explorer.lazy") local fn = vim.fn local health = vim.health +local async = vim.async local M = {} local has_nvim_version, has_curl, is_reachable -local run_checks = ce.async.void(function() +local run_checks = async.run(function() has_nvim_version = fn.has("nvim-0.10") > 0 has_curl = fn.executable("curl") > 0 + -- TODO: proper cancellation if not has_curl then is_reachable = false return @@ -18,12 +20,11 @@ local run_checks = ce.async.void(function() -- Ensure the next call is not cached. ce.cache.delete() - is_reachable = pcall(ce.rest.languages_get) + is_reachable = async.pawait(ce.rest.languages_get) end) M.check = function() - run_checks() - vim.wait(2000, function() return is_reachable ~= nil end) + vim.await(run_checks) health.start("compiler-explorer.nvim report") diff --git a/lua/compiler-explorer/http.lua b/lua/compiler-explorer/http.lua index 85252e8..c94aa3e 100644 --- a/lua/compiler-explorer/http.lua +++ b/lua/compiler-explorer/http.lua @@ -1,14 +1,20 @@ local ce = require("compiler-explorer.lazy") local json = vim.json +local async = vim.async local M = {} -M.get = ce.async.void(function(url) +local scheduler = async.wrap(1, vim.schedule) +local system = async.wrap(2, vim.system) +local request = async.wrap(4, vim.net.request) + +M.get = function(url) local data = ce.cache.get()[url] if data ~= nil then return 200, data end local args = { + "curl", "-X", "GET", "-H", @@ -18,11 +24,9 @@ M.get = ce.async.void(function(url) url, } - local ok, ret = pcall(ce.job.curl, args) - if not ok then error("curl executable not found") end + local ret = system(args, { text = true }) - ce.async.scheduler() - if ret.exit ~= 0 then + if ret.code ~= 0 then error( ("curl error:\ncommand: %s\nexit_code: %d\nstderr: %s"):format( ret.cmd, @@ -41,10 +45,11 @@ M.get = ce.async.void(function(url) local resp, status = json.decode(split[1]), tonumber(split[2]) if status == 200 then ce.cache.get()[url] = resp end return status, resp -end) +end -M.post = ce.async.void(function(url, body) +M.post = function(url, body) local args = { + "curl", "-s", "-X", "POST", @@ -58,11 +63,10 @@ M.post = ce.async.void(function(url, body) [[\n%{http_code}\n]], url, } - local ok, ret = pcall(ce.job.curl, args) - if not ok then error("curl executable not found") end - ce.async.scheduler() - if ret.exit ~= 0 then + local ret = system(args, { text = true }) + + if ret.code ~= 0 then error( ("curl error:\n command: %s \n exit_code %d\n stderr: %s"):format( ret.cmd, @@ -80,6 +84,6 @@ M.post = ce.async.void(function(url, body) end local resp, status = json.decode(split[1]), tonumber(split[2]) return status, resp -end) +end return M diff --git a/lua/compiler-explorer/init.lua b/lua/compiler-explorer/init.lua index 1a764ad..917eacb 100644 --- a/lua/compiler-explorer/init.lua +++ b/lua/compiler-explorer/init.lua @@ -1,19 +1,19 @@ local ce = require("compiler-explorer.lazy") local api, fn = vim.api, vim.fn +local async = vim.async local M = {} -- Return a function to avoid caching the vim.ui functions -local get_select = function() return ce.async.wrap(vim.ui.select, 3) end -local get_input = function() return ce.async.wrap(vim.ui.input, 2) end +local ui_select = async.wrap(3, vim.ui.select) +local ui_input = async.wrap(2, vim.ui.input) +local scheduler = async.wrap(1, vim.schedule) M.setup = function(user_config) ce.config.setup(user_config or {}) end -M.compile = ce.async.void(function(opts, live) +local compile = function(opts, live) local conf = ce.config.get_config() - local vim_select = get_select() - local vim_input = get_input() local args = ce.util.parse_args(opts.fargs) @@ -37,6 +37,7 @@ M.compile = ce.async.void(function(opts, live) local lang if not compiler then local lang_list = ce.rest.languages_get() + scheduler() local possible_langs = lang_list -- Infer language based on extension and prompt user. @@ -61,7 +62,7 @@ M.compile = ce.async.void(function(opts, live) lang = possible_langs[1] else -- Choose language - lang = vim_select(possible_langs, { + lang = ui_select(possible_langs, { prompt = "Select language> ", format_item = function(item) return item.name end, }) @@ -86,7 +87,7 @@ M.compile = ce.async.void(function(opts, live) else -- Choose compiler local compilers = ce.rest.compilers_get(lang.id) - compiler = vim_select(compilers, { + compiler = ui_select(compilers, { prompt = "Select compiler> ", format_item = function(item) return item.name end, }) @@ -95,8 +96,10 @@ M.compile = ce.async.void(function(opts, live) vim.cmd("redraw") end + scheduler() + -- Choose compiler options - args.flags = vim_input({ + args.flags = ui_input({ prompt = "Select compiler options> ", default = conf.compiler_flags, }) @@ -104,7 +107,7 @@ M.compile = ce.async.void(function(opts, live) args.compiler = compiler end - ce.async.scheduler() + scheduler() args.lang = compiler.lang @@ -126,7 +129,10 @@ M.compile = ce.async.void(function(opts, live) end -- Compile + local body = ce.rest.create_compile_body(args) + + ce.util.start_spinner("Compiling") local response ok, response = pcall(ce.rest.compile_post, compiler.id, body) @@ -137,8 +143,11 @@ M.compile = ce.async.void(function(opts, live) response.asm ) + scheduler() + ce.util.stop_spinner() local asm_bufnr = ce.util.create_window_buffer(source_bufnr, compiler.id, opts.bang) + api.nvim_buf_clear_namespace(asm_bufnr, -1, 0, -1) api.nvim_set_option_value("modifiable", true, { buf = asm_bufnr }) @@ -180,7 +189,12 @@ M.compile = ce.async.void(function(opts, live) {} ) api.nvim_buf_create_user_command(asm_bufnr, "CEGotoLabel", M.goto_label, {}) -end) +end + +M.compile = function(opts, live) + async.run(compile, opts, live) +end + M.open_website = function() local cmd @@ -209,10 +223,10 @@ M.open_website = function() vim.cmd(table.concat({ "silent", cmd, url }, " ")) end -M.add_library = ce.async.void(function() - local vim_select = get_select() +local add_library = function() local lang_list = ce.rest.languages_get() + scheduler() -- Infer language based on extension and prompt user. local extension = "." .. fn.expand("%:e") @@ -234,7 +248,7 @@ M.add_library = ce.async.void(function() lang = possible_langs[1] else -- Choose language - lang = vim_select(possible_langs, { + lang = ui_select(possible_langs, { prompt = "Select language> ", format_item = function(item) return item.name end, }) @@ -249,8 +263,9 @@ M.add_library = ce.async.void(function() return end + scheduler() -- Choose library - local lib = vim_select(libs, { + local lib = ui_select(libs, { prompt = "Select library> ", format_item = function(item) return item.name end, }) @@ -259,7 +274,7 @@ M.add_library = ce.async.void(function() vim.cmd("redraw") -- Choose version - local version = vim_select(lib.versions, { + local version = ui_select(lib.versions, { prompt = "Select library version> ", format_item = function(item) return item.version end, }) @@ -275,17 +290,20 @@ M.add_library = ce.async.void(function() ) ce.alert.info("Added library %s version %s", lib.name, version.version) -end) +end -M.format = ce.async.void(function() - local vim_select = get_select() +M.add_library = function() + async.run(add_library) +end + +local format = function() -- Get contents of current buffer local buf_contents = api.nvim_buf_get_lines(0, 0, -1, false) local source = table.concat(buf_contents, "\n") -- Select formatter local formatters = ce.rest.formatters_get() - local formatter = vim_select(formatters, { + local formatter = ui_select(formatters, { prompt = "Select formatter> ", format_item = function(item) return item.name end, }) @@ -294,7 +312,7 @@ M.format = ce.async.void(function() local style = formatter.styles[1] or "__DefaultStyle" if #formatter.styles > 0 then - style = vim_select(formatter.styles, { + style = ui_select(formatter.styles, { prompt = "Select formatter style> ", format_item = function(item) return item end, }) @@ -306,6 +324,8 @@ M.format = ce.async.void(function() local body = ce.rest.create_format_body(source, style) local out = ce.rest.format_post(formatter.type, body) + scheduler() + if out.exit ~= 0 then ce.alert.error("Could not format code with %s", formatter.name) return @@ -318,11 +338,17 @@ M.format = ce.async.void(function() api.nvim_buf_set_lines(0, 0, -1, false, lines) ce.alert.info("Text formatted using %s and style %s", formatter.name, style) -end) +end + +M.format = function() + async.run(format) +end -M.show_tooltip = ce.async.void(function() +local show_tooltip = function() local ok, response = pcall(ce.rest.tooltip_get, vim.b.arch, fn.expand("")) + + scheduler() if not ok then ce.alert.error(response) return @@ -333,7 +359,11 @@ M.show_tooltip = ce.async.void(function() close_events = { "CursorMoved" }, border = "single", }) -end) +end + +M.show_tooltip = function() + async.run(show_tooltip) +end M.goto_label = function() local word_under_cursor = fn.expand("") @@ -352,9 +382,9 @@ M.goto_label = function() api.nvim_win_set_cursor(0, { label, 0 }) end -M.load_example = ce.async.void(function() - local vim_select = get_select() +local load_example = function() local examples = ce.rest.list_examples_get() + scheduler() local examples_by_lang = {} for _, example in ipairs(examples) do @@ -368,7 +398,7 @@ M.load_example = ce.async.void(function() local langs = vim.tbl_keys(examples_by_lang) table.sort(langs) - local lang_id = vim_select(langs, { + local lang_id = ui_select(langs, { prompt = "Select language> ", format_item = function(item) return item end, }) @@ -376,11 +406,13 @@ M.load_example = ce.async.void(function() if not lang_id then return end vim.cmd("redraw") - local example = vim_select(examples_by_lang[lang_id], { + local example = ui_select(examples_by_lang[lang_id], { prompt = "Select example> ", format_item = function(item) return item.name end, }) local response = ce.rest.load_example_get(lang_id, example.file) + scheduler() + local lines = vim.split(response.file, "\n") langs = ce.rest.languages_get() @@ -402,6 +434,10 @@ M.load_example = ce.async.void(function() else vim.filetype.match(bufname, 0) end -end) +end + +M.load_example = function() + async.run(load_example) +end return M diff --git a/lua/compiler-explorer/job.lua b/lua/compiler-explorer/job.lua deleted file mode 100644 index 1c74039..0000000 --- a/lua/compiler-explorer/job.lua +++ /dev/null @@ -1,75 +0,0 @@ -local ce = require("compiler-explorer.lazy") - -local uv = vim.loop - -local M = {} - -local function close_pipes(...) - for _, pipe in ipairs({ ... }) do - if not pipe:is_closing() then pipe:close() end - end -end - -local function close_timer(timer) - timer:stop() - if not timer:is_closing() then timer:close() end -end - -local function read_stop_pipes(...) - for _, pipe in ipairs({ ... }) do - pipe:read_stop() - end -end - -local spawn = function(cmd, args, cb) - local conf = ce.config.get_config() - local stdout = uv.new_pipe() - local stderr = uv.new_pipe() - - local stdout_data, stderr_data = {}, {} - local full_cmd = table.concat({ "curl", unpack(args) }, " ") - - local handle, timer - handle = uv.spawn(cmd, { - args = args, - stdio = { nil, stdout, stderr }, - }, function(code, signal) - close_timer(timer) - handle:close() - - read_stop_pipes(stdout, stderr) - close_pipes(stdout, stderr) - - local stdout_result = table.concat(stdout_data) - local stderr_result = table.concat(stderr_data) - - cb({ - cmd = full_cmd, - exit = code, - signal = signal, - stdout = stdout_result, - stderr = stderr_result, - }) - end) - - if not handle then - close_pipes(stdout, stderr) - error(("Failed to start the process: %s"):format(full_cmd)) - end - - timer = uv.new_timer() - timer:start(conf.job_timeout_ms, 0, function() handle:kill("sigkill") end) - - stdout:read_start(function(_, data) table.insert(stdout_data, data) end) - stderr:read_start(function(_, data) table.insert(stderr_data, data) end) -end - -local start = ce.async.wrap(spawn, 3) - -setmetatable(M, { - __index = function(_, key) - return ce.async.void(function(args) return start(key, args) end) - end, -}) - -return M diff --git a/lua/compiler-explorer/rest.lua b/lua/compiler-explorer/rest.lua index b463f8c..72bc981 100644 --- a/lua/compiler-explorer/rest.lua +++ b/lua/compiler-explorer/rest.lua @@ -11,11 +11,7 @@ local get = function(url) end local post = function(url, req_body, spinner_text) - ce.util.start_spinner(spinner_text) - local ok, status, body = pcall(ce.http.post, url, req_body) - ce.util.stop_spinner() - - if not ok then error(status) end + local status, body = ce.http.post(url, req_body) if status ~= 200 then error(("POST %s returned %d. %s"):format(url, status, body.error), 0)