@@ -181,6 +181,17 @@ function M.file(ctx)
181181ctx .preview :loc ()
182182end
183183
184+ --- @param diff string | string[] | snacks.picker.diff.Block[]
185+ --- @param ft " diff" | " git"
186+ --- @param ctx snacks.picker.preview.ctx
187+ local function fancy_diff (diff ,ft ,ctx )
188+ require (" snacks.picker.util.diff" ).render (ctx .preview :scratch (),ns ,diff , {ft = ft })
189+ Snacks .util .wo (ctx .win ,ctx .picker .opts .previewers .diff .wo or {})
190+ if ft ~= " diff" then
191+ ctx .preview :highlight ({ft = ft })
192+ end
193+ end
194+
184195--- @param cmd string[]
185196--- @param ctx snacks.picker.preview.ctx
186197--- @param opts ? snacks.job.Opts |{ ft ?: string }
@@ -190,48 +201,55 @@ function M.cmd(cmd, ctx, opts)
190201local buf = ctx .preview :scratch ()
191202vim .bo [buf ].buftype = " nofile"
192203
193- local job = Job .new (
194- buf ,
195- cmd ,
196- Snacks .config .merge (opts , {
197- debug = ctx .picker .opts .debug .proc ,
198- term = opts .term ~= false and not opts .ft and opts .pty ~= false ,
199- width = vim .api .nvim_win_get_width (ctx .win ),
200- height = vim .api .nvim_win_get_height (ctx .win ),
201- cwd = ctx .item .cwd or ctx .picker .opts .cwd ,
202- env = {
203- PAGER = " cat" ,
204- DELTA_PAGER = " cat" ,
205- },
206- })
207- )
204+ opts = Snacks .config .merge (opts , {
205+ debug = ctx .picker .opts .debug .proc ,
206+ term = opts .term ~= false and not opts .ft and opts .pty ~= false ,
207+ width = vim .api .nvim_win_get_width (ctx .win ),
208+ height = vim .api .nvim_win_get_height (ctx .win ),
209+ cwd = ctx .item .cwd or ctx .picker .opts .cwd ,
210+ env = {
211+ PAGER = " cat" ,
212+ DELTA_PAGER = " cat" ,
213+ },
214+ })
208215
209- if opts .ft then
216+ local style = ctx .picker .opts .previewers .diff .style
217+ if style == " fancy" and vim .tbl_contains ({" diff" ," git" },opts .ft )then
218+ opts .on_line = function ()end or nil -- disable default line handler
219+ opts .on_lines = function (_ ,lines )
220+ fancy_diff (lines ,opts .ft ,ctx )
221+ end
222+ end
223+
224+ local job = Job .new (buf ,cmd ,opts )
225+
226+ if opts .ft and style ~= " fancy" then
210227ctx .preview :highlight ({ft = opts .ft })
211228end
212229return job
213230end
214231
215232--- @param ctx snacks.picker.preview.ctx
233+ --- @return string[] , boolean terminal
216234local function git (ctx , ...)
217- local builtin = ctx .picker .opts .previewers .git . builtin
218- local ret = {" git" , " -c " , " delta. " .. vim . o . background .. " =true " }
219- vim .list_extend (ret ,builtin and {" --no-pager" }or {})
235+ local terminal = ctx .picker .opts .previewers .diff . style == " terminal "
236+ local ret = {" git" }
237+ vim .list_extend (ret ,not terminal and {" --no-pager" }or {})
220238vim .list_extend (ret ,ctx .picker .opts .previewers .git .args or {})
221239vim .list_extend (ret , {... })
222- return ret ,builtin
240+ return ret ,terminal
223241end
224242
225243--- @param ctx snacks.picker.preview.ctx
226244function M .git_show (ctx )
227- local cmd ,builtin = git (ctx ," show" ,ctx .item .commit )
245+ local cmd ,terminal = git (ctx ," show" ,ctx .item .commit )
228246local pathspec = ctx .item .files or ctx .item .file
229247pathspec = type (pathspec )== " table" and pathspec or {pathspec }
230248if # pathspec > 0 then
231249cmd [# cmd + 1 ]= " --"
232250vim .list_extend (cmd ,pathspec )
233251end
234- M .cmd (cmd ,ctx , {ft = builtin and " git" or nil })
252+ M .cmd (cmd ,ctx , {ft = not terminal and " git" or nil })
235253end
236254
237255--- @param ctx snacks.picker.preview.ctx
@@ -265,20 +283,24 @@ function M.git_log(ctx)
265283author = author ,
266284 },ctx .picker )
267285Snacks .picker .highlight .render (ctx .buf ,ns , {hl }, {append = true })
286+ Snacks .util .wo (ctx .win , {breakindent = true ,wrap = true ,linebreak = true })
268287end
269288end ,
270289 })
271290end
272291
273292--- @param ctx snacks.picker.preview.ctx
274293function M .diff (ctx )
275- local builtin = ctx .picker .opts .previewers .diff .builtin
276- if builtin then
294+ local style = ctx .picker .opts .previewers .diff .style
295+ local cmd = vim .deepcopy (ctx .picker .opts .previewers .diff .cmd )
296+ style = style == " terminal" and vim .fn .executable (cmd [1 ])== 0 and " fancy" or style
297+ if style == " syntax" then
277298ctx .item .preview = {text = ctx .item .diff ,ft = " diff" ,loc = false }
278299return M .preview (ctx )
300+ elseif style ~= " terminal" then
301+ return fancy_diff (ctx .item .diff ," diff" ,ctx )
279302end
280- local cmd = vim .deepcopy (ctx .picker .opts .previewers .diff .cmd )
281- if cmd [1 ]== " delta" then
303+ if cmd [1 ]== " delta" and not vim .tbl_contains (cmd ," --dark" )and not vim .tbl_contains (cmd ," --light" )then
282304table.insert (cmd ,2 ," --" .. vim .o .background )
283305end
284306M .cmd (cmd ,ctx , {
@@ -288,17 +310,26 @@ end
288310
289311--- @param ctx snacks.picker.preview.ctx
290312function M .git_diff (ctx )
291- local cmd ,builtin = git (ctx ," diff" ," HEAD" )
313+ local cmd ,terminal = git (ctx ," diff" ," --no-ext-diff" )
314+ if not ctx .item .status then
315+ cmd [# cmd + 1 ]= " HEAD" -- generic diff against HEAD
316+ elseif ctx .item .status :find (" [UAD][UAD]" )then
317+ cmd [# cmd + 1 ]= " --cc" -- combined diff for conflicts
318+ elseif ctx .item .status :sub (1 ,1 )~= " " then
319+ cmd [# cmd + 1 ]= " --cached" -- staged changes
320+ end
292321if ctx .item .file then
293322vim .list_extend (cmd , {" --" ,ctx .item .file })
294323end
295- M .cmd (cmd ,ctx , {ft = builtin and " diff" or nil })
324+ M .cmd (cmd ,ctx , {
325+ ft = not terminal and " diff" or nil ,
326+ })
296327end
297328
298329--- @param ctx snacks.picker.preview.ctx
299330function M .git_stash (ctx )
300- local cmd ,builtin = git (ctx ," stash" ," show" ," --patch" ,ctx .item .stash )
301- M .cmd (cmd ,ctx , {ft = builtin and " diff" or nil })
331+ local cmd ,terminal = git (ctx ," stash" ," show" ," --patch" ,ctx .item .stash )
332+ M .cmd (cmd ,ctx , {ft = not terminal and " diff" or nil })
302333end
303334
304335--- @param ctx snacks.picker.preview.ctx