diff options
Diffstat (limited to 'runtime/lua/vim/_defaults.lua')
-rw-r--r-- | runtime/lua/vim/_defaults.lua | 386 |
1 files changed, 256 insertions, 130 deletions
diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua index cc872dea83..91baee1a1e 100644 --- a/runtime/lua/vim/_defaults.lua +++ b/runtime/lua/vim/_defaults.lua @@ -4,27 +4,10 @@ do --- --- See |v_star-default| and |v_#-default| do - local function region_chunks(region) - local chunks = {} - local maxcol = vim.v.maxcol - for line, cols in vim.spairs(region) do - local endcol = cols[2] == maxcol and -1 or cols[2] - local chunk = vim.api.nvim_buf_get_text(0, line, cols[1], line, endcol, {})[1] - table.insert(chunks, chunk) - end - return chunks - end - local function _visual_search(cmd) assert(cmd == '/' or cmd == '?') - local region = vim.region( - 0, - '.', - 'v', - vim.api.nvim_get_mode().mode:sub(1, 1), - vim.o.selection == 'inclusive' - ) - local chunks = region_chunks(region) + local chunks = + vim.fn.getregion(vim.fn.getpos('.'), vim.fn.getpos('v'), { type = vim.fn.mode() }) local esc_chunks = vim .iter(chunks) :map(function(v) @@ -67,17 +50,23 @@ do --- See |&-default| vim.keymap.set('n', '&', ':&&<CR>', { desc = ':help &-default' }) + --- Use Q in visual mode to execute a macro on each line of the selection. #21422 + --- + --- Applies to @x and includes @@ too. + vim.keymap.set( + 'x', + 'Q', + ':normal! @<C-R>=reg_recorded()<CR><CR>', + { silent = true, desc = ':help v_Q-default' } + ) + vim.keymap.set( + 'x', + '@', + "':normal! @'.getcharstr().'<CR>'", + { silent = true, expr = true, desc = ':help v_@-default' } + ) --- Map |gx| to call |vim.ui.open| on the identifier under the cursor do - -- TODO: use vim.region() when it lands... #13896 #16843 - local function get_visual_selection() - local save_a = vim.fn.getreginfo('a') - vim.cmd([[norm! "ay]]) - local selection = vim.fn.getreg('a', 1) - vim.fn.setreg('a', save_a) - return selection - end - local function do_open(uri) local _, err = vim.ui.open(uri) if err then @@ -91,7 +80,10 @@ do do_open(vim.fn.expand('<cfile>')) end, { desc = gx_desc }) vim.keymap.set({ 'x' }, 'gx', function() - do_open(get_visual_selection()) + local lines = + vim.fn.getregion(vim.fn.getpos('.'), vim.fn.getpos('v'), { type = vim.fn.mode() }) + -- Trim whitespace on each line and concatenate. + do_open(table.concat(vim.iter(lines):map(vim.trim):totable())) end, { desc = gx_desc }) end end @@ -128,14 +120,43 @@ do vim.api.nvim_create_autocmd({ 'TermClose' }, { group = nvim_terminal_augroup, + nested = true, desc = 'Automatically close terminal buffers when started with no arguments and exiting without an error', callback = function(args) - if vim.v.event.status == 0 then - local info = vim.api.nvim_get_chan_info(vim.bo[args.buf].channel) - local argv = info.argv or {} - if #argv == 1 and argv[1] == vim.o.shell then - vim.cmd({ cmd = 'bdelete', args = { args.buf }, bang = true }) + if vim.v.event.status ~= 0 then + return + end + local info = vim.api.nvim_get_chan_info(vim.bo[args.buf].channel) + local argv = info.argv or {} + if #argv == 1 and argv[1] == vim.o.shell then + vim.api.nvim_buf_delete(args.buf, { force = true }) + end + end, + }) + + vim.api.nvim_create_autocmd('TermRequest', { + group = nvim_terminal_augroup, + desc = 'Respond to OSC foreground/background color requests', + callback = function(args) + local channel = vim.bo[args.buf].channel + if channel == 0 then + return + end + local fg_request = args.data == '\027]10;?' + local bg_request = args.data == '\027]11;?' + if fg_request or bg_request then + -- WARN: This does not return the actual foreground/background color, + -- but rather returns: + -- - fg=white/bg=black when Nvim option 'background' is 'dark' + -- - fg=black/bg=white when Nvim option 'background' is 'light' + local red, green, blue = 0, 0, 0 + local bg_option_dark = vim.o.background == 'dark' + if (fg_request and bg_option_dark) or (bg_request and not bg_option_dark) then + red, green, blue = 65535, 65535, 65535 end + local command = fg_request and 10 or 11 + local data = string.format('\027]%d;rgb:%04x/%04x/%04x\007', command, red, green, blue) + vim.api.nvim_chan_send(channel, data) end end, }) @@ -165,118 +186,125 @@ do }) end ---- Guess value of 'background' based on terminal color. ---- ---- We write Operating System Command (OSC) 11 to the terminal to request the ---- terminal's background color. We then wait for a response. If the response ---- matches `rgba:RRRR/GGGG/BBBB/AAAA` where R, G, B, and A are hex digits, then ---- compute the luminance[1] of the RGB color and classify it as light/dark ---- accordingly. Note that the color components may have anywhere from one to ---- four hex digits, and require scaling accordingly as values out of 4, 8, 12, ---- or 16 bits. Also note the A(lpha) component is optional, and is parsed but ---- ignored in the calculations. ---- ---- [1] https://en.wikipedia.org/wiki/Luma_%28video%29 -do - --- Parse a string of hex characters as a color. - --- - --- The string can contain 1 to 4 hex characters. The returned value is - --- between 0.0 and 1.0 (inclusive) representing the intensity of the color. - --- - --- For instance, if only a single hex char "a" is used, then this function - --- returns 0.625 (10 / 16), while a value of "aa" would return 0.664 (170 / - --- 256). +-- Only do the following when the TUI is attached +local tty = nil +for _, ui in ipairs(vim.api.nvim_list_uis()) do + if ui.chan == 1 and ui.stdout_tty then + tty = ui + break + end +end + +if tty then + local group = vim.api.nvim_create_augroup('nvim_tty', {}) + + --- Set an option after startup (so that OptionSet is fired), but only if not + --- already set by the user. --- - --- @param c string Color as a string of hex chars - --- @return number? Intensity of the color - local function parsecolor(c) - if #c == 0 or #c > 4 then - return nil + --- @param option string Option name + --- @param value any Option value + local function setoption(option, value) + if vim.api.nvim_get_option_info2(option, {}).was_set then + -- Don't do anything if option is already set + return end - local val = tonumber(c, 16) - if not val then - return nil + -- Wait until Nvim is finished starting to set the option to ensure the + -- OptionSet event fires. + if vim.v.vim_did_enter == 1 then + vim.o[option] = value + else + vim.api.nvim_create_autocmd('VimEnter', { + group = group, + once = true, + nested = true, + callback = function() + setoption(option, value) + end, + }) end - - local max = tonumber(string.rep('f', #c), 16) - return val / max end - --- Parse an OSC 11 response - --- - --- Either of the two formats below are accepted: - --- - --- OSC 11 ; rgb:<red>/<green>/<blue> + --- Guess value of 'background' based on terminal color. --- - --- or + --- We write Operating System Command (OSC) 11 to the terminal to request the + --- terminal's background color. We then wait for a response. If the response + --- matches `rgba:RRRR/GGGG/BBBB/AAAA` where R, G, B, and A are hex digits, then + --- compute the luminance[1] of the RGB color and classify it as light/dark + --- accordingly. Note that the color components may have anywhere from one to + --- four hex digits, and require scaling accordingly as values out of 4, 8, 12, + --- or 16 bits. Also note the A(lpha) component is optional, and is parsed but + --- ignored in the calculations. --- - --- OSC 11 ; rgba:<red>/<green>/<blue>/<alpha> - --- - --- where - --- - --- <red>, <green>, <blue>, <alpha> := h | hh | hhh | hhhh - --- - --- The alpha component is ignored, if present. - --- - --- @param resp string OSC 11 response - --- @return string? Red component - --- @return string? Green component - --- @return string? Blue component - local function parseosc11(resp) - local r, g, b - r, g, b = resp:match('^\027%]11;rgb:(%x+)/(%x+)/(%x+)$') - if not r and not g and not b then - local a - r, g, b, a = resp:match('^\027%]11;rgba:(%x+)/(%x+)/(%x+)/(%x+)$') - if not a or #a > 4 then - return nil, nil, nil + --- [1] https://en.wikipedia.org/wiki/Luma_%28video%29 + do + --- Parse a string of hex characters as a color. + --- + --- The string can contain 1 to 4 hex characters. The returned value is + --- between 0.0 and 1.0 (inclusive) representing the intensity of the color. + --- + --- For instance, if only a single hex char "a" is used, then this function + --- returns 0.625 (10 / 16), while a value of "aa" would return 0.664 (170 / + --- 256). + --- + --- @param c string Color as a string of hex chars + --- @return number? Intensity of the color + local function parsecolor(c) + if #c == 0 or #c > 4 then + return nil end - end - - if r and g and b and #r <= 4 and #g <= 4 and #b <= 4 then - return r, g, b - end - return nil, nil, nil - end + local val = tonumber(c, 16) + if not val then + return nil + end - local tty = false - for _, ui in ipairs(vim.api.nvim_list_uis()) do - if ui.chan == 1 and ui.stdout_tty then - tty = true - break + local max = tonumber(string.rep('f', #c), 16) + return val / max end - end - - if tty then - local timer = assert(vim.uv.new_timer()) - ---@param bg string New value of the 'background' option - local function setbg(bg) - if vim.api.nvim_get_option_info2('background', {}).was_set then - -- Don't do anything if 'background' is already set - return + --- Parse an OSC 11 response + --- + --- Either of the two formats below are accepted: + --- + --- OSC 11 ; rgb:<red>/<green>/<blue> + --- + --- or + --- + --- OSC 11 ; rgba:<red>/<green>/<blue>/<alpha> + --- + --- where + --- + --- <red>, <green>, <blue>, <alpha> := h | hh | hhh | hhhh + --- + --- The alpha component is ignored, if present. + --- + --- @param resp string OSC 11 response + --- @return string? Red component + --- @return string? Green component + --- @return string? Blue component + local function parseosc11(resp) + local r, g, b + r, g, b = resp:match('^\027%]11;rgb:(%x+)/(%x+)/(%x+)$') + if not r and not g and not b then + local a + r, g, b, a = resp:match('^\027%]11;rgba:(%x+)/(%x+)/(%x+)/(%x+)$') + if not a or #a > 4 then + return nil, nil, nil + end end - -- Wait until Nvim is finished starting to set 'background' to ensure the - -- OptionSet event fires. - if vim.v.vim_did_enter == 1 then - if vim.o.background ~= bg then - vim.o.background = bg - end - else - vim.api.nvim_create_autocmd('VimEnter', { - once = true, - nested = true, - callback = function() - setbg(bg) - end, - }) + if r and g and b and #r <= 4 and #g <= 4 and #b <= 4 then + return r, g, b end + + return nil, nil, nil end + local timer = assert(vim.uv.new_timer()) + local id = vim.api.nvim_create_autocmd('TermResponse', { + group = group, nested = true, callback = function(args) local resp = args.data ---@type string @@ -289,7 +317,7 @@ do if rr and gg and bb then local luminance = (0.299 * rr) + (0.587 * gg) + (0.114 * bb) local bg = luminance < 0.5 and 'dark' or 'light' - setbg(bg) + setoption('background', bg) end return true @@ -297,10 +325,10 @@ do end, }) - io.stdout:write('\027]11;?\027\\') + io.stdout:write('\027]11;?\007') timer:start(1000, 0, function() - -- No response received. Delete the autocommand + -- Delete the autocommand if no response was received vim.schedule(function() -- Suppress error if autocommand has already been deleted pcall(vim.api.nvim_del_autocmd, id) @@ -311,4 +339,102 @@ do end end) end + + --- If the TUI (term_has_truecolor) was able to determine that the host + --- terminal supports truecolor, enable 'termguicolors'. Otherwise, query the + --- terminal (using both XTGETTCAP and SGR + DECRQSS). If the terminal's + --- response indicates that it does support truecolor enable 'termguicolors', + --- but only if the user has not already disabled it. + do + if tty.rgb then + -- The TUI was able to determine truecolor support + setoption('termguicolors', true) + else + local caps = {} ---@type table<string, boolean> + require('vim.termcap').query({ 'Tc', 'RGB', 'setrgbf', 'setrgbb' }, function(cap, found) + if not found then + return + end + + caps[cap] = true + if caps.Tc or caps.RGB or (caps.setrgbf and caps.setrgbb) then + setoption('termguicolors', true) + end + end) + + local timer = assert(vim.uv.new_timer()) + + -- Arbitrary colors to set in the SGR sequence + local r = 1 + local g = 2 + local b = 3 + + local id = vim.api.nvim_create_autocmd('TermResponse', { + group = group, + nested = true, + callback = function(args) + local resp = args.data ---@type string + local decrqss = resp:match('^\027P1%$r([%d;:]+)m$') + + if decrqss then + -- The DECRQSS SGR response first contains attributes separated by + -- semicolons, followed by the SGR itself with parameters separated + -- by colons. Some terminals include "0" in the attribute list + -- unconditionally; others do not. Our SGR sequence did not set any + -- attributes, so there should be no attributes in the list. + local attrs = vim.split(decrqss, ';') + if #attrs ~= 1 and (#attrs ~= 2 or attrs[1] ~= '0') then + return true + end + + -- The returned SGR sequence should begin with 48:2 + local sgr = attrs[#attrs]:match('^48:2:([%d:]+)$') + if not sgr then + return true + end + + -- The remaining elements of the SGR sequence should be the 3 colors + -- we set. Some terminals also include an additional parameter + -- (which can even be empty!), so handle those cases as well + local params = vim.split(sgr, ':') + if #params ~= 3 and (#params ~= 4 or (params[1] ~= '' and params[1] ~= '1')) then + return true + end + + if + tonumber(params[#params - 2]) == r + and tonumber(params[#params - 1]) == g + and tonumber(params[#params]) == b + then + setoption('termguicolors', true) + end + + return true + end + end, + }) + + -- Write SGR followed by DECRQSS. This sets the background color then + -- immediately asks the terminal what the background color is. If the + -- terminal responds to the DECRQSS with the same SGR sequence that we + -- sent then the terminal supports truecolor. + local decrqss = '\027P$qm\027\\' + if os.getenv('TMUX') then + decrqss = string.format('\027Ptmux;%s\027\\', decrqss:gsub('\027', '\027\027')) + end + io.stdout:write(string.format('\027[48;2;%d;%d;%dm%s', r, g, b, decrqss)) + + timer:start(1000, 0, function() + -- Delete the autocommand if no response was received + vim.schedule(function() + -- Suppress error if autocommand has already been deleted + pcall(vim.api.nvim_del_autocmd, id) + end) + + if not timer:is_closing() then + timer:close() + end + end) + end + end end |