mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +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
	 Justin M. Keyes
					Justin M. Keyes