diff --git a/README.md b/README.md index 29940a2..939c717 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ You also need to have the gh command-line tool installed and configured with you Once you have Neovim and gh installed, you can install `gist.nvim` using your favorite plugin manager. For example, if you are using packer, you can add the following line to your init.vim file: -``` +```lua use "rawnly/gist.nvim" ``` @@ -21,11 +21,18 @@ To create a Gist from the current file, use the `:CreateGist` command in Neovim. The plugin will prompt you for a description and whether the Gist should be private or public. ```vim -:CreateGist + :CreateGist [description] [public=true] ``` -After you enter the description and privacy settings, the plugin will create the Gist using the gh command-line tool and copy the Gist's URL to the system clipboard. -You can then paste the URL into a browser to view the Gist. +- `:CreateGist` will create the gist from the current selection +- `:CreateGistFromFile` will create the gist from the current file + +Both the commands accept the same options which are `[description=]` and `[public=true]` + +If you don't pass the `description` it will prompt to insert one later. +If you pass `[public=true]` it won't prompt for privacy later. + +After you enter the description and privacy settings, the plugin will create the Gist using the gh command-line tool and copy the Gist's URL to the given clipboard registry. ## Configuration diff --git a/doc/gist.txt b/doc/gist.txt index 2e25b8c..46346a1 100644 --- a/doc/gist.txt +++ b/doc/gist.txt @@ -5,19 +5,29 @@ NAME SYNOPSIS :CreateGist + :CreateGistFromFile DESCRIPTION - The `:CreateGist` command creates a GitHub Gist from the current file using the `gh` command-line tool. The plugin prompts you for a description and privacy settings for the Gist, and then copies the URL of the created Gist to the system clipboard. + The `:CreateGist` command creates a GitHub Gist from the buffer selection using the `gh` command-line tool. + The `:CreateGistFile` command creates a GitHub Gist from the current file using the `gh` command-line tool. + + The plugin prompts you for a description and privacy settings for the Gist, and then copies the URL of the created Gist to the system clipboard. OPTIONS - None + You can provide description for the Gist as an argument to the command. If you don't provide a description, the plugin will prompt you for one. + public=true You can override the privacy settings for the Gist as an argument to the command. If you don't provide privacy settings, the plugin will prompt you for them. EXAMPLES To create a Gist from the current file, run the following command in Neovim: - :CreateGist + :CreateGistFile [description] [public=true] + + The plugin will prompt you for a description and privacy settings for the Gist. + After you enter the description and privacy settings, the plugin will create the Gist using the `gh` command-line tool and copy the URL of the created Gist to the system clipboard. + + To Create a Gist from current selection, run the following command in Neovim: - The plugin will prompt you for a description and privacy settings for the Gist. After you enter the description and privacy settings, the plugin will create the Gist using the `gh` command-line tool and copy the URL of the created Gist to the system clipboard. + :CreateGist [description] [public=true] SEE ALSO :help gist.config diff --git a/lua/gist/core/gh.lua b/lua/gist/core/gh.lua index 3c8ee3f..06d2a99 100644 --- a/lua/gist/core/gh.lua +++ b/lua/gist/core/gh.lua @@ -1,44 +1,47 @@ +local utils = require("gist.core.utils") local M = {} --- Creates a Github gist with the specified filename and description -- -- @param filename string The filename of the Gist +-- @param content string|nil The content of the Gist -- @param description string The description of the Gist -- @param private boolean Wether the Gist should be private -- @return string|nil The URL of the created Gist -- @return number|nil The error of the command -function M.create_gist(filename, description, private) +function M.create_gist(filename, content, description, private) local public_flag = private and "" or "--public" - local escaped_description = vim.fn.shellescape(description) - - local cmd = string.format( - "gh gist create %s %s --filename %s -d %s", - vim.fn.expand("%"), - public_flag, - filename, - escaped_description - ) - - local handle = io.popen(cmd) - - -- null check on handle - if handle == nil then - return nil + description = vim.fn.shellescape(description) + + local cmd + + if content ~= nil then + filename = vim.fn.shellescape(filename) + cmd = string.format("gh gist create -f %s -d %s %s", filename, description, public_flag) + else + -- expand filepath if no content is provided + cmd = string.format( + "gh gist create %s %s --filename %s -d %s", + vim.fn.expand("%"), + public_flag, + filename, + description + ) end - local output = handle:read("*a") - handle:close() + local output = utils.exec(cmd, content) if vim.v.shell_error ~= 0 then return output, vim.v.shell_error end - local url = string.gsub(output, "\n", "") + local url = utils.extract_gist_url(output) return url, nil end --- Reads the configuration from the user's vimrc +-- -- @return table A table with the configuration properties function M.read_config() local ok, values = pcall(vim.api.nvim_get_var, { "gist_is_private", "gist_clipboard" }) diff --git a/lua/gist/core/utils.lua b/lua/gist/core/utils.lua new file mode 100644 index 0000000..745e1d5 --- /dev/null +++ b/lua/gist/core/utils.lua @@ -0,0 +1,102 @@ +local M = {} + +local function is_visual() + if vim.fn.mode() ~= "v" and vim.fn.mode() ~= "V" and vim.fn.mode() ~= "" then + return false + end + + return true +end + +function M.get_current_selection(start_line, end_line) + local bufnr = vim.api.nvim_get_current_buf() + + start_line = start_line - 1 -- Convert to 0-based line number + end_line = end_line - 1 -- Convert to 0-based line number + + local lines = vim.api.nvim_buf_get_lines(bufnr, start_line, end_line + 1, false) + + return table.concat(lines, "\n") +end + +function M.get_last_selection() + local bufnr = vim.api.nvim_get_current_buf() + + -- Save the current cursor position + local saved_cursor = vim.api.nvim_win_get_cursor(0) + + -- Get the start and end positions of the visual selection + vim.cmd("normal! gv") + local start_pos = vim.fn.getpos("'<") + local end_pos = vim.fn.getpos("'>") + + -- Restore the cursor position + vim.api.nvim_win_set_cursor(0, saved_cursor) + + local start_line = start_pos[2] - 1 + local end_line = end_pos[2] + local content_lines = vim.api.nvim_buf_get_lines(bufnr, start_line, end_line, false) + local content = table.concat(content_lines, "\n") + + return content +end + +local function read_file(path) + local file = io.open(path, "rb") -- r read mode and b binary mode + if not file then + return nil + end + local content = file:read("*a") -- *a or *all reads the whole file + file:close() + return content +end + +function M.exec(cmd, stdin) + print(string.format("Executing: %s", cmd)) + local tmp = os.tmpname() + + local pipe = io.popen(cmd .. "> " .. tmp, "w") + + if not pipe then + return nil + end + + if stdin then + pipe:write(stdin) + end + + pipe:close() + + local output = read_file(tmp) + os.remove(tmp) + + return output +end + +function M.extract_gist_url(output) + local pattern = "https://gist.github.com/%S+" + + return output:match(pattern) +end + +-- @param args string +function M.parseArgs(args) + -- parse args as key=value + local parsed = {} + + for _, arg in ipairs(vim.split(args, " ", {})) do + local key, value = unpack(vim.split(arg, "=", { plain = true })) + + if value == "true" then + value = true + elseif value == "false" then + value = false + end + + parsed[key] = value + end + + return parsed +end + +return M diff --git a/lua/gist/init.lua b/lua/gist/init.lua index 1eccad7..ba4fac1 100644 --- a/lua/gist/init.lua +++ b/lua/gist/init.lua @@ -1,15 +1,34 @@ +local utils = require("gist.core.utils") local core = require("gist.core.gh") local M = {} -function M.create() +local function get_details(ctx) local config = core.read_config() local filename = vim.fn.expand("%:t") - local description = vim.fn.input("Description: ") - local is_private = config.is_private or vim.fn.input("Create a private Gist? (y/n): ") == "y" + local description = ctx.description or vim.fn.input("Gist description: ") - local url, err = core.create_gist(filename, description, is_private) + local is_private + + if ctx.public ~= nil then + is_private = not ctx.public + else + is_private = config.public or vim.fn.input("Create a private Gist? (y/n): ") == "y" + end + + return { + filename = filename, + description = description, + is_private = is_private, + } +end + +local function create(content, ctx) + local config = core.read_config() + local details = get_details(ctx) + + local url, err = core.create_gist(details.filename, content, details.description, details.is_private) if err ~= nil then vim.api.nvim_err_writeln("Error creating Gist: " .. err) @@ -19,4 +38,34 @@ function M.create() end end +--- Creates a Gist from the current selection +function M.create(opts) + local content = nil + local args = utils.parseArgs(opts.args) + + local start_line = opts.line1 + local end_line = opts.line2 + local description = opts.fargs[1] + + if start_line ~= end_line then + content = utils.get_current_selection(start_line, end_line) + end + + return create(content, { + description = description, + public = args.public, + }) +end + +--- Creates a Gist from the current file. +function M.create_from_file(opts) + local args = utils.parseArgs(opts.args) + local description = opts.fargs[1] + + create(nil, { + description = description, + public = args.public, + }) +end + return M diff --git a/plugin/gist.lua b/plugin/gist.lua index f43b6ac..dd9f72e 100644 --- a/plugin/gist.lua +++ b/plugin/gist.lua @@ -1,6 +1,13 @@ local gist = require("gist") +vim.api.nvim_create_user_command("CreateGistFromFile", gist.create_from_file, { + nargs = "?", + desc = "Create a Gist from the current file.", + range = false, +}) + vim.api.nvim_create_user_command("CreateGist", gist.create, { - bang = true, - desc = "Create a new gist from current file", + nargs = "?", + desc = "Create a Gist from the current selection.", + range = true, })