mirror of
https://github.com/neovim/neovim.git
synced 2025-09-06 03:18:16 +00:00
docs: OSC 133 shell config #32771
This commit is contained in:
@@ -455,6 +455,8 @@ between Vi and Vim.
|
|||||||
first column. When used after an operator, then also
|
first column. When used after an operator, then also
|
||||||
stops below a "}" in the first column. |exclusive|
|
stops below a "}" in the first column. |exclusive|
|
||||||
Note that |exclusive-linewise| often applies.
|
Note that |exclusive-linewise| often applies.
|
||||||
|
In a :terminal buffer each shell prompt is treated as
|
||||||
|
a section. |terminal_]]|
|
||||||
|
|
||||||
*][*
|
*][*
|
||||||
][ [count] |section|s forward or to the next '}' in the
|
][ [count] |section|s forward or to the next '}' in the
|
||||||
@@ -465,6 +467,8 @@ between Vi and Vim.
|
|||||||
[[ [count] |section|s backward or to the previous "{" in
|
[[ [count] |section|s backward or to the previous "{" in
|
||||||
the first column. |exclusive|
|
the first column. |exclusive|
|
||||||
Note that |exclusive-linewise| often applies.
|
Note that |exclusive-linewise| often applies.
|
||||||
|
In a :terminal buffer each shell prompt is treated as
|
||||||
|
a section. |terminal_]]|
|
||||||
|
|
||||||
*[]*
|
*[]*
|
||||||
[] [count] |section|s backward or to the previous "}" in
|
[] [count] |section|s backward or to the previous "}" in
|
||||||
@@ -498,6 +502,7 @@ A section begins after a form-feed (<C-L>) in the first column and at each of
|
|||||||
a set of section macros, specified by the pairs of characters in the
|
a set of section macros, specified by the pairs of characters in the
|
||||||
'sections' option. The default is "SHNHH HUnhsh", which defines a section to
|
'sections' option. The default is "SHNHH HUnhsh", which defines a section to
|
||||||
start at the nroff macros ".SH", ".NH", ".H", ".HU", ".nh" and ".sh".
|
start at the nroff macros ".SH", ".NH", ".H", ".HU", ".nh" and ".sh".
|
||||||
|
In a :terminal buffer each shell prompt is treated as a section. |terminal_]]|
|
||||||
|
|
||||||
The "]]" and "[[" commands stop at the '{' in the first column. This is
|
The "]]" and "[[" commands stop at the '{' in the first column. This is
|
||||||
useful to find the start of a function in a C program. To search for a '}' in
|
useful to find the start of a function in a C program. To search for a '}' in
|
||||||
|
@@ -187,26 +187,48 @@ event. The event is handled directly by Nvim and is not forwarded to plugins.
|
|||||||
|
|
||||||
OSC 133: shell integration *terminal-osc133*
|
OSC 133: shell integration *terminal-osc133*
|
||||||
|
|
||||||
Some shells will emit semantic escape sequences (OSC 133) to mark the
|
Shells can emit semantic escape sequences (OSC 133) to mark where each prompt
|
||||||
beginning and end of a prompt. The start of a prompt is marked with the
|
starts and ends. The start of a prompt is marked by sequence `OSC 133 ; A ST`,
|
||||||
sequence `OSC 133 ; A`. Nvim can be configured to create signs in terminal
|
and the end by `OSC 133 ; B ST`.
|
||||||
buffers marking shell prompts. Example: >lua
|
|
||||||
|
|
||||||
local ns = vim.api.nvim_create_namespace('terminal_prompt_markers')
|
*shell-prompt-config*
|
||||||
|
You can configure your shell "rc" (e.g. ~/.bashrc) to emit OSC 133 sequences,
|
||||||
|
or your terminal may attempt to do it for you (assuming your shell config
|
||||||
|
doesn't interfere).
|
||||||
|
|
||||||
|
- fish: https://fishshell.com/docs/current/relnotes.html#improved-terminal-support
|
||||||
|
- kitty: https://sw.kovidgoyal.net/kitty/shell-integration/
|
||||||
|
- vscode: https://code.visualstudio.com/docs/terminal/shell-integration
|
||||||
|
|
||||||
|
To configure bash to mark the start/end of each prompt, set $PROMPT_COMMAND
|
||||||
|
and $PS1 as follows: >bash
|
||||||
|
|
||||||
|
# Prompt start:
|
||||||
|
PROMPT_COMMAND='printf "\033]133;A\007"'
|
||||||
|
# Prompt end:
|
||||||
|
PS1="$PS1"'\033]133;B\007'
|
||||||
|
<
|
||||||
|
*terminal_]]* *terminal_[[*
|
||||||
|
The |]]| and |[[| motions jump to the next/previous prompts, if your shell
|
||||||
|
emits OSC 133 as described above.
|
||||||
|
|
||||||
|
*terminal-shell-prompt-signs*
|
||||||
|
To annotate each terminal prompt with a sign, call |nvim_buf_set_extmark()|
|
||||||
|
from a |TermRequest| handler: >lua
|
||||||
|
|
||||||
|
local ns = vim.api.nvim_create_namespace('my.terminal.prompt')
|
||||||
vim.api.nvim_create_autocmd('TermRequest', {
|
vim.api.nvim_create_autocmd('TermRequest', {
|
||||||
callback = function(args)
|
callback = function(args)
|
||||||
if string.match(args.data.sequence, '^\027]133;A') then
|
if string.match(args.data.sequence, '^\027]133;A') then
|
||||||
local lnum = args.data.cursor[1]
|
local lnum = args.data.cursor[1]
|
||||||
vim.api.nvim_buf_set_extmark(args.buf, ns, lnum - 1, 0, {
|
vim.api.nvim_buf_set_extmark(args.buf, ns, lnum - 1, 0, {
|
||||||
-- Replace with sign text and highlight group of choice
|
|
||||||
sign_text = '▶',
|
sign_text = '▶',
|
||||||
sign_hl_group = 'SpecialChar',
|
sign_hl_group = 'SpecialChar',
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
-- Enable signcolumn in terminal buffers.
|
||||||
-- Enable signcolumn in terminal buffers
|
|
||||||
vim.api.nvim_create_autocmd('TermOpen', {
|
vim.api.nvim_create_autocmd('TermOpen', {
|
||||||
command = 'setlocal signcolumn=auto',
|
command = 'setlocal signcolumn=auto',
|
||||||
})
|
})
|
||||||
|
@@ -125,12 +125,11 @@ lua_State *get_global_lstate(void)
|
|||||||
return global_lstate;
|
return global_lstate;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// get error on top of stack as a string
|
/// Gets the Lua error at top of stack as a string, possibly modifying it in-place (but doesn't
|
||||||
|
/// change stack height).
|
||||||
///
|
///
|
||||||
/// Might alter the top value on stack in place (but doesn't change stack height)
|
/// The returned string points to memory on the Lua stack. Use or duplicate it before using
|
||||||
///
|
/// `lstate` again.
|
||||||
/// "error" points to memory on the lua stack, use
|
|
||||||
/// or duplicate the string before using "lstate" again
|
|
||||||
///
|
///
|
||||||
/// @param[out] len length of error (can be NULL)
|
/// @param[out] len length of error (can be NULL)
|
||||||
static const char *nlua_get_error(lua_State *lstate, size_t *len)
|
static const char *nlua_get_error(lua_State *lstate, size_t *len)
|
||||||
@@ -147,7 +146,7 @@ static const char *nlua_get_error(lua_State *lstate, size_t *len)
|
|||||||
return lua_tolstring(lstate, -1, len);
|
return lua_tolstring(lstate, -1, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert lua error into a Vim error message
|
/// Converts a Lua error into a Vim error message.
|
||||||
///
|
///
|
||||||
/// @param lstate Lua interpreter state.
|
/// @param lstate Lua interpreter state.
|
||||||
/// @param[in] msg Message base, must contain one `%.*s`.
|
/// @param[in] msg Message base, must contain one `%.*s`.
|
||||||
|
@@ -170,7 +170,8 @@ struct terminal {
|
|||||||
struct {
|
struct {
|
||||||
int row, col;
|
int row, col;
|
||||||
int shape;
|
int shape;
|
||||||
bool visible;
|
bool visible; ///< Terminal wants to show cursor.
|
||||||
|
///< `TerminalState.cursor_visible` indicates whether it is actually shown.
|
||||||
bool blink;
|
bool blink;
|
||||||
} cursor;
|
} cursor;
|
||||||
|
|
||||||
|
@@ -2526,7 +2526,7 @@ describe('LSP', function()
|
|||||||
)
|
)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('Supports file creation with CreateFile payload', function()
|
it('supports file creation with CreateFile payload', function()
|
||||||
local tmpfile = tmpname(false)
|
local tmpfile = tmpname(false)
|
||||||
local uri = exec_lua('return vim.uri_from_fname(...)', tmpfile)
|
local uri = exec_lua('return vim.uri_from_fname(...)', tmpfile)
|
||||||
local edit = {
|
local edit = {
|
||||||
@@ -2544,7 +2544,7 @@ describe('LSP', function()
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
it(
|
it(
|
||||||
'Supports file creation in folder that needs to be created with CreateFile payload',
|
'supports file creation in folder that needs to be created with CreateFile payload',
|
||||||
function()
|
function()
|
||||||
local tmpfile = tmpname(false) .. '/dummy/x/'
|
local tmpfile = tmpname(false) .. '/dummy/x/'
|
||||||
local uri = exec_lua('return vim.uri_from_fname(...)', tmpfile)
|
local uri = exec_lua('return vim.uri_from_fname(...)', tmpfile)
|
||||||
@@ -2647,7 +2647,7 @@ describe('LSP', function()
|
|||||||
describe('lsp.util.rename', function()
|
describe('lsp.util.rename', function()
|
||||||
local pathsep = n.get_pathsep()
|
local pathsep = n.get_pathsep()
|
||||||
|
|
||||||
it('Can rename an existing file', function()
|
it('can rename an existing file', function()
|
||||||
local old = tmpname()
|
local old = tmpname()
|
||||||
write_file(old, 'Test content')
|
write_file(old, 'Test content')
|
||||||
local new = tmpname(false)
|
local new = tmpname(false)
|
||||||
@@ -2668,7 +2668,7 @@ describe('LSP', function()
|
|||||||
os.remove(new)
|
os.remove(new)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('Can rename a directory', function()
|
it('can rename a directory', function()
|
||||||
-- only reserve the name, file must not exist for the test scenario
|
-- only reserve the name, file must not exist for the test scenario
|
||||||
local old_dir = tmpname(false)
|
local old_dir = tmpname(false)
|
||||||
local new_dir = tmpname(false)
|
local new_dir = tmpname(false)
|
||||||
@@ -2695,7 +2695,7 @@ describe('LSP', function()
|
|||||||
os.remove(new_dir)
|
os.remove(new_dir)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('Does not touch buffers that do not match path prefix', function()
|
it('does not touch buffers that do not match path prefix', function()
|
||||||
local old = tmpname(false)
|
local old = tmpname(false)
|
||||||
local new = tmpname(false)
|
local new = tmpname(false)
|
||||||
n.mkdir_p(old)
|
n.mkdir_p(old)
|
||||||
@@ -2730,7 +2730,7 @@ describe('LSP', function()
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
it(
|
it(
|
||||||
'Does not rename file if target exists and ignoreIfExists is set or overwrite is false',
|
'does not rename file if target exists and ignoreIfExists is set or overwrite is false',
|
||||||
function()
|
function()
|
||||||
local old = tmpname()
|
local old = tmpname()
|
||||||
write_file(old, 'Old File')
|
write_file(old, 'Old File')
|
||||||
@@ -2753,7 +2753,7 @@ describe('LSP', function()
|
|||||||
end
|
end
|
||||||
)
|
)
|
||||||
|
|
||||||
it('Maintains undo information for loaded buffer', function()
|
it('maintains undo information for loaded buffer', function()
|
||||||
local old = tmpname()
|
local old = tmpname()
|
||||||
write_file(old, 'line')
|
write_file(old, 'line')
|
||||||
local new = tmpname(false)
|
local new = tmpname(false)
|
||||||
@@ -2777,7 +2777,7 @@ describe('LSP', function()
|
|||||||
eq(true, undo_kept)
|
eq(true, undo_kept)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('Maintains undo information for unloaded buffer', function()
|
it('maintains undo information for unloaded buffer', function()
|
||||||
local old = tmpname()
|
local old = tmpname()
|
||||||
write_file(old, 'line')
|
write_file(old, 'line')
|
||||||
local new = tmpname(false)
|
local new = tmpname(false)
|
||||||
@@ -2798,7 +2798,7 @@ describe('LSP', function()
|
|||||||
eq(true, undo_kept)
|
eq(true, undo_kept)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('Does not rename file when it conflicts with a buffer without file', function()
|
it('does not rename file when it conflicts with a buffer without file', function()
|
||||||
local old = tmpname()
|
local old = tmpname()
|
||||||
write_file(old, 'Old File')
|
write_file(old, 'Old File')
|
||||||
local new = tmpname(false)
|
local new = tmpname(false)
|
||||||
@@ -2817,7 +2817,7 @@ describe('LSP', function()
|
|||||||
eq('Old File', read_file(old))
|
eq('Old File', read_file(old))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('Does override target if overwrite is true', function()
|
it('does override target if overwrite is true', function()
|
||||||
local old = tmpname()
|
local old = tmpname()
|
||||||
write_file(old, 'Old file')
|
write_file(old, 'Old file')
|
||||||
local new = tmpname()
|
local new = tmpname()
|
||||||
@@ -2833,7 +2833,7 @@ describe('LSP', function()
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
describe('lsp.util.locations_to_items', function()
|
describe('lsp.util.locations_to_items', function()
|
||||||
it('Convert Location[] to items', function()
|
it('convert Location[] to items', function()
|
||||||
local expected_template = {
|
local expected_template = {
|
||||||
{
|
{
|
||||||
filename = '/fake/uri',
|
filename = '/fake/uri',
|
||||||
@@ -2879,7 +2879,7 @@ describe('LSP', function()
|
|||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('Convert LocationLink[] to items', function()
|
it('convert LocationLink[] to items', function()
|
||||||
local expected = {
|
local expected = {
|
||||||
{
|
{
|
||||||
filename = '/fake/uri',
|
filename = '/fake/uri',
|
||||||
@@ -2926,7 +2926,7 @@ describe('LSP', function()
|
|||||||
|
|
||||||
describe('lsp.util.symbols_to_items', function()
|
describe('lsp.util.symbols_to_items', function()
|
||||||
describe('convert DocumentSymbol[] to items', function()
|
describe('convert DocumentSymbol[] to items', function()
|
||||||
it('DocumentSymbol has children', function()
|
it('documentSymbol has children', function()
|
||||||
local expected = {
|
local expected = {
|
||||||
{
|
{
|
||||||
col = 1,
|
col = 1,
|
||||||
@@ -3047,7 +3047,7 @@ describe('LSP', function()
|
|||||||
)
|
)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('DocumentSymbol has no children', function()
|
it('documentSymbol has no children', function()
|
||||||
local expected = {
|
local expected = {
|
||||||
{
|
{
|
||||||
col = 1,
|
col = 1,
|
||||||
@@ -4387,7 +4387,7 @@ describe('LSP', function()
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
describe('vim.lsp.buf.code_action', function()
|
describe('vim.lsp.buf.code_action', function()
|
||||||
it('Calls client side command if available', function()
|
it('calls client side command if available', function()
|
||||||
local client --- @type vim.lsp.Client
|
local client --- @type vim.lsp.Client
|
||||||
local expected_handlers = {
|
local expected_handlers = {
|
||||||
{ NIL, {}, { method = 'shutdown', client_id = 1 } },
|
{ NIL, {}, { method = 'shutdown', client_id = 1 } },
|
||||||
@@ -4431,7 +4431,7 @@ describe('LSP', function()
|
|||||||
}
|
}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('Calls workspace/executeCommand if no client side command', function()
|
it('calls workspace/executeCommand if no client side command', function()
|
||||||
local client --- @type vim.lsp.Client
|
local client --- @type vim.lsp.Client
|
||||||
local expected_handlers = {
|
local expected_handlers = {
|
||||||
{ NIL, {}, { method = 'shutdown', client_id = 1 } },
|
{ NIL, {}, { method = 'shutdown', client_id = 1 } },
|
||||||
@@ -4472,7 +4472,7 @@ describe('LSP', function()
|
|||||||
})
|
})
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('Filters and automatically applies action if requested', function()
|
it('filters and automatically applies action if requested', function()
|
||||||
local client --- @type vim.lsp.Client
|
local client --- @type vim.lsp.Client
|
||||||
local expected_handlers = {
|
local expected_handlers = {
|
||||||
{ NIL, {}, { method = 'shutdown', client_id = 1 } },
|
{ NIL, {}, { method = 'shutdown', client_id = 1 } },
|
||||||
@@ -4547,7 +4547,7 @@ describe('LSP', function()
|
|||||||
}
|
}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('Fallback to command execution on resolve error', function()
|
it('fallback to command execution on resolve error', function()
|
||||||
clear()
|
clear()
|
||||||
exec_lua(create_server_definition)
|
exec_lua(create_server_definition)
|
||||||
local result = exec_lua(function()
|
local result = exec_lua(function()
|
||||||
@@ -4592,7 +4592,7 @@ describe('LSP', function()
|
|||||||
eq('command:1', result[5].params.command)
|
eq('command:1', result[5].params.command)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('Resolves command property', function()
|
it('resolves command property', function()
|
||||||
clear()
|
clear()
|
||||||
exec_lua(create_server_definition)
|
exec_lua(create_server_definition)
|
||||||
local result = exec_lua(function()
|
local result = exec_lua(function()
|
||||||
@@ -4639,14 +4639,14 @@ describe('LSP', function()
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
describe('vim.lsp.commands', function()
|
describe('vim.lsp.commands', function()
|
||||||
it('Accepts only string keys', function()
|
it('accepts only string keys', function()
|
||||||
matches(
|
matches(
|
||||||
'.*The key for commands in `vim.lsp.commands` must be a string',
|
'.*The key for commands in `vim.lsp.commands` must be a string',
|
||||||
pcall_err(exec_lua, 'vim.lsp.commands[1] = function() end')
|
pcall_err(exec_lua, 'vim.lsp.commands[1] = function() end')
|
||||||
)
|
)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('Accepts only function values', function()
|
it('accepts only function values', function()
|
||||||
matches(
|
matches(
|
||||||
'.*Command added to `vim.lsp.commands` must be a function',
|
'.*Command added to `vim.lsp.commands` must be a function',
|
||||||
pcall_err(exec_lua, 'vim.lsp.commands.dummy = 10')
|
pcall_err(exec_lua, 'vim.lsp.commands.dummy = 10')
|
||||||
@@ -4880,7 +4880,7 @@ describe('LSP', function()
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
describe('vim.lsp.buf.format', function()
|
describe('vim.lsp.buf.format', function()
|
||||||
it('Aborts with notify if no client matches filter', function()
|
it('aborts with notify if no client matches filter', function()
|
||||||
local client --- @type vim.lsp.Client
|
local client --- @type vim.lsp.Client
|
||||||
test_rpc_server {
|
test_rpc_server {
|
||||||
test_name = 'basic_init',
|
test_name = 'basic_init',
|
||||||
@@ -4906,7 +4906,7 @@ describe('LSP', function()
|
|||||||
}
|
}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('Sends textDocument/formatting request to format buffer', function()
|
it('sends textDocument/formatting request to format buffer', function()
|
||||||
local expected_handlers = {
|
local expected_handlers = {
|
||||||
{ NIL, {}, { method = 'shutdown', client_id = 1 } },
|
{ NIL, {}, { method = 'shutdown', client_id = 1 } },
|
||||||
{ NIL, {}, { method = 'start', client_id = 1 } },
|
{ NIL, {}, { method = 'start', client_id = 1 } },
|
||||||
@@ -4940,7 +4940,7 @@ describe('LSP', function()
|
|||||||
}
|
}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('Sends textDocument/rangeFormatting request to format a range', function()
|
it('sends textDocument/rangeFormatting request to format a range', function()
|
||||||
local expected_handlers = {
|
local expected_handlers = {
|
||||||
{ NIL, {}, { method = 'shutdown', client_id = 1 } },
|
{ NIL, {}, { method = 'shutdown', client_id = 1 } },
|
||||||
{ NIL, {}, { method = 'start', client_id = 1 } },
|
{ NIL, {}, { method = 'start', client_id = 1 } },
|
||||||
@@ -4981,7 +4981,7 @@ describe('LSP', function()
|
|||||||
}
|
}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('Sends textDocument/rangesFormatting request to format multiple ranges', function()
|
it('sends textDocument/rangesFormatting request to format multiple ranges', function()
|
||||||
local expected_handlers = {
|
local expected_handlers = {
|
||||||
{ NIL, {}, { method = 'shutdown', client_id = 1 } },
|
{ NIL, {}, { method = 'shutdown', client_id = 1 } },
|
||||||
{ NIL, {}, { method = 'start', client_id = 1 } },
|
{ NIL, {}, { method = 'start', client_id = 1 } },
|
||||||
@@ -5028,7 +5028,7 @@ describe('LSP', function()
|
|||||||
}
|
}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('Can format async', function()
|
it('can format async', function()
|
||||||
local expected_handlers = {
|
local expected_handlers = {
|
||||||
{ NIL, {}, { method = 'shutdown', client_id = 1 } },
|
{ NIL, {}, { method = 'shutdown', client_id = 1 } },
|
||||||
{ NIL, {}, { method = 'start', client_id = 1 } },
|
{ NIL, {}, { method = 'start', client_id = 1 } },
|
||||||
@@ -5157,7 +5157,7 @@ describe('LSP', function()
|
|||||||
eq(expected_range, result[5].params.range)
|
eq(expected_range, result[5].params.range)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('Aborts with notify if no clients support requested method', function()
|
it('aborts with notify if no clients support requested method', function()
|
||||||
exec_lua(create_server_definition)
|
exec_lua(create_server_definition)
|
||||||
exec_lua(function()
|
exec_lua(function()
|
||||||
vim.notify = function(msg, _)
|
vim.notify = function(msg, _)
|
||||||
|
Reference in New Issue
Block a user