Repository navigation
Expand file tree
/
Copy pathscrollback.lua
More file actions
148 lines (141 loc) · 5.03 KB
/
Copy pathscrollback.lua
File metadata and controls
148 lines (141 loc) · 5.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
-- Minimal scrollback pager, used by Rex's edit_scrollback (cmd+shift+f).
-- Usage: nvim --clean -u ~/.config/nvim/scrollback.lua <file captured with `rex capture --format=vt`>
-- Renders the ANSI colors via a terminal buffer; no plugins, starts in ~10ms.
vim.opt.rtp:prepend(vim.fn.stdpath("data") .. "/lazy/catppuccin")
pcall(function()
require("catppuccin").setup({ flavour = "mocha", term_colors = true, auto_integrations = false })
vim.cmd.colorscheme("catppuccin-mocha")
end)
local o = vim.o
o.number = false
o.relativenumber = false
o.signcolumn = "no"
o.laststatus = 0
o.showmode = false
o.ruler = false
o.ignorecase = true
o.smartcase = true
o.hlsearch = true
o.scrolloff = 4
o.shada = ""
o.swapfile = false
o.clipboard = ""
o.mousescroll = "ver:3,hor:0"
o.scrollback = 100000
-- visible cursor: solid block (same as main config) plus a highlighted line
o.guicursor = "n-v-c-sm:block-blinkon0,i-ci-ve:ver25-blinkon0,r-cr-o:hor20-blinkon0,t:block-blinkon0-TermCursor"
o.cursorline = true
vim.g.mapleader = " "
local map = vim.keymap.set
map({ "n", "x" }, "y", '"+y', { desc = "Copy to system clipboard" })
map({ "n", "x" }, "gy", '"+y', { desc = "Copy to system clipboard" })
map("n", "Y", '"+y$')
-- strip trailing padding (background colors, right prompts) from yanked lines
vim.api.nvim_create_autocmd("TextYankPost", {
callback = function()
local e = vim.v.event
if e.operator ~= "y" then
return
end
local lines = vim.tbl_map(function(l)
return (l:gsub("%s+$", ""))
end, e.regcontents)
-- dedent by the common indent, so nested lines keep their relative indent
local indent = math.huge
for _, l in ipairs(lines) do
if l ~= "" then
indent = math.min(indent, #l:match("^%s*"))
end
end
if indent ~= math.huge and indent > 0 then
lines = vim.tbl_map(function(l)
return l:sub(indent + 1)
end, lines)
end
-- regtype without the blockwise width, so setreg recomputes it
vim.fn.setreg(e.regname == "" and '"' or e.regname, lines, e.regtype:sub(1, 1))
end,
})
map("n", "q", "<cmd>qa!<cr>")
map("n", "<Esc>", function()
if vim.v.hlsearch == 1 then
vim.cmd.nohlsearch()
else
vim.cmd("qa!")
end
end)
-- e: save the rendered text (no escapes) over the capture and exit 3; the Rex script reopens it in full nvim
map("n", "e", function()
vim.fn.writefile(vim.api.nvim_buf_get_lines(0, 0, -1, false), vim.fn.argv(0))
vim.cmd("cquit 3")
end, { desc = "Open in full nvim" })
-- ctrl+h/j/k/l: move to the neighbouring Rex pane (Rex passes these keys to nvim)
for key, dir in pairs({ h = "left", j = "down", k = "up", l = "right" }) do
map("n", "<C-" .. key .. ">", function()
vim.system({ vim.env.REX or "rex", "focus", dir })
end, { desc = "Focus pane " .. dir })
end
-- jump between shell prompts (starship's ❯)
map({ "n", "x" }, "[[", "<cmd>silent! ?^\\s*❯<cr><cmd>nohlsearch<cr>", { desc = "Previous prompt" })
map({ "n", "x" }, "]]", "<cmd>silent! /^\\s*❯<cr><cmd>nohlsearch<cr>", { desc = "Next prompt" })
-- URLs: ]u / [u jump, <leader>u picks one from the whole capture
local url_pat = [[\v<https?://[^ \t<>"'`()\[\]{}]+]]
map("n", "]u", function()
vim.fn.search(url_pat, "W")
end, { desc = "Next URL" })
map("n", "[u", function()
vim.fn.search(url_pat, "bW")
end, { desc = "Previous URL" })
local plain = "" -- capture without escapes, set on VimEnter
map("n", "<leader>u", function()
local urls, seen = {}, {}
for url in plain:gmatch("https?://[^%s<>\"'`()%[%]{}]+") do
url = url:gsub("[.,;:!?]+$", "")
if not seen[url] then
seen[url] = true
table.insert(urls, 1, url) -- newest first
end
end
if #urls == 0 then
return vim.notify("No URLs in scrollback")
end
vim.ui.select(urls, { prompt = "Open URL" }, function(url)
if url then
vim.ui.open(url)
end
end)
end, { desc = "Pick URL to open" })
vim.api.nvim_create_autocmd("VimEnter", {
once = true,
callback = function()
local path = vim.fn.argv(0)
local f = path ~= "" and io.open(path, "rb")
if not f then
return
end
local data = f:read("*a")
f:close()
-- strip CSI and OSC sequences, but keep OSC 8 hyperlink targets (clickable text whose URL isn't shown)
local targets = {}
for url in data:gmatch("\27%]8;[^;]*;([^\27\7]+)") do
table.insert(targets, url)
end
plain = table.concat(targets, "\n")
.. "\n"
.. data:gsub("\27%[[%d;:?<=>]*[ -/]*[@-~]", ""):gsub("\27%].-\7", ""):gsub("\27%].-\27\\", "")
local file_buf = vim.api.nvim_get_current_buf()
local buf = vim.api.nvim_create_buf(false, true)
vim.api.nvim_win_set_buf(0, buf)
vim.api.nvim_buf_delete(file_buf, { force = true })
local chan = vim.api.nvim_open_term(buf, {})
vim.api.nvim_chan_send(chan, (data:gsub("\r?\n", "\r\n")))
-- terminal output is processed async; jump to the end once it lands
vim.defer_fn(function()
vim.bo[buf].modifiable = false
vim.cmd("normal! G")
-- drop trailing empty lines so G lands on real output
vim.fn.search("\\S", "bW")
vim.cmd("normal! 0")
end, 50)
end,
})