diff --git a/README.md b/README.md index 2b3fc33..94a6d14 100644 --- a/README.md +++ b/README.md @@ -116,11 +116,14 @@ visual mode, and it will take a screenshot of the selected lines. ### Keymaps ```lua -vim.keymap.set("n", "fz", require("freeze-code").freeze) +local fz_api = require("freeze-code.utils.api") +vim.keymap.set("n", "fz", fz_api.freeze) vim.keymap.set("v", "fz", function() - require("freeze-code").freeze(vim.fn.line("'<"), vim.fn.line("'>")) + fz_api.freeze(vim.fn.line("'<"), vim.fn.line("'>")) end) -- or using `Freeze` +vim.keymap.set("n", "fl", fz.freeze_line) +-- or using `FreezeLine` ``` ## Contributing diff --git a/doc/freeze-code.nvim.txt b/doc/freeze-code.nvim.txt index 8383fc3..e9468d2 100644 --- a/doc/freeze-code.nvim.txt +++ b/doc/freeze-code.nvim.txt @@ -1,9 +1,10 @@ -*freeze-code.nvim.txt* For Neovim >= 0.9.0 Last change: 2024 May 19 +*freeze-code.nvim.txt* For Neovim >= 0.9.0 Last change: 2024 June 04 ============================================================================== Table of Contents *freeze-code.nvim-table-of-contents* - Installation |freeze-code.nvim-installation| + - Usage |freeze-code.nvim-usage| - Contributing |freeze-code.nvim-contributing| @@ -89,6 +90,29 @@ tools. +USAGE *freeze-code.nvim-usage* + +To use this plugin, simply call `:Freeze` and it will take a screenshot of the +current buffer and save it in the `dir` path you have configured. + +If you want to take a screenshot of a specific line, you can use the `:Freeze` +in visual mode, and it will take a screenshot of the selected lines. + + +KEYMAPS ~ + +>lua + local fz_api = require("freeze-code.utils.api") + vim.keymap.set("n", "fz", fz_api.freeze) + vim.keymap.set("v", "fz", function() + fz_api.freeze(vim.fn.line("'<"), vim.fn.line("'>")) + end) + -- or using `Freeze` + vim.keymap.set("n", "fl", fz.freeze_line) + -- or using `FreezeLine` +< + + CONTRIBUTING *freeze-code.nvim-contributing* Thank you to everyone that is contributing and to those who want to contribute. diff --git a/lua/freeze-code/binary/binary_fetcher.lua b/lua/freeze-code/binary/binary_fetcher.lua new file mode 100644 index 0000000..606c953 --- /dev/null +++ b/lua/freeze-code/binary/binary_fetcher.lua @@ -0,0 +1,147 @@ +local cfg = require("freeze-code.config") +local u = require("freeze-code.utils") +local logger = u.logger + +local FreezeBinaryFetcher = {} + +---@class FreezeBinaryFetcher +---@field go_installation function: installs `freeze` using `go install github.com/charmbracelet/freeze@latest` +---Freeze installation using `go install` +---@param opts FreezeCodeConfig +function FreezeBinaryFetcher:go_installation(opts) + local cmd_args = { + "go", + "install", + "github.com/charmbracelet/freeze@latest", + } + local stdio = { stdout = "", stderr = "" } + local job = nil + + local function on_output(err, data) + if err then + logger.err(err) + end + if data then + stdio.stderr = stdio.stderr .. data + end + end + local callbacks = { + on_sterr = vim.schedule_wrap(function(_, data, _) + local out = table.concat(data, "\n") + on_output(out) + end), + on_exit = vim.schedule_wrap(function() + opts._installed = true + opts.freeze_path = vim.env.HOME .. "/go/bin/freeze" + opts.install_path = opts.freeze_path + logger.warn("[freeze-code] go install github.com/charmbracelet/freeze@latest completed") + cfg.setup(opts) + vim.fn.jobstop(job) + end), + } + job = vim.fn.jobstart(cmd_args, callbacks) +end + +---@class FreezeBinaryFetcher +---@field agnostic_installation function: installs `freeze` using `cURL` from GitHub release +---Freeze installation using GitHub release +---@param opts FreezeCodeConfig +function FreezeBinaryFetcher:agnostic_installation(opts) + local os_name = u.get_os_info() + local release_url = u.release_file_url() + if release_url == "" then + logger.err("could not get release file") + return + end + + local install_path = vim.fn.expand(opts.install_path) + if install_path == "" then + install_path = vim.fn.expand("~/.local/bin") + end + local output_filename = "freeze.tar.gz" + local download_command = { "curl", "-sL", "-o", output_filename, release_url } + local extract_command = { "tar", "-zxf", output_filename, "-C", install_path } + -- vim.loop.spawn("rm", { args = { "-rf", install_path .. "/" .. u.get_freeze_filename() } }) + local rm_command_args = { "-rf", install_path .. "/" .. u.get_freeze_filename() } + if os_name == "Windows" then + extract_command = { "Expand-Archive", output_filename, install_path } + rm_command_args = { "-r", "-Force", install_path .. "/" .. u.get_freeze_filename() } + end + local binary_path = vim.fn.expand(table.concat({ install_path, u.get_freeze_filename() .. "/freeze" }, "/")) + + -- check for existing files / folders + if vim.fn.isdirectory(install_path) == 0 then + vim.loop.fs_mkdir(install_path, tonumber("777", 8)) + end + + if vim.fn.filereadable(binary_path) == 1 then + local success = vim.loop.fs_unlink(binary_path) + if not success then + logger.err("[freeze-code.nvim] ERROR: `freeze` binary could not be removed!") + return + end + end + local stdio = { stdout = "", stderr = "" } + local job = nil + + local function on_output(err, data) + if err then + logger.err(err) + end + if data then + stdio.stderr = stdio.stderr .. data + end + end + + -- download and install the freeze binary + local callbacks = { + on_sterr = vim.schedule_wrap(function(_, data, _) + local out = table.concat(data, "\n") + on_output(out) + end), + on_exit = vim.schedule_wrap(function() + logger.info_fmt("[freeze-code.nvim] extracting release with `%s`", table.concat(extract_command, " ")) + vim.fn.system(extract_command) + if vim.v.shell_error ~= 0 then + logger.err("[freeze-code.nvim] ERROR: extracting release failed") + return + end + -- remove the archive after completion + if vim.fn.filereadable(output_filename) == 1 then + local success = vim.loop.fs_unlink(output_filename) + if not success then + logger.err("[freeze-code.nvim] ERROR: existing archive could not be removed") + return + end + end + vim.loop.spawn("mv", { args = { binary_path, install_path .. "/freeze" } }) + binary_path = install_path .. "/freeze" + opts.freeze_path = binary_path + opts._installed = true + opts.install_path = install_path + logger.warn_fmt("[freeze-code.nvim] `freeze` binary installed in installed in path=%s", cfg.config.freeze_path) + vim.loop.spawn("rm", { args = rm_command_args }) + logger.warn_fmt("[freeze-code.nvim] `freeze` binary installed in path=%s", cfg.config.freeze_path) + cfg.setup(opts) + vim.fn.jobstop(job) + end), + } + logger.info_fmt("[freeze-code.nvim] downloading release from `%s`", release_url) + job = vim.fn.jobstart(download_command, callbacks) +end + +---@class FreezeBinaryFetcher +---@field install_freeze function: `freeze` installation process +---Install freeze for the user +---@param opts FreezeCodeConfig +function FreezeBinaryFetcher:install_freeze(opts) + if u.check_executable("go", vim.fn.exepath("go")) then + logger.warn("[freeze-code.nvim] go install github.com/charmbracelet/freeze@latest completed") + self:go_installation(opts) + return + end + logger.info("[freeze-code.nvim] Installing info with `curl`") + self:agnostic_installation(opts) +end + +return FreezeBinaryFetcher diff --git a/lua/freeze-code/binary/binary_handler.lua b/lua/freeze-code/binary/binary_handler.lua new file mode 100644 index 0000000..d4619cb --- /dev/null +++ b/lua/freeze-code/binary/binary_handler.lua @@ -0,0 +1,92 @@ +local binary_fetcher = require("freeze-code.binary.binary_fetcher") +local cfg = require("freeze-code.config") +local commands = require("freeze-code.commands") +local u = require("freeze-code.utils") +local logger = u.logger +local FreezeBinary = {} + +---@class FreezeBinary +---@field freeze function: Running `freeze` function +---Freeze file with a range or current buffer if no range is given +---@param s_line? number: line to start range +---@param e_line? number: line to start range +function FreezeBinary:freeze(s_line, e_line) + if not cfg.config._installed then + logger.warn("[freeze-code.nvim] `freeze` not installed") + binary_fetcher:install_freeze(cfg.config) + return + end + + s_line = s_line or 1 + e_line = e_line or vim.api.nvim_buf_line_count(vim.api.nvim_get_current_buf()) + + local cmd = cfg.config.freeze_path + + if not u.check_executable("freeze", cmd) then + return + end + + local lang = "" + if vim.fn.has("nvim-0.10") == 1 then + lang = vim.api.nvim_get_option_value("filetype", { buf = vim.api.nvim_get_current_buf() }) + else + ---@diagnostic disable-next-line: deprecated + lang = vim.api.nvim_buf_get_option(vim.api.nvim_get_current_buf(), "filetype") + end + local file = vim.api.nvim_buf_get_name(vim.api.nvim_get_current_buf()) + local conf = cfg.config.freeze_config.config + local dir = cfg.config.dir + local theme = cfg.config.freeze_config.theme + local output = cfg.config.freeze_config.output + + if output ~= "freeze" then + local t_stamp = os.date("%Y%m%d%H%M%S") + local filename = file:match("^.+/(.*)$") or file + + output = string.format("%s_%s_%s", tostring(t_stamp), filename, output) + end + + cfg.config.output = dir .. "/" .. output .. ".png" + + local cmd_args = { + "--output", + cfg.config.output, + "--language", + lang, + "--lines", + s_line .. "," .. e_line, + "--config", + conf, + file, + } + if conf == "base" or conf == "full" then + vim.list_extend(cmd_args, { + "--theme", + theme, + }) + end + + commands.job = {} + commands.job.stdout = vim.loop.new_pipe(false) + commands.job.stderr = vim.loop.new_pipe(false) + + local job_opts = { + args = cmd_args, + stdio = { nil, commands.job.stdout, commands.job.stderr }, + } + + local msg = "🍧 frozen frame in path=" .. cfg.config.output + commands.job.handle = vim.loop.spawn(cmd, job_opts, commands.on_exit(msg)) + vim.loop.read_start(commands.job.stdout, vim.schedule_wrap(commands.on_output)) + vim.loop.read_start(commands.job.stderr, vim.schedule_wrap(commands.on_output)) +end + +---@class FreezeBinary +---@field freeze_line function: Running `freeze` function for current line +---Freeze file with a range or current buffer if no range is given +function FreezeBinary:freeze_line() + local curent_line = vim.api.nvim_win_get_cursor(0)[1] + self:freeze(curent_line, curent_line) +end + +return FreezeBinary diff --git a/lua/freeze-code/commands.lua b/lua/freeze-code/commands.lua index f897fb6..a3730da 100644 --- a/lua/freeze-code/commands.lua +++ b/lua/freeze-code/commands.lua @@ -41,18 +41,18 @@ end ---@param msg string: Message to display if success ---@return function cb: Schedule wrap callback function function M.on_exit(msg, opts) - local freeze_code = require("freeze-code") + local cfg = require("freeze-code.config") return vim.schedule_wrap(function(code, _) if code == 0 then vim.notify("[freeze-code.nvim] " .. msg, vim.log.levels.INFO, { title = "FreezeCode" }) else vim.notify(M.stdio.stdout, vim.log.levels.ERROR, { title = "Freeze" }) end - if freeze_code.config.copy == true then - freeze_code.copy(freeze_code.config) + if cfg.config.copy == true then + M.copy(cfg.config) end - if freeze_code.config.open == true then - freeze_code.open(freeze_code.config) + if cfg.config.open == true then + M.open(cfg.config) end if opts and opts.freeze then vim.wait(5000, function() diff --git a/lua/freeze-code/config.lua b/lua/freeze-code/config.lua new file mode 100644 index 0000000..90f6820 --- /dev/null +++ b/lua/freeze-code/config.lua @@ -0,0 +1,51 @@ +---@meta + +---@class FreezeConfig +---@field output string: Freeze output filename `--output "freeze.png"` +---@field theme string: Freeze theme `--theme "default"` +---@field config string: Freeze configuration `--config "base"` + +---@class FreezeCodeConfig +---@field freeze_path string: Path to `freeze` executable +---@field copy_cmd string: Path to copy `image/png` to clipboard command +---@field copy boolean: Open image after creation option +---@field open boolean: Open image after creation option +---@field dir string: Directory to create image +---@field freeze_config FreezeConfig +---@field output? string: output filename +---@field install_path string: path in where to install `freeze` +---@field _installed boolean: + +---@type FreezeCodeConfig +local default_config = { + freeze_config = { + output = "freeze", + config = "base", + theme = "default", + }, + _installed = vim.fn.exepath("freeze") ~= "", + install_path = vim.env.HOME .. "/.local/bin", + freeze_path = vim.fn.exepath("freeze"), + copy_cmd = vim.env.HOME .. "/dev/nvim_plugins/freeze-code.nvim/bin/pngcopy-macos", + copy = false, + open = false, + dir = vim.env.PWD, + output = nil, +} + +local M = { + config = vim.deepcopy(default_config), +} + +M.setup = function(opts) + M.config = vim.tbl_deep_extend("force", vim.deepcopy(default_config), opts or {}) +end + +return setmetatable(M, { + __index = function(_, key) + if key == "setup" then + return M.setup + end + return rawget(M.config, key) + end, +}) diff --git a/lua/freeze-code/init.lua b/lua/freeze-code/init.lua index f15f2aa..f30632f 100644 --- a/lua/freeze-code/init.lua +++ b/lua/freeze-code/init.lua @@ -1,370 +1,24 @@ ---@meta ----@class FreezeConfig ----@field output string: Freeze output filename `--output "freeze.png"` ----@field theme string: Freeze theme `--theme "default"` ----@field config string: Freeze configuration `--config "base"` ----@class FreezeCodeConfig ----@field freeze_path string: Path to `freeze` executable ----@field copy_cmd string: Path to copy `image/png` to clipboard command ----@field copy boolean: Open image after creation option ----@field open boolean: Open image after creation option ----@field dir string: Directory to create image ----@field freeze_config FreezeConfig ----@field output? string: output filename ----@field install_path string: path in where to install `freeze` ----@field _installed boolean: - ----@type FreezeConfig -local freeze_config = { - output = "freeze", - config = "base", - theme = "default", -} +local api = require("freeze-code.utils.api") +local cfg = require("freeze-code.config") ---@class FreezeCode ----@field config FreezeCodeConfig ----@type FreezeCodeConfig -local config = { - _installed = vim.fn.exepath("freeze") ~= "", - install_path = vim.env.HOME .. "/.local/bin", - freeze_path = vim.fn.exepath("freeze"), - copy_cmd = vim.env.HOME .. "/dev/nvim_plugins/freeze-code.nvim/bin/pngcopy-macos", - copy = false, - open = false, - dir = vim.env.PWD, - freeze_config = freeze_config, - output = nil, -} - ----@type FreezeCode|{} local freeze_code = {} -local freeze_version = "0.1.6" - -local logger = require("freeze-code.utils").logger - -local commands = require("freeze-code.commands") - -freeze_code.config = config - ----@class FreezeCode ----@field copy function: Copying image to clipboard -freeze_code.copy = function(opts) - commands.copy(opts) -end - ----@class FreezeCode ----@field open function: Opening image in default image viewer -freeze_code.open = function(opts) - commands.open(opts) -end - local create_autocmds = function() vim.api.nvim_create_user_command("Freeze", function(opts) if opts.count > 0 then - freeze_code.freeze(opts.line1, opts.line2) + api.freeze(opts.line1, opts.line2) else - freeze_code.freeze() + api.freeze() end end, { range = true, }) -end - -local get_os_info = function() - local os_name, arch - - local raw_os = vim.loop.os_uname().sysname - local raw_arch = jit.arch - local os_patterns = { - ["Windows"] = "Windows", - ["Windows_NT"] = "Windows", - ["Linux"] = "Linux", - ["Darwin"] = "Darwin", - ["BSD"] = "Freebsd", - } - - local arch_patterns = { - ["x86"] = "i386", - ["x64"] = "x86_64", - ["arm"] = "arm7", - ["arm64"] = "arm64", - } - - os_name = os_patterns[raw_os] - arch = arch_patterns[raw_arch] - - return os_name, arch -end - --- Get the filename for the release (e.g. freeze___) ----@return string -local function get_freeze_filename() - local os_name, arch = get_os_info() - - if os_name == nil or arch == nil then - vim.notify("os not supported or could not be parsed", vim.log.levels.ERROR, { title = "Freeze" }) - return "" - end - local filename = "freeze_" .. freeze_version .. "_" .. os_name .. "_" .. arch - return filename -end - ----Get the release archive file extension depending on OS ----@return string extension -local function get_archive_extension() - local os_name, _ = get_os_info() - - return (os_name == "Windows" and ".zip" or ".tar.gz") -end - ----Get the release file for the right OS and Architecture from official release ----page, https://github.com/charmbracelet/freeze/releases, for the specified version ----@return string release_url -local function release_file_url() - -- check pre-existence of required programs - if vim.fn.executable("curl") == 0 or vim.fn.executable("tar") == 0 then - vim.notify("curl and/or tar are required", vim.log.levels.ERROR, { title = "Freeze" }) - return "" - end - - local filename = get_freeze_filename() .. get_archive_extension() - - -- create the url, filename based on os and arch - return "https://github.com/charmbracelet/freeze/releases/download/v" .. freeze_version .. "/" .. filename -end - ----@class FreezeCode ----@field go_install_freeze function: installs `freeze` using `go install github.com/charmbracelet/freeze@latest` ----Freeze installation using `go install` ----@param opts FreezeCodeConfig -freeze_code.go_install_freeze = function(opts) - local cmd_args = { - "go", - "install", - "github.com/charmbracelet/freeze@latest", - } - local stdio = { stdout = "", stderr = "" } - local job = nil - - local function on_output(err, data) - if err then - logger.err(err) - end - if data then - stdio.stderr = stdio.stderr .. data - end - end - local callbacks = { - on_sterr = vim.schedule_wrap(function(_, data, _) - local out = table.concat(data, "\n") - on_output(out) - end), - on_exit = vim.schedule_wrap(function() - opts._installed = true - opts.freeze_path = vim.env.HOME .. "/go/bin/freeze" - opts.install_path = opts.freeze_path - freeze_code.setup(opts) - logger.warn("[freeze-code] go install github.com/charmbracelet/freeze@latest completed") - create_autocmds() - vim.fn.jobstop(job) - end), - } - job = vim.fn.jobstart(cmd_args, callbacks) -end - ----@class FreezeCode ----@field agnostic_install_freeze function: installs `freeze` using `cURL` from GitHub release ----Freeze installation using GitHub release ----@param opts FreezeCodeConfig -freeze_code.agnostic_install_freeze = function(opts) - local os_name = get_os_info() - local release_url = release_file_url() - if release_url == "" then - logger.err("could not get release file") - return - end - - local install_path = vim.fn.expand(opts.install_path) - if install_path == "" then - install_path = vim.fn.expand("~/.local/bin") - end - local output_filename = "freeze.tar.gz" - local download_command = { "curl", "-sL", "-o", output_filename, release_url } - local extract_command = { "tar", "-zxf", output_filename, "-C", install_path } - -- vim.loop.spawn("rm", { args = { "-rf", install_path .. "/" .. get_freeze_filename() } }) - local rm_command_args = { "-rf", install_path .. "/" .. get_freeze_filename() } - if os_name == "Windows" then - extract_command = { "Expand-Archive", output_filename, install_path } - rm_command_args = { "-r", "-Force", install_path .. "/" .. get_freeze_filename() } - end - local binary_path = vim.fn.expand(table.concat({ install_path, get_freeze_filename() .. "/freeze" }, "/")) - - -- check for existing files / folders - if vim.fn.isdirectory(install_path) == 0 then - vim.loop.fs_mkdir(install_path, tonumber("777", 8)) - end - - if vim.fn.filereadable(binary_path) == 1 then - local success = vim.loop.fs_unlink(binary_path) - if not success then - logger.err("[freeze-code.nvim] ERROR: `freeze` binary could not be removed!") - return - end - end - local stdio = { stdout = "", stderr = "" } - local job = nil - - local function on_output(err, data) - if err then - logger.err(err) - end - if data then - stdio.stderr = stdio.stderr .. data - end - end - - -- download and install the freeze binary - local callbacks = { - on_sterr = vim.schedule_wrap(function(_, data, _) - local out = table.concat(data, "\n") - on_output(out) - end), - on_exit = vim.schedule_wrap(function() - logger.info_fmt("[freeze-code.nvim] extracting release with `%s`", table.concat(extract_command, " ")) - vim.fn.system(extract_command) - if vim.v.shell_error ~= 0 then - logger.err("[freeze-code.nvim] ERROR: extracting release failed") - return - end - -- remove the archive after completion - if vim.fn.filereadable(output_filename) == 1 then - local success = vim.loop.fs_unlink(output_filename) - if not success then - logger.err("[freeze-code.nvim] ERROR: existing archive could not be removed") - return - end - end - vim.loop.spawn("mv", { args = { binary_path, install_path .. "/freeze" } }) - binary_path = install_path .. "/freeze" - opts.freeze_path = binary_path - opts._installed = true - opts.install_path = install_path - freeze_code.setup(opts) - vim.loop.spawn("rm", { args = rm_command_args }) - logger.warn_fmt( - "[freeze-code.nvim] `freeze` binary installed in installed in path=%s", - freeze_code.config.freeze_path - ) - freeze_code.setup(opts) - create_autocmds() - vim.fn.jobstop(job) - end), - } - logger.info_fmt("[freeze-code.nvim] downloading release from `%s`", release_url) - job = vim.fn.jobstart(download_command, callbacks) -end - ----@class FreezeCode ----@field install_freeze function: `freeze` installation process ----Install freeze for the user ----@param opts FreezeCodeConfig -freeze_code.install_freeze = function(opts) - if commands.check_executable("go", vim.fn.exepath("go")) then - logger.warn("[freeze-code.nvim] go install github.com/charmbracelet/freeze@latest completed") - freeze_code.go_install_freeze(opts) - return - end - logger.info("[freeze-code.nvim] Installing info with `curl`") - freeze_code.agnostic_install_freeze(opts) -end - ----@class FreezeCode ----@field freeze function: Running `freeze` function ----Freeze file with a range ----@param s_line? number: line to start range ----@param e_line? number: line to start range -freeze_code.freeze = function(s_line, e_line) - if not freeze_code.config._installed then - logger.warn("[freeze-code.nvim] `freeze` not installed") - freeze_code.install_freeze(freeze_code.config) - return - end - - s_line = s_line or 1 - e_line = e_line or vim.api.nvim_buf_line_count(0) - - local cmd = freeze_code.config.freeze_path - - if not commands.check_executable("freeze", cmd) then - return - end - - local lang = "" - if vim.fn.has("nvim-0.10") == 1 then - lang = vim.api.nvim_get_option_value("filetype", { buf = vim.api.nvim_get_current_buf() }) - else - ---@diagnostic disable-next-line: deprecated - lang = vim.api.nvim_buf_get_option(vim.api.nvim_get_current_buf(), "filetype") - end - local file = vim.api.nvim_buf_get_name(vim.api.nvim_get_current_buf()) - local conf = freeze_code.config.freeze_config.config - local dir = freeze_code.config.dir - local theme = freeze_code.config.freeze_config.theme - local output = freeze_code.config.freeze_config.output - - if output ~= "freeze" then - local t_stamp = os.date("%Y%m%d%H%M%S") - local filename = file:match("^.+/(.*)$") or file - - output = string.format("%s_%s_%s", tostring(t_stamp), filename, output) - end - - freeze_code.config.output = dir .. "/" .. output .. ".png" - - local cmd_args = {} - if conf == "base" or conf == "full" then - cmd_args = { - "--output", - freeze_code.config.output, - "--language", - lang, - "--lines", - s_line .. "," .. e_line, - "--config", - conf, - "--theme", - theme, - file, - } - else - cmd_args = { - "--output", - freeze_code.config.output, - "--language", - lang, - "--lines", - s_line .. "," .. e_line, - "--config", - conf, - file, - } - end - - commands.job = {} - commands.job.stdout = vim.loop.new_pipe(false) - commands.job.stderr = vim.loop.new_pipe(false) - - local job_opts = { - args = cmd_args, - stdio = { nil, commands.job.stdout, commands.job.stderr }, - } - - local msg = "🍧 frozen frame in path=" .. freeze_code.config.output - commands.job.handle = vim.loop.spawn(cmd, job_opts, commands.on_exit(msg)) - vim.loop.read_start(commands.job.stdout, vim.schedule_wrap(commands.on_output)) - vim.loop.read_start(commands.job.stderr, vim.schedule_wrap(commands.on_output)) + vim.api.nvim_create_user_command("FreezeLine", function(_) + api.freeze_line() + end, {}) end ---@class FreezeCode @@ -372,7 +26,7 @@ end ---freeze-code's set up function ---@param opts FreezeCodeConfig|nil freeze_code.setup = function(opts) - freeze_code.config = vim.tbl_deep_extend("force", {}, freeze_code.config, opts or {}) + cfg.setup(opts) create_autocmds() end diff --git a/lua/freeze-code/utils/api.lua b/lua/freeze-code/utils/api.lua new file mode 100644 index 0000000..118b925 --- /dev/null +++ b/lua/freeze-code/utils/api.lua @@ -0,0 +1,12 @@ +local binary = require("freeze-code.binary.binary_handler") +local M = {} + +M.freeze = function(start_line, end_line) + binary:freeze(start_line, end_line) +end + +M.freeze_line = function() + binary:freeze_line() +end + +return M diff --git a/lua/freeze-code/utils/init.lua b/lua/freeze-code/utils/init.lua index 07a63ca..6192112 100644 --- a/lua/freeze-code/utils/init.lua +++ b/lua/freeze-code/utils/init.lua @@ -7,12 +7,99 @@ ---@class FreezeCodeUtils ---@field logger FreezeCodeLogger ----@field os FreezeCodeOS ---@type FreezeCodeUtils|{} local M = {} M.logger = require("freeze-code.utils.logger") +local freeze_version = "0.1.6" + +---@class FreezeCodeUtils +---@field get_archive_extension function: Get the release archive file extension depending on OS +M.get_archive_extension = function() + local os_name, _ = M.get_os_info() + + return (os_name == "Windows" and ".zip" or ".tar.gz") +end + +---@class FreezeCodeUtils +---@field release_file_url function: Get the release file for the right OS and Architecture from official release +---page, https://github.com/charmbracelet/freeze/releases, for the specified version +---@return string +M.release_file_url = function() + -- check pre-existence of required programs + if vim.fn.executable("curl") == 0 or vim.fn.executable("tar") == 0 then + vim.notify("curl and/or tar are required", vim.log.levels.ERROR, { title = "Freeze" }) + return "" + end + + local filename = M.get_freeze_filename() .. M.get_archive_extension() + + -- create the url, filename based on os and arch + return "https://github.com/charmbracelet/freeze/releases/download/v" .. freeze_version .. "/" .. filename +end + +---@class FreezeCodeUtils +---@field get_os_info function: Get the os and architecture info +---@return string os_name: OS name +---@return string arch: OS architecture +M.get_os_info = function() + local os_name, arch + + local raw_os = vim.loop.os_uname().sysname + local raw_arch = jit.arch + local os_patterns = { + ["Windows"] = "Windows", + ["Windows_NT"] = "Windows", + ["Linux"] = "Linux", + ["Darwin"] = "Darwin", + ["BSD"] = "Freebsd", + } + + local arch_patterns = { + ["x86"] = "i386", + ["x64"] = "x86_64", + ["arm"] = "arm7", + ["arm64"] = "arm64", + } + + os_name = os_patterns[raw_os] + arch = arch_patterns[raw_arch] + + return os_name, arch +end + +---@class FreezeCodeUtils +---@field get_freeze_filename function: Get the filename for the release (e.g. freeze___) +M.get_freeze_filename = function() + local os_name, arch = M.get_os_info() + + if os_name == nil or arch == nil then + vim.notify("os not supported or could not be parsed", vim.log.levels.ERROR, { title = "Freeze" }) + return "" + end + local filename = "freeze_" .. freeze_version .. "_" .. os_name .. "_" .. arch + return filename +end + +---@class FreezeCodeUtils +---@field check_executable function: Check if the executable is available +---@param cmd string: Command to check +---@param path_to_check string: Path to check +---@return boolean success: true if executes, false otherwise +M.check_executable = function(cmd, path_to_check) + if vim.fn.executable(cmd) == 0 then + M.logger.err_fmt( + "[freeze-code] could not execute `" .. cmd .. "` binary in path=`%s` . make sure you have the right config", + path_to_check + ) + return false + end + return true +end + +---@class FreezeCodeUtils +---@field os FreezeCodeOS M.os = { is_win = vim.loop.os_uname().version:match("Windows"), is_macos = vim.loop.os_uname().version:match("Darwin"), diff --git a/tests/freeze-code/freeze-code_spec.lua b/tests/freeze-code/freeze-code_spec.lua index d4cea0c..27c2633 100644 --- a/tests/freeze-code/freeze-code_spec.lua +++ b/tests/freeze-code/freeze-code_spec.lua @@ -43,29 +43,32 @@ local function get_image_path() end describe("[freeze-code test]", function() + local freeze_code_config = require("freeze-code.config") + local freeze_code_api = require("freeze-code.utils.api") local freeze_code = require("freeze-code") local same = assert.are.same local not_same = assert.not_same before_each(function() + freeze_code_config = require("freeze-code.config") freeze_code = require("freeze-code") freeze_code.setup() end) describe("setup", function() it("creates user commands", function() vim.cmd("runtime plugin/freeze-code.lua") - freeze_code.setup() + freeze_code_config.setup() local user_commands = api.nvim_get_commands({}) not_same(nil, user_commands.Freeze) end) it("with default config", function() - local expected = require("freeze-code").config + local expected = require("freeze-code.config").config freeze_code.setup() - same(freeze_code.config, expected) + same(freeze_code_config.config, expected) end) it("with custom config", function() - local default_config = require("freeze-code").config + local default_config = require("freeze-code.config").config local opts = { copy = true, freeze_config = { @@ -74,9 +77,9 @@ describe("[freeze-code test]", function() } local expected = vim.tbl_deep_extend("force", {}, default_config, opts or {}) freeze_code.setup(expected) - same(freeze_code.config, expected) - same(freeze_code.config.copy, true) - same(freeze_code.config.freeze_config.theme, "rose-pine-moon") + same(freeze_code_config.config, expected) + same(freeze_code_config.config.copy, true) + same(freeze_code_config.config.freeze_config.theme, "rose-pine-moon") end) end) @@ -96,7 +99,7 @@ describe("[freeze-code test]", function() local buffer = create_buffer() freeze_code.setup() - api.nvim_buf_call(buffer, freeze_code.freeze) + api.nvim_buf_call(buffer, freeze_code_api.freeze) if vim.wait(delay, function() return get_image_path() ~= nil @@ -111,7 +114,7 @@ describe("[freeze-code test]", function() freeze_code.setup() api.nvim_buf_call(buffer, function() - freeze_code.freeze(1, 3) + freeze_code_api.freeze(1, 3) end) if vim.wait(delay, function() diff --git a/tests/freeze-code/installation_spec.lua b/tests/freeze-code/installation_spec.lua index a8cc168..0c6e549 100644 --- a/tests/freeze-code/installation_spec.lua +++ b/tests/freeze-code/installation_spec.lua @@ -51,11 +51,13 @@ describe("[freeze-code installation]", function() local mocks_bin_freeze = mocks_bin .. "/freeze" local mocks_go_bin = vim.fn.expand("./tests/mocks/go/bin") local mocks_go_bin_freeze = mocks_go_bin .. "/freeze" + local binary_fetcher = require("freeze-code.binary.binary_fetcher") before_each(function() ---@type FreezeCode freeze_code = require("freeze-code") - default_config = require("freeze-code").config + default_config = require("freeze-code.config").config + freeze_code.setup() remove_binaries(mocks_bin, mocks_go_bin) @@ -79,7 +81,7 @@ describe("[freeze-code installation]", function() freeze_path = mocks_bin_freeze, } opts = vim.tbl_deep_extend("force", {}, default_config, opts or {}) - freeze_code.agnostic_install_freeze(opts) + binary_fetcher:agnostic_installation(opts) local mock_binary = mocks_bin_freeze if vim.wait(delay, function() @@ -98,7 +100,7 @@ describe("[freeze-code installation]", function() freeze_path = mocks_go_bin_freeze, } opts = vim.tbl_deep_extend("force", {}, default_config, opts or {}) - freeze_code.go_install_freeze(opts) + binary_fetcher:go_installation(opts) local mock_binary = mocks_go_bin_freeze if vim.wait(delay, function()