1- local vim = vim
2-
31local M = {}
42
53local config = {
64 keymaps = {},
75 border = ' single' ,
6+ custom_border = {
7+ {}, -- Top left corner
8+ {}, -- Top side
9+ {}, -- Top right corner
10+ {}, -- Right side
11+ {}, -- Bottom right corner
12+ {}, -- Bottom side
13+ {}, -- Bottom left corner
14+ {}, -- Left side
15+ },
816 width = 0.8 ,
917 height = 0.8 ,
1018 cmd = { ' gh' , ' dash' },
@@ -33,6 +41,41 @@ function M.setup(user_config)
3341 end
3442end
3543
44+ local styles = {
45+ single = {
46+ { ' ╭' , ' FloatBorder' },
47+ { ' ─' , ' FloatBorder' },
48+ { ' ╮' , ' FloatBorder' },
49+ { ' │' , ' FloatBorder' },
50+ { ' ╯' , ' FloatBorder' },
51+ { ' ─' , ' FloatBorder' },
52+ { ' ╰' , ' FloatBorder' },
53+ { ' │' , ' FloatBorder' },
54+ },
55+ double = {
56+ { ' ╔' , ' FloatBorder' },
57+ { ' ═' , ' FloatBorder' },
58+ { ' ╗' , ' FloatBorder' },
59+ { ' ║' , ' FloatBorder' },
60+ { ' ╝' , ' FloatBorder' },
61+ { ' ═' , ' FloatBorder' },
62+ { ' ╚' , ' FloatBorder' },
63+ { ' ║' , ' FloatBorder' },
64+ },
65+ square = {
66+ { ' ┌' , ' FloatBorder' },
67+ { ' ─' , ' FloatBorder' },
68+ { ' ┐' , ' FloatBorder' },
69+ { ' │' , ' FloatBorder' },
70+ { ' ┘' , ' FloatBorder' },
71+ { ' ─' , ' FloatBorder' },
72+ { ' └' , ' FloatBorder' },
73+ { ' │' , ' FloatBorder' },
74+ },
75+ custom = config .custom_border ,
76+ none = nil ,
77+ }
78+
3679-- Create a floating window displaying the gh_dash buffer
3780local function open_window ()
3881 -- compute dimensions and position
@@ -43,40 +86,11 @@ local function open_window()
4386 -- resolve border style (string or table)
4487 local border = config .border
4588 if type (border ) == ' string' then
46- local styles = {
47- single = {
48- { ' ╭' , ' FloatBorder' },
49- { ' ─' , ' FloatBorder' },
50- { ' ╮' , ' FloatBorder' },
51- { ' │' , ' FloatBorder' },
52- { ' ╯' , ' FloatBorder' },
53- { ' ─' , ' FloatBorder' },
54- { ' ╰' , ' FloatBorder' },
55- { ' │' , ' FloatBorder' },
56- },
57- double = {
58- { ' ╔' , ' FloatBorder' },
59- { ' ═' , ' FloatBorder' },
60- { ' ╗' , ' FloatBorder' },
61- { ' ║' , ' FloatBorder' },
62- { ' ╝' , ' FloatBorder' },
63- { ' ═' , ' FloatBorder' },
64- { ' ╚' , ' FloatBorder' },
65- { ' ║' , ' FloatBorder' },
66- },
67- square = {
68- { ' ┌' , ' FloatBorder' },
69- { ' ─' , ' FloatBorder' },
70- { ' ┐' , ' FloatBorder' },
71- { ' │' , ' FloatBorder' },
72- { ' ┘' , ' FloatBorder' },
73- { ' ─' , ' FloatBorder' },
74- { ' └' , ' FloatBorder' },
75- { ' │' , ' FloatBorder' },
76- },
77- none = nil ,
78- }
79- border = styles [border ] or styles .single
89+ if border == ' none' then
90+ border = ' none'
91+ else
92+ border = styles [border ] or styles .single
93+ end
8094 end
8195 -- open floating window
8296 state .win = vim .api .nvim_open_win (state .buf , true , {
@@ -95,16 +109,22 @@ function M.open()
95109 vim .api .nvim_set_current_win (state .win )
96110 return
97111 end
98- if not state .buf or not vim .api .nvim_buf_is_valid (state .buf ) then
112+ if not state .buf or not vim .api .nvim_buf_is_valid (state .buf ) or vim . api . nvim_buf_get_option ( state . buf , ' modified ' ) then
99113 -- create an unlisted scratch buffer for the terminal
100114 state .buf = vim .api .nvim_create_buf (false , false )
101115 -- buffer options
102116 vim .api .nvim_buf_set_option (state .buf , ' bufhidden' , ' hide' )
103117 vim .api .nvim_buf_set_option (state .buf , ' swapfile' , false )
104118 vim .api .nvim_buf_set_option (state .buf , ' filetype' , ' gh_dash' )
105- -- map <Esc> in terminal and normal modes to close the gh_dash window
106- vim .api .nvim_buf_set_keymap (state .buf , ' t' , ' <Esc>' , [[ <C-\><C-n><cmd>lua require('gh_dash').close()<CR>]] , { noremap = true , silent = true })
107- vim .api .nvim_buf_set_keymap (state .buf , ' n' , ' <Esc>' , [[ <cmd>lua require('gh_dash').close()<CR>]] , { noremap = true , silent = true })
119+ -- Escape backgrounds the window cleanly
120+ -- Map <Esc> in terminal mode to hide the popup
121+ vim .api .nvim_buf_set_keymap (
122+ state .buf ,
123+ ' t' ,
124+ ' <Esc>' ,
125+ [[ <C-\><C-n><cmd>lua vim.defer_fn(function() require('gh_dash').toggle() end, 10)<CR>]] ,
126+ { noremap = true , silent = true }
127+ )
108128 end
109129 open_window ()
110130 -- determine if config.cmd is a simple executable name (no args) for checking
@@ -120,40 +140,34 @@ function M.open()
120140 -- if simple command and not found, handle auto-install or notify
121141 if check_cmd and vim .fn .executable (check_cmd ) == 0 then
122142 if config .autoinstall then
123- if vim .fn .executable ' npm ' == 1 then
143+ if vim .fn .executable ' gh ' == 1 then
124144 -- install via npm in the floating terminal to show output
125145 do
126146 local shell_cmd = vim .o .shell or ' sh'
127147 local cmd = {
128148 shell_cmd ,
129149 ' -c' ,
130- " echo 'Autoinstalling OpenAI gh_dash via npm ...'; npm install -g @openai/gh_dash " ,
150+ " echo 'Autoinstalling gh_dash via gh CLI extensions ...'; gh extension install dlvhdr/gh-dash " ,
131151 }
132152 state .job = vim .fn .termopen (cmd , {
133153 cwd = vim .loop .cwd (),
134- on_exit = function (_ , exit_code )
154+ on_exit = function (_ , exit_code , _ )
155+ state .job = nil
135156 if exit_code == 0 then
136- vim .notify (' [gh_dash.nvim] gh_dash CLI installed successfully' , vim .log .levels .INFO )
137- -- automatically re-launch gh_dash CLI now that it's installed
138157 vim .schedule (function ()
139158 M .close ()
140- state .buf = nil
141- M .open ()
142159 end )
143- else
144- vim .notify (' [gh_dash.nvim] failed to install gh_dash CLI' , vim .log .levels .ERROR )
145160 end
146- state .job = nil
147161 end ,
148162 })
149163 end
150164 else
151165 -- show installation instructions in the gh_dash popup
152166 local msg = {
153- ' npm not found; cannot auto-install gh_dash CLI .' ,
167+ ' gh CLI not found; cannot auto-install gh_dash extension .' ,
154168 ' ' ,
155- ' Please install via your system package manager, or manually run: ' ,
156- ' npm install -g @openai/gh_dash ' ,
169+ ' Please install the gh CLI via your system package manager' ,
170+ ' i.e. `brew install gh` ' ,
157171 }
158172 vim .api .nvim_buf_set_lines (state .buf , 0 , - 1 , false , msg )
159173 end
@@ -163,7 +177,7 @@ function M.open()
163177 ' gh_dash CLI not found.' ,
164178 ' ' ,
165179 ' Install with:' ,
166- ' npm install -g @openai/gh_dash ' ,
180+ ' gh extension install dlvhdr/gh-dash ' ,
167181 ' ' ,
168182 ' Or enable autoinstall in your plugin setup:' ,
169183 ' require("gh_dash").setup{ autoinstall = true }' ,
@@ -175,9 +189,14 @@ function M.open()
175189 -- spawn the gh_dash CLI in the floating terminal buffer
176190 if not state .job then
177191 state .job = vim .fn .termopen (config .cmd , {
178- cwd = vim .loop .cwd (),
179- on_exit = function ()
192+ wd = vim .loop .cwd (),
193+ on_exit = function (_ , exit_code , _ )
180194 state .job = nil
195+ if exit_code == 0 then
196+ vim .schedule (function ()
197+ M .close ()
198+ end )
199+ end
181200 end ,
182201 })
183202 end
@@ -188,12 +207,22 @@ function M.close()
188207 vim .api .nvim_win_close (state .win , true )
189208 state .win = nil
190209 end
210+ if state .buf and vim .api .nvim_buf_is_valid (state .buf ) then
211+ vim .api .nvim_buf_delete (state .buf , { force = true })
212+ state .buf = nil
213+ end
191214end
192215
193216function M .toggle ()
194217 if state .win and vim .api .nvim_win_is_valid (state .win ) then
195- M .close ()
218+ -- HIDE the window (don't kill the job)
219+ vim .api .nvim_win_close (state .win , true )
220+ state .win = nil
221+ elseif state .buf and vim .api .nvim_buf_is_valid (state .buf ) then
222+ -- Reopen window into existing buffer
223+ open_window ()
196224 else
225+ -- Full open if everything is gone
197226 M .open ()
198227 end
199228end
@@ -218,7 +247,7 @@ function M.status()
218247 return M .statusline () ~= ' '
219248 end ,
220249 -- gear icon
221- icon = ' ' ,
250+ icon = ' ' ,
222251 -- default color (blue)
223252 color = { fg = ' #51afef' },
224253 }
0 commit comments