mirror of
https://github.com/folke/snacks.nvim
synced 2025-12-23 08:47:57 +00:00
7.5 KiB
7.5 KiB
🍿 gh
A modern GitHub CLI integration for Neovim that brings GitHub issues and pull requests directly into your editor.
✨ Features
- 📋 Browse and search GitHub issues and pull requests with fuzzy finding
- 🔍 View full issue/PR details including comments, reactions, and status checks
- 📝 Perform GitHub actions directly from Neovim:
- Comment on issues and PRs
- Close, reopen, edit, and merge PRs
- Add reactions and labels
- Review PRs (approve, request changes, comment)
- Checkout PR branches locally
- View PR diffs with syntax highlighting
- ⌨️ Customizable keymaps for common GitHub operations
- 🎨 Beautiful syntax highlighting using Treesitter
- 🔗 Open issues/PRs in your web browser
- 📎 Yank URLs to clipboard
- 🌲 Built on top of the powerful Snacks picker
⚡️ Requirements
- GitHub CLI (
gh) - must be installed and authenticated - Snacks picker enabled
🚀 Recommended Setup
{
"folke/snacks.nvim",
opts = {
gh = {
-- your gh configuration comes here
-- or leave it empty to use the default settings
-- refer to the configuration section below
},
picker = {
sources = {
gh_issue = {
-- your gh_issue picker configuration comes here
-- or leave it empty to use the default settings
},
gh_pr = {
-- your gh_pr picker configuration comes here
-- or leave it empty to use the default settings
}
}
},
},
keys = {
{ "<leader>gi", function() Snacks.picker.gh_issue() end, desc = "GitHub Issues (open)" },
{ "<leader>gI", function() Snacks.picker.gh_issue({ state = "all" }) end, desc = "GitHub Issues (all)" },
{ "<leader>gp", function() Snacks.picker.gh_pr() end, desc = "GitHub Pull Requests (open)" },
{ "<leader>gP", function() Snacks.picker.gh_pr({ state = "all" }) end, desc = "GitHub Pull Requests (all)" },
},
}
📚 Usage
-- Browse open issues
Snacks.picker.gh_issue()
-- Browse all issues (including closed)
Snacks.picker.gh_issue({ state = "all" })
-- Browse open pull requests
Snacks.picker.gh_pr()
-- Browse all pull requests
Snacks.picker.gh_pr({ state = "all" })
-- View PR diff
Snacks.picker.gh_diff({ pr = 123 })
-- Open issue/PR in buffer
Snacks.gh.open({ type = "issue", number = 123, repo = "owner/repo" })
Available Actions
When viewing an issue or PR in the picker, press <cr> to show available actions:
Snacks.gh makes extensive use of Snacks.scratch for editing comments and descriptions.
Common Actions:
- Open in buffer - View full details with comments
- Open in browser - Open in GitHub web UI
- Add comment - Add a new comment
- Add reaction - React with emoji
- Add/Remove labels - Manage labels
- Close/Reopen - Change issue/PR state
- Edit - Edit title and body
- Yank URL - Copy URL to clipboard
Pull Request/Issue Specific:
- View diff - Show changed files with syntax highlighting
- Checkout - Checkout PR branch locally
- Merge - Merge, squash, or rebase and merge
- Review - Approve, request changes, or comment
- Mark as draft/ready - Change draft status
- and more...
GitHub Buffers
When you open an issue or PR in a buffer, you get a beautiful rendered view with:
- Metadata - Status, author, dates, labels, reactions, and assignees
- Description - Full issue/PR body with markdown rendering
- Comments - All comments with author info and timestamps
- Status Checks - PR status checks and CI results (for PRs)
- Syntax Highlighting - Full Treesitter support for markdown
- Folding - Foldable sections for comments and metadata
Default Keymaps in GitHub Buffers:
| Key | Action | Description |
|---|---|---|
<cr> |
Select Action | Show available actions menu |
i |
Edit | Edit issue/PR title and body |
a |
Add Comment | Add a new comment |
c |
Close | Close the issue/PR |
o |
Reopen | Reopen a closed issue/PR |
See the config section to customize these keymaps.
📦 Setup
-- lazy.nvim
{
"folke/snacks.nvim",
---@type snacks.Config
opts = {
gh = {
-- your gh configuration comes here
-- or leave it empty to use the default settings
-- refer to the configuration section below
}
}
}
⚙️ Config
---@class snacks.gh.Config
{
--- Keymaps for GitHub buffers
---@type table<string, snacks.gh.Keymap|false>?
keys = {
select = { "<cr>", "gh_actions", desc = "Select Action" },
edit = { "i" , "gh_edit" , desc = "Edit" },
comment = { "a" , "gh_comment", desc = "Add Comment" },
close = { "c" , "gh_close" , desc = "Close" },
reopen = { "o" , "gh_reopen" , desc = "Reopen" },
},
---@type vim.wo|{}
wo = {
breakindent = true,
wrap = true,
showbreak = "",
linebreak = true,
number = false,
relativenumber = false,
foldexpr = "v:lua.vim.treesitter.foldexpr()",
foldmethod = "expr",
concealcursor = "n",
conceallevel = 2,
winhighlight = Snacks.util.winhl({
Normal = "SnacksGhNormal",
NormalFloat = "SnacksGhNormalFloat",
FloatBorder = "SnacksGhBorder",
FloatTitle = "SnacksGhTitle",
FloatFooter = "SnacksGhFooter",
}),
},
---@type vim.bo|{}
bo = {},
icons = {
logo = " ",
user= " ",
checkmark = " ",
crossmark = " ",
block = "■",
file = " ",
checks = {
pending = " ",
success = " ",
failure = "",
skipped = " ",
},
issue = {
open = " ",
completed = " ",
other = " "
},
pr = {
open = " ",
closed = " ",
merged = " ",
draft = " ",
other = " ",
},
merge_status = {
clean = " ",
dirty = " ",
blocked = " ",
unstable = " "
},
reactions = {
thumbs_up = "👍",
thumbs_down = "👎",
eyes = "👀",
confused = "😕",
heart = "❤️",
hooray = "🎉",
laugh = "😄",
rocket = "🚀",
},
},
}
📚 Types
---@alias snacks.gh.Keymap.fn fun(item:snacks.picker.gh.Item, buf:snacks.gh.Buf)
---@class snacks.gh.Keymap: vim.keymap.set.Opts
---@field [1] string lhs
---@field [2] string|snacks.gh.Keymap.fn rhs
---@field mode? string|string[] defaults to `n`
📦 Module
---@class snacks.gh
---@field api snacks.gh.api
---@field item snacks.picker.gh.Item
Snacks.gh = {}
Snacks.gh.issue()
---@param opts? snacks.picker.gh.issue.Config
Snacks.gh.issue(opts)
Snacks.gh.pr()
---@param opts? snacks.picker.gh.pr.Config
Snacks.gh.pr(opts)