From 309e5614724e7ccbb35cd3895120893e57996647 Mon Sep 17 00:00:00 2001 From: rawnly Date: Wed, 15 Mar 2023 13:33:06 +0100 Subject: [PATCH 1/8] feat(utils): get user selection --- lua/gist/core/utils.lua | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 lua/gist/core/utils.lua diff --git a/lua/gist/core/utils.lua b/lua/gist/core/utils.lua new file mode 100644 index 0000000..e07ec4d --- /dev/null +++ b/lua/gist/core/utils.lua @@ -0,0 +1,23 @@ +local M = {} + +function M.get_selection() + local start_line, start_col = unpack(vim.fn.getpos("'<")[2]) + local end_line, end_col = unpack(vim.fn.getpos("'>")[2]) + + -- selection is empty + if start_line == end_line and start_col == end_col then + return nil + end + + -- selection is not empty + 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, false) + + return table.concat(lines, "\n") +end + +return M From 04e7047adcae36921f67cf247df71079691d3edf Mon Sep 17 00:00:00 2001 From: rawnly Date: Wed, 15 Mar 2023 14:20:55 +0100 Subject: [PATCH 2/8] fix: fixed default clipboard --- lua/gist/core/gh.lua | 5 +++-- lua/gist/init.lua | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lua/gist/core/gh.lua b/lua/gist/core/gh.lua index 62aaca2..a268e49 100644 --- a/lua/gist/core/gh.lua +++ b/lua/gist/core/gh.lua @@ -3,11 +3,12 @@ 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) @@ -44,7 +45,7 @@ function M.read_config() local ok, values = pcall(vim.api.nvim_get_var, { "gist_is_private", "gist_clipboard" }) local is_private = ok and values[1] or false - local clipboard = ok and values[2] or "xsel" + local clipboard = ok and values[2] or "+" local config = { is_private = is_private, diff --git a/lua/gist/init.lua b/lua/gist/init.lua index 1eccad7..a7c0ff9 100644 --- a/lua/gist/init.lua +++ b/lua/gist/init.lua @@ -9,7 +9,7 @@ function M.create() local description = vim.fn.input("Description: ") local is_private = config.is_private or vim.fn.input("Create a private Gist? (y/n): ") == "y" - local url, err = core.create_gist(filename, description, is_private) + local url, err = core.create_gist(filename, nil, description, is_private) if err ~= nil then vim.api.nvim_err_writeln("Error creating Gist: " .. err) From b81152b02960878584d34108e743ee45e57432c0 Mon Sep 17 00:00:00 2001 From: rawnly Date: Wed, 15 Mar 2023 15:02:19 +0100 Subject: [PATCH 3/8] feat: add inline description --- lua/gist/init.lua | 23 +++++++++++++++++++++-- plugin/gist.lua | 18 +++++++++++++++--- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/lua/gist/init.lua b/lua/gist/init.lua index a7c0ff9..67cf1c6 100644 --- a/lua/gist/init.lua +++ b/lua/gist/init.lua @@ -2,11 +2,24 @@ local core = require("gist.core.gh") local M = {} -function M.create() +function M.create_from_file(args) local config = core.read_config() local filename = vim.fn.expand("%:t") - local description = vim.fn.input("Description: ") + local description = "" + + if args[1] ~= nil then + description = args[1] + end + + if description == "" or description == nil then + vim.api.nvim_echo({ + { "No description provided" }, + }, true, {}) + + description = vim.fn.input("Description: ") + end + local is_private = config.is_private or vim.fn.input("Create a private Gist? (y/n): ") == "y" local url, err = core.create_gist(filename, nil, description, is_private) @@ -19,4 +32,10 @@ function M.create() end end +function M.create_from_selection(opts) + vim.api.nvim_echo({ + { "Creating Gist from selection..." .. opts.args, "Identifier" }, + }, true, {}) +end + return M diff --git a/plugin/gist.lua b/plugin/gist.lua index b86c600..b223d46 100644 --- a/plugin/gist.lua +++ b/plugin/gist.lua @@ -1,6 +1,18 @@ local gist = require("gist") -vim.api.nvim_create_user_command("CreateGist", gist.create, { - bang = true, - desc = "Create a new gist from curretn file", +vim.api.nvim_create_user_command("Rawnly", function(args) + vim.api.nvim_echo({ + { "Args: " .. table.concat(args, " "), "Identifier" }, + }, true, {}) +end, { + nargs = "*", +}) + +vim.api.nvim_create_user_command("CreateFileGist", gist.create_from_file, { + desc = "Create a new gist from current file", + nargs = "*", +}) + +vim.api.nvim_create_user_command("CreateGist", gist.create_from_selection, { + desc = "Create a new gist from current selection", }) From 4aba4e21efd809f7cfd7c41bbab28641d09bb4ae Mon Sep 17 00:00:00 2001 From: rawnly Date: Wed, 15 Mar 2023 16:57:24 +0100 Subject: [PATCH 4/8] feat: create from selection --- lua/gist/core/gh.lua | 38 ++++++++++--------- lua/gist/core/utils.lua | 81 +++++++++++++++++++++++++++++++++++------ lua/gist/init.lua | 29 ++++----------- plugin/gist.lua | 21 ++--------- 4 files changed, 101 insertions(+), 68 deletions(-) diff --git a/lua/gist/core/gh.lua b/lua/gist/core/gh.lua index a268e49..06d2a99 100644 --- a/lua/gist/core/gh.lua +++ b/lua/gist/core/gh.lua @@ -1,3 +1,4 @@ +local utils = require("gist.core.utils") local M = {} --- Creates a Github gist with the specified filename and description @@ -10,36 +11,37 @@ local M = {} -- @return number|nil The error of the command 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 index e07ec4d..a9884f7 100644 --- a/lua/gist/core/utils.lua +++ b/lua/gist/core/utils.lua @@ -1,23 +1,82 @@ local M = {} -function M.get_selection() - local start_line, start_col = unpack(vim.fn.getpos("'<")[2]) - local end_line, end_col = unpack(vim.fn.getpos("'>")[2]) - - -- selection is empty - if start_line == end_line and start_col == end_col then - return nil +local function is_visual() + if vim.fn.mode() ~= "v" and vim.fn.mode() ~= "V" and vim.fn.mode() ~= "" then + return false end - -- selection is not empty + 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 + 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, false) + 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 + return M diff --git a/lua/gist/init.lua b/lua/gist/init.lua index 67cf1c6..a8f1549 100644 --- a/lua/gist/init.lua +++ b/lua/gist/init.lua @@ -1,28 +1,21 @@ +local utils = require("gist.core.utils") local core = require("gist.core.gh") local M = {} -function M.create_from_file(args) +function M.create(start_line, end_line) local config = core.read_config() + local content = nil - local filename = vim.fn.expand("%:t") - local description = "" - - if args[1] ~= nil then - description = args[1] - end - - if description == "" or description == nil then - vim.api.nvim_echo({ - { "No description provided" }, - }, true, {}) - - description = vim.fn.input("Description: ") + if start_line ~= end_line then + content = utils.get_current_selection(start_line, end_line) end + local filename = vim.fn.expand("%:t") + local description = vim.fn.input("Gist description: ") local is_private = config.is_private or vim.fn.input("Create a private Gist? (y/n): ") == "y" - local url, err = core.create_gist(filename, nil, description, is_private) + local url, err = core.create_gist(filename, content, description, is_private) if err ~= nil then vim.api.nvim_err_writeln("Error creating Gist: " .. err) @@ -32,10 +25,4 @@ function M.create_from_file(args) end end -function M.create_from_selection(opts) - vim.api.nvim_echo({ - { "Creating Gist from selection..." .. opts.args, "Identifier" }, - }, true, {}) -end - return M diff --git a/plugin/gist.lua b/plugin/gist.lua index b223d46..2e87ab0 100644 --- a/plugin/gist.lua +++ b/plugin/gist.lua @@ -1,18 +1,3 @@ -local gist = require("gist") - -vim.api.nvim_create_user_command("Rawnly", function(args) - vim.api.nvim_echo({ - { "Args: " .. table.concat(args, " "), "Identifier" }, - }, true, {}) -end, { - nargs = "*", -}) - -vim.api.nvim_create_user_command("CreateFileGist", gist.create_from_file, { - desc = "Create a new gist from current file", - nargs = "*", -}) - -vim.api.nvim_create_user_command("CreateGist", gist.create_from_selection, { - desc = "Create a new gist from current selection", -}) +vim.cmd([[ + command! -range CreateGist :lua require("gist").create(, ) +]]) From c53ef29e32c65272891c70596d156cbd6c9b9426 Mon Sep 17 00:00:00 2001 From: rawnly Date: Wed, 15 Mar 2023 22:06:45 +0100 Subject: [PATCH 5/8] feat: inline args --- lua/gist/core/utils.lua | 15 +++++++++++++- lua/gist/init.lua | 44 +++++++++++++++++++++++++++++++++-------- plugin/gist.lua | 17 +++++++++++++--- 3 files changed, 64 insertions(+), 12 deletions(-) diff --git a/lua/gist/core/utils.lua b/lua/gist/core/utils.lua index a9884f7..b940acf 100644 --- a/lua/gist/core/utils.lua +++ b/lua/gist/core/utils.lua @@ -12,7 +12,7 @@ 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 + 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) @@ -79,4 +79,17 @@ function M.extract_gist_url(output) 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 })) + parsed[key] = value + end + + return parsed +end + return M diff --git a/lua/gist/init.lua b/lua/gist/init.lua index a8f1549..70bc85c 100644 --- a/lua/gist/init.lua +++ b/lua/gist/init.lua @@ -3,19 +3,25 @@ local core = require("gist.core.gh") local M = {} -function M.create(start_line, end_line) +local function get_details(default_description) local config = core.read_config() - local content = nil - - if start_line ~= end_line then - content = utils.get_current_selection(start_line, end_line) - end local filename = vim.fn.expand("%:t") - local description = vim.fn.input("Gist description: ") + local description = default_description or vim.fn.input("Gist description: ") local is_private = config.is_private or vim.fn.input("Create a private Gist? (y/n): ") == "y" - local url, err = core.create_gist(filename, content, description, is_private) + return { + filename = filename, + description = description, + is_private = is_private, + } +end + +local function create(content, desc) + local config = core.read_config() + local details = get_details(desc) + + 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) @@ -25,4 +31,26 @@ function M.create(start_line, end_line) end end +--- Creates a Gist from the current selection +function M.create(opts) + local content = nil + + 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) +end + +--- Creates a Gist from the current file. +function M.create_from_file(opts) + local description = opts.fargs[1] + + create(nil, description) +end + return M diff --git a/plugin/gist.lua b/plugin/gist.lua index 2e87ab0..3201519 100644 --- a/plugin/gist.lua +++ b/plugin/gist.lua @@ -1,3 +1,14 @@ -vim.cmd([[ - command! -range CreateGist :lua require("gist").create(, ) -]]) +local gist = require("gist") +local utils = require("gist.core.utils") + +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, { + nargs = "?", + desc = "Create a Gist from the current selection.", + range = true, +}) From 4dfee271ec4162b6244be2d09eba5c4b41a9a934 Mon Sep 17 00:00:00 2001 From: rawnly Date: Wed, 15 Mar 2023 22:08:49 +0100 Subject: [PATCH 6/8] feat: help update --- doc/gist.txt | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) 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 From 8ca0c373103557db7ce107fee798d98e583f92cd Mon Sep 17 00:00:00 2001 From: rawnly Date: Wed, 15 Mar 2023 22:34:51 +0100 Subject: [PATCH 7/8] docs: reamde --- README.md | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) 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 From 01d5964fc4c0712e48ad45521894b851c54ef655 Mon Sep 17 00:00:00 2001 From: rawnly Date: Wed, 15 Mar 2023 22:38:46 +0100 Subject: [PATCH 8/8] feat: handle options --- lua/gist/core/utils.lua | 7 +++++++ lua/gist/init.lua | 29 ++++++++++++++++++++++------- plugin/gist.lua | 1 - 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/lua/gist/core/utils.lua b/lua/gist/core/utils.lua index b940acf..745e1d5 100644 --- a/lua/gist/core/utils.lua +++ b/lua/gist/core/utils.lua @@ -86,6 +86,13 @@ function M.parseArgs(args) 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 diff --git a/lua/gist/init.lua b/lua/gist/init.lua index 70bc85c..ba4fac1 100644 --- a/lua/gist/init.lua +++ b/lua/gist/init.lua @@ -3,12 +3,19 @@ local core = require("gist.core.gh") local M = {} -local function get_details(default_description) +local function get_details(ctx) local config = core.read_config() local filename = vim.fn.expand("%:t") - local description = default_description or vim.fn.input("Gist 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 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, @@ -17,9 +24,9 @@ local function get_details(default_description) } end -local function create(content, desc) +local function create(content, ctx) local config = core.read_config() - local details = get_details(desc) + local details = get_details(ctx) local url, err = core.create_gist(details.filename, content, details.description, details.is_private) @@ -34,6 +41,7 @@ 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 @@ -43,14 +51,21 @@ function M.create(opts) content = utils.get_current_selection(start_line, end_line) end - return create(content, description) + 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) + create(nil, { + description = description, + public = args.public, + }) end return M diff --git a/plugin/gist.lua b/plugin/gist.lua index 3201519..dd9f72e 100644 --- a/plugin/gist.lua +++ b/plugin/gist.lua @@ -1,5 +1,4 @@ local gist = require("gist") -local utils = require("gist.core.utils") vim.api.nvim_create_user_command("CreateGistFromFile", gist.create_from_file, { nargs = "?",