mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	Merge #10433 from erw7/vim-8.1.0027
vim-patch:8.1.{27,32,36,69,70,71,91,92}
			
			
This commit is contained in:
		| @@ -2084,6 +2084,7 @@ ctxsize()			Number	return |context-stack| size | ||||
| cursor({lnum}, {col} [, {off}]) | ||||
| 				Number	move cursor to {lnum}, {col}, {off} | ||||
| cursor({list})			Number	move cursor to position in {list} | ||||
| debugbreak({pid})		Number  interrupt process being debugged | ||||
| deepcopy({expr} [, {noref}])	any	make a full copy of {expr} | ||||
| delete({fname} [, {flags}])	Number	delete the file or directory {fname} | ||||
| deletebufline({expr}, {first}[, {last}]) | ||||
| @@ -2280,6 +2281,10 @@ pathshorten({expr})		String	shorten directory names in a path | ||||
| pow({x}, {y})			Float	{x} to the power of {y} | ||||
| prevnonblank({lnum})		Number	line nr of non-blank line <= {lnum} | ||||
| printf({fmt}, {expr1}...)	String	format text | ||||
| prompt_addtext({buf}, {expr})	none	add text to a prompt buffer | ||||
| prompt_setcallback({buf}, {expr}) none	set prompt callback function | ||||
| prompt_setinterrupt({buf}, {text}) none	set prompt interrupt function | ||||
| prompt_setprompt({buf}, {text}) none	set prompt text | ||||
| pum_getpos()			Dict	position and size of pum if visible | ||||
| pumvisible()			Number	whether popup menu is visible | ||||
| pyeval({expr})			any	evaluate |Python| expression | ||||
| @@ -2290,7 +2295,7 @@ range({expr} [, {max} [, {stride}]]) | ||||
| readdir({dir} [, {expr}])	List	file names in {dir} selected by {expr} | ||||
| readfile({fname} [, {binary} [, {max}]]) | ||||
| 				List	get list of lines from file {fname} | ||||
| reg_executing()			Number	get the executing register name | ||||
| reg_executing()			String	get the executing register name | ||||
| reg_recording()			String	get the recording register name | ||||
| reltime([{start} [, {end}]])	List	get time value | ||||
| reltimefloat({time})		Float	turn the time value into a Float | ||||
| @@ -3637,6 +3642,11 @@ exp({expr})						*exp()* | ||||
| 			:echo exp(-1) | ||||
| <			0.367879 | ||||
|  | ||||
| debugbreak({pid})					*debugbreak()* | ||||
| 		Specifically used to interrupt a program being debugged.  It | ||||
| 		will cause process {pid} to get a SIGTRAP.  Behavior for other | ||||
| 		processes is undefined. See |terminal-debugger|. | ||||
| 		{Sends a SIGINT to a process {pid} other than MS-Windows} | ||||
|  | ||||
| expand({expr} [, {nosuf} [, {list}]])				*expand()* | ||||
| 		Expand wildcards and the following special keywords in {expr}. | ||||
| @@ -4541,7 +4551,7 @@ getline({lnum} [, {end}]) | ||||
| 		from the current buffer.  Example: > | ||||
| 			getline(1) | ||||
| <		When {lnum} is a String that doesn't start with a | ||||
| 		digit, line() is called to translate the String into a Number. | ||||
| 		digit, |line()| is called to translate the String into a Number. | ||||
| 		To get the line under the cursor: > | ||||
| 			getline(".") | ||||
| <		When {lnum} is smaller than 1 or bigger than the number of | ||||
| @@ -6541,6 +6551,50 @@ printf({fmt}, {expr1} ...)				*printf()* | ||||
| 		of "%" items.  If there are not sufficient or too many | ||||
| 		arguments an error is given.  Up to 18 arguments can be used. | ||||
|  | ||||
| prompt_setcallback({buf}, {expr})			*prompt_setcallback()* | ||||
| 		Set prompt callback for buffer {buf} to {expr}.  When {expr} | ||||
| 		is an empty string the callback is removed.  This has only | ||||
| 		effect if {buf} has 'buftype' set to "prompt". | ||||
|  | ||||
| 		The callback is invoked when pressing Enter.  The current | ||||
| 		buffer will always be the prompt buffer.  A new line for a | ||||
| 		prompt is added before invoking the callback, thus the prompt | ||||
| 		for which the callback was invoked will be in the last but one | ||||
| 		line. | ||||
| 		If the callback wants to add text to the buffer, it must | ||||
| 		insert it above the last line, since that is where the current | ||||
| 		prompt is.  This can also be done asynchronously. | ||||
| 		The callback is invoked with one argument, which is the text | ||||
| 		that was entered at the prompt.  This can be an empty string | ||||
| 		if the user only typed Enter. | ||||
| 		Example: > | ||||
| 		   call prompt_setcallback(bufnr(''), function('s:TextEntered')) | ||||
| 		   func s:TextEntered(text) | ||||
| 		     if a:text == 'exit' || a:text == 'quit' | ||||
| 		       stopinsert | ||||
| 		       close | ||||
| 		     else | ||||
| 		       call append(line('$') - 1, 'Entered: "' . a:text . '"') | ||||
| 		       " Reset 'modified' to allow the buffer to be closed. | ||||
| 		       set nomodified | ||||
| 		     endif | ||||
| 		   endfunc | ||||
|  | ||||
| prompt_setinterrupt({buf}, {expr})			*prompt_setinterrupt()* | ||||
| 		Set a callback for buffer {buf} to {expr}.  When {expr} is an | ||||
| 		empty string the callback is removed.  This has only effect if | ||||
| 		{buf} has 'buftype' set to "prompt". | ||||
|  | ||||
| 		This callback will be invoked when pressing CTRL-C in Insert | ||||
| 		mode.  Without setting a callback Vim will exit Insert mode, | ||||
| 		as in any buffer. | ||||
|  | ||||
| prompt_setprompt({buf}, {text})				*prompt_setprompt()* | ||||
| 		Set prompt for buffer {buf} to {text}.  You most likely want | ||||
| 		{text} to end in a space. | ||||
| 		The result is only visible if {buf} has 'buftype' set to | ||||
| 		"prompt".  Example: > | ||||
| 			call prompt_setprompt(bufnr(''), 'command: ') | ||||
|  | ||||
| pum_getpos()						*pum_getpos()* | ||||
|  		If the popup menu (see |ins-completion-menu|) is not visible, | ||||
|   | ||||
| @@ -307,6 +307,23 @@ Other commands ~ | ||||
| 	     isn't one | ||||
|  | ||||
|  | ||||
| Prompt mode ~ | ||||
| 						*termdebug-prompt* | ||||
| When on MS-Windows, gdb will run in a buffer with 'buftype' set to "prompt". | ||||
| This works slightly differently: | ||||
| - The gdb window will be in Insert mode while typing commands.  Go to Normal | ||||
|   mode with <Esc>, then you can move around in the buffer, copy/paste, etc. | ||||
|   Go back to editing the gdb command with any command that starts Insert mode, | ||||
|   such as `a` or `i`. | ||||
| - The program being debugged will run in a separate window.  On MS-Windows | ||||
|   this is a new console window.  On Unix, if the |+terminal| feature is | ||||
|   available a Terminal window will be opened to run the debugged program in. | ||||
|  | ||||
| 						*termdebug_use_prompt* | ||||
| Prompt mode can be used even when the |+terminal| feature is present with: > | ||||
| 	let g:termdebug_use_prompt = 1 | ||||
|  | ||||
|  | ||||
| Communication ~ | ||||
| 						*termdebug-communication* | ||||
| There is another, hidden, buffer, which is used for Vim to communicate with | ||||
|   | ||||
| @@ -1093,6 +1093,8 @@ A jump table for the options with a short description can be found at |Q_op|. | ||||
| 	  nowrite	buffer will not be written | ||||
| 	  quickfix	list of errors |:cwindow| or locations |:lwindow| | ||||
| 	  terminal	|terminal-emulator| buffer | ||||
| 	  prompt	buffer where only the last line can be edited, meant | ||||
| 			to be used by a plugin, see |prompt-buffer| | ||||
|  | ||||
| 	This option is used together with 'bufhidden' and 'swapfile' to | ||||
| 	specify special kinds of buffers.   See |special-buffers|. | ||||
|   | ||||
							
								
								
									
										241
									
								
								runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										241
									
								
								runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
									
									
									
									
										vendored
									
									
								
							| @@ -37,7 +37,7 @@ | ||||
| " For neovim compatibility, the vim specific calls were replaced with neovim | ||||
| " specific calls: | ||||
| "   term_start -> term_open | ||||
| "   term_sendkeys -> jobsend | ||||
| "   term_sendkeys -> chansend | ||||
| "   term_getline -> getbufline | ||||
| "   job_info && term_getjob -> using linux command ps to get the tty | ||||
| "   balloon -> nvim floating window | ||||
| @@ -47,8 +47,6 @@ | ||||
| " https://github.com/autozimu/LanguageClient-neovim/blob/0ed9b69dca49c415390a8317b19149f97ae093fa/autoload/LanguageClient.vim#L304 | ||||
| " | ||||
| " Neovim terminal also works seamlessly on windows, which is why the ability | ||||
| " to use the prompt buffer was removed. | ||||
| " | ||||
| " Author: Bram Moolenaar | ||||
| " Copyright: Vim license applies, see ":help license" | ||||
|  | ||||
| @@ -57,6 +55,12 @@ if exists(':Termdebug') | ||||
|   finish | ||||
| endif | ||||
|  | ||||
| " The terminal feature does not work with gdb on win32. | ||||
| if !has('win32') | ||||
|   let s:way = 'terminal' | ||||
| else | ||||
|   let s:way = 'prompt' | ||||
| endif | ||||
|  | ||||
| let s:keepcpo = &cpo | ||||
| set cpo&vim | ||||
| @@ -138,7 +142,19 @@ func s:StartDebug_internal(dict) | ||||
|     let s:vertical = 0 | ||||
|   endif | ||||
|  | ||||
|   call s:StartDebug_term(a:dict) | ||||
|   " Override using a terminal window by setting g:termdebug_use_prompt to 1. | ||||
|   let use_prompt = exists('g:termdebug_use_prompt') && g:termdebug_use_prompt | ||||
|   if !has('win32') && !use_prompt | ||||
|     let s:way = 'terminal' | ||||
|    else | ||||
|     let s:way = 'prompt' | ||||
|    endif | ||||
|  | ||||
|   if s:way == 'prompt' | ||||
|     call s:StartDebug_prompt(a:dict) | ||||
|   else | ||||
|     call s:StartDebug_term(a:dict) | ||||
|   endif | ||||
| endfunc | ||||
|  | ||||
| " Use when debugger didn't start or ended. | ||||
| @@ -214,11 +230,11 @@ func s:StartDebug_term(dict) | ||||
|  | ||||
|   " Set arguments to be run | ||||
|   if len(proc_args) | ||||
|     call jobsend(s:gdb_job_id, 'set args ' . join(proc_args) . "\r") | ||||
|     call chansend(s:gdb_job_id, 'set args ' . join(proc_args) . "\r") | ||||
|   endif | ||||
|  | ||||
|   " Connect gdb to the communication pty, using the GDB/MI interface | ||||
|   call jobsend(s:gdb_job_id, 'new-ui mi ' . commpty . "\r") | ||||
|   call chansend(s:gdb_job_id, 'new-ui mi ' . commpty . "\r") | ||||
|  | ||||
|   " Wait for the response to show up, users may not notice the error and wonder | ||||
|   " why the debugger doesn't work. | ||||
| @@ -275,6 +291,100 @@ func s:StartDebug_term(dict) | ||||
|   call s:StartDebugCommon(a:dict) | ||||
| endfunc | ||||
|  | ||||
| func s:StartDebug_prompt(dict) | ||||
|   " Open a window with a prompt buffer to run gdb in. | ||||
|   if s:vertical | ||||
|     vertical new | ||||
|   else | ||||
|     new | ||||
|   endif | ||||
|   let s:gdbwin = win_getid(winnr()) | ||||
|   let s:promptbuf = bufnr('') | ||||
|   call prompt_setprompt(s:promptbuf, 'gdb> ') | ||||
|   set buftype=prompt | ||||
|   file gdb | ||||
|   call prompt_setcallback(s:promptbuf, function('s:PromptCallback')) | ||||
|   call prompt_setinterrupt(s:promptbuf, function('s:PromptInterrupt')) | ||||
|  | ||||
|   if s:vertical | ||||
|     " Assuming the source code window will get a signcolumn, use two more | ||||
|     " columns for that, thus one less for the terminal window. | ||||
|     exe (&columns / 2 - 1) . "wincmd |" | ||||
|   endif | ||||
|  | ||||
|   " Add -quiet to avoid the intro message causing a hit-enter prompt. | ||||
|   let gdb_args = get(a:dict, 'gdb_args', []) | ||||
|   let proc_args = get(a:dict, 'proc_args', []) | ||||
|  | ||||
|   let cmd = [g:termdebugger, '-quiet', '--interpreter=mi2'] + gdb_args | ||||
|   "call ch_log('executing "' . join(cmd) . '"') | ||||
|  | ||||
|   let s:gdbjob = jobstart(cmd, { | ||||
| 	\ 'on_exit': function('s:EndPromptDebug'), | ||||
| 	\ 'on_stdout': function('s:GdbOutCallback'), | ||||
| 	\ }) | ||||
|   if s:gdbjob == 0 | ||||
|     echoerr 'invalid argument (or job table is full) while starting gdb job' | ||||
|     exe 'bwipe! ' . s:ptybuf | ||||
|     return | ||||
|   elseif s:gdbjob == -1 | ||||
|     echoerr 'Failed to start the gdb job' | ||||
|     call s:CloseBuffers() | ||||
|     return | ||||
|   endif | ||||
|  | ||||
|   " Interpret commands while the target is running.  This should usualy only | ||||
|   " be exec-interrupt, since many commands don't work properly while the | ||||
|   " target is running. | ||||
|   call s:SendCommand('-gdb-set mi-async on') | ||||
|   " Older gdb uses a different command. | ||||
|   call s:SendCommand('-gdb-set target-async on') | ||||
|  | ||||
|   let s:ptybuf = 0 | ||||
|   if has('win32') | ||||
|     " MS-Windows: run in a new console window for maximum compatibility | ||||
|     call s:SendCommand('set new-console on') | ||||
|   else | ||||
|     " Unix: Run the debugged program in a terminal window.  Open it below the | ||||
|     " gdb window. | ||||
|     execute 'new' | ||||
|     wincmd x | wincmd j | ||||
|     belowright let s:pty_job_id = termopen('tail -f /dev/null;#gdb program') | ||||
|     if s:pty_job_id == 0 | ||||
|       echoerr 'invalid argument (or job table is full) while opening terminal window' | ||||
|       return | ||||
|     elseif s:pty_job_id == -1 | ||||
|       echoerr 'Failed to open the program terminal window' | ||||
|       return | ||||
|     endif | ||||
|     let pty_job_info = nvim_get_chan_info(s:pty_job_id) | ||||
|     let s:ptybuf = pty_job_info['buffer'] | ||||
|     let pty = pty_job_info['pty'] | ||||
|     let s:ptywin = win_getid(winnr()) | ||||
|     call s:SendCommand('tty ' . pty) | ||||
|  | ||||
|     " Since GDB runs in a prompt window, the environment has not been set to | ||||
|     " match a terminal window, need to do that now. | ||||
|     call s:SendCommand('set env TERM = xterm-color') | ||||
|     call s:SendCommand('set env ROWS = ' . winheight(s:ptywin)) | ||||
|     call s:SendCommand('set env LINES = ' . winheight(s:ptywin)) | ||||
|     call s:SendCommand('set env COLUMNS = ' . winwidth(s:ptywin)) | ||||
|     call s:SendCommand('set env COLORS = ' . &t_Co) | ||||
|     call s:SendCommand('set env VIM_TERMINAL = ' . v:version) | ||||
|   endif | ||||
|   call s:SendCommand('set print pretty on') | ||||
|   call s:SendCommand('set breakpoint pending on') | ||||
|   " Disable pagination, it causes everything to stop at the gdb | ||||
|   call s:SendCommand('set pagination off') | ||||
|  | ||||
|   " Set arguments to be run | ||||
|   if len(proc_args) | ||||
|     call s:SendCommand('set args ' . join(proc_args)) | ||||
|   endif | ||||
|  | ||||
|   call s:StartDebugCommon(a:dict) | ||||
|   startinsert | ||||
| endfunc | ||||
|  | ||||
| func s:StartDebugCommon(dict) | ||||
|   " Sign used to highlight the line where the program has stopped. | ||||
| @@ -316,23 +426,99 @@ endfunc | ||||
| " Send a command to gdb.  "cmd" is the string without line terminator. | ||||
| func s:SendCommand(cmd) | ||||
|   "call ch_log('sending to gdb: ' . a:cmd) | ||||
|   call jobsend(s:comm_job_id, a:cmd . "\r") | ||||
|   if s:way == 'prompt' | ||||
|     call chansend(s:gdbjob, a:cmd . "\n") | ||||
|   else | ||||
|     call chansend(s:comm_job_id, a:cmd . "\r") | ||||
|   endif | ||||
| endfunc | ||||
|  | ||||
| " This is global so that a user can create their mappings with this. | ||||
| func TermDebugSendCommand(cmd) | ||||
|   let do_continue = 0 | ||||
|   if !s:stopped | ||||
|     let do_continue = 1 | ||||
|     call s:SendCommand('-exec-interrupt') | ||||
|     sleep 10m | ||||
|   if s:way == 'prompt' | ||||
|     call chansend(s:gdbjob, a:cmd . "\n") | ||||
|   else | ||||
|     let do_continue = 0 | ||||
|     if !s:stopped | ||||
|       let do_continue = 1 | ||||
|       if s:way == 'prompt' | ||||
|         " Need to send a signal to get the UI to listen.  Strangely this is only | ||||
|         " needed once. | ||||
|         call jobstop(s:gdbjob) | ||||
|       else | ||||
|         call s:SendCommand('-exec-interrupt') | ||||
|       endif | ||||
|       sleep 10m | ||||
|     endif | ||||
|     call chansend(s:gdb_job_id, a:cmd . "\r") | ||||
|     if do_continue | ||||
|       Continue | ||||
|     endif | ||||
|   endif | ||||
|   call jobsend(s:gdb_job_id, a:cmd . "\r") | ||||
|   if do_continue | ||||
|     Continue | ||||
| endfunc | ||||
|  | ||||
| " Function called when entering a line in the prompt buffer. | ||||
| func s:PromptCallback(text) | ||||
|   call s:SendCommand(a:text) | ||||
| endfunc | ||||
|  | ||||
| " Function called when pressing CTRL-C in the prompt buffer and when placing a | ||||
| " breakpoint. | ||||
| func s:PromptInterrupt() | ||||
|   if s:pid == 0 | ||||
|     echoerr 'Cannot interrupt gdb, did not find a process ID' | ||||
|   else | ||||
|     "call ch_log('Interrupting gdb') | ||||
|     " Using job_stop(s:gdbjob, 'int') does not work. | ||||
|     call debugbreak(s:pid) | ||||
|   endif | ||||
| endfunc | ||||
|  | ||||
| " Function called when gdb outputs text. | ||||
| func s:GdbOutCallback(job_id, msgs, event) | ||||
|   "call ch_log('received from gdb: ' . a:text) | ||||
|  | ||||
|   " Drop the gdb prompt, we have our own. | ||||
|   " Drop status and echo'd commands. | ||||
|   call filter(a:msgs, { index, val -> | ||||
|         \ val !=# '(gdb)' && val !=# '^done' && val[0] !=# '&'}) | ||||
|  | ||||
|   let lines = [] | ||||
|   let index = 0 | ||||
|  | ||||
|   for msg in a:msgs | ||||
|     if msg =~ '^^error,msg=' | ||||
|       if exists('s:evalexpr') | ||||
|             \ && s:DecodeMessage(msg[11:]) | ||||
|             \    =~ 'A syntax error in expression, near\|No symbol .* in current context' | ||||
|         " Silently drop evaluation errors. | ||||
|         call remove(a:msgs, index) | ||||
|         unlet s:evalexpr | ||||
|         continue | ||||
|       endif | ||||
|     elseif msg[0] == '~' | ||||
|       call add(lines, s:DecodeMessage(msg[1:])) | ||||
|       call remove(a:msgs, index) | ||||
|       continue | ||||
|     endif | ||||
|     let index += 1 | ||||
|   endfor | ||||
|  | ||||
|   let curwinid = win_getid(winnr()) | ||||
|   call win_gotoid(s:gdbwin) | ||||
|  | ||||
|   " Add the output above the current prompt. | ||||
|   for line in lines | ||||
|     call append(line('$') - 1, line) | ||||
|   endfor | ||||
|   if !empty(lines) | ||||
|     set modified | ||||
|   endif | ||||
|  | ||||
|   call win_gotoid(curwinid) | ||||
|   call s:CommOutput(a:job_id, a:msgs, a:event) | ||||
| endfunc | ||||
|  | ||||
| " Decode a message from gdb.  quotedText starts with a ", return the text up | ||||
| " to the next ", unescaping characters. | ||||
| func s:DecodeMessage(quotedText) | ||||
| @@ -396,6 +582,19 @@ func s:EndDebugCommon() | ||||
|   au! TermDebug | ||||
| endfunc | ||||
|  | ||||
| func s:EndPromptDebug(job_id, exit_code, event) | ||||
|   let curwinid = win_getid(winnr()) | ||||
|   call win_gotoid(s:gdbwin) | ||||
|   close | ||||
|   if curwinid != s:gdbwin | ||||
|     call win_gotoid(curwinid) | ||||
|   endif | ||||
|  | ||||
|   call s:EndDebugCommon() | ||||
|   unlet s:gdbwin | ||||
|   "call ch_log("Returning from EndPromptDebug()") | ||||
| endfunc | ||||
|  | ||||
| func s:CommOutput(job_id, msgs, event) | ||||
|  | ||||
|   for msg in a:msgs | ||||
| @@ -436,7 +635,11 @@ func s:InstallCommands() | ||||
|   command Stop call s:SendCommand('-exec-interrupt') | ||||
|  | ||||
|   " using -exec-continue results in CTRL-C in gdb window not working | ||||
|   command Continue call jobsend(s:gdb_job_id, "continue\r") | ||||
|   if s:way == 'prompt' | ||||
|     command Continue call s:SendCommand('continue') | ||||
|   else | ||||
|     command Continue call chansend(s:gdb_job_id, "continue\r") | ||||
|   endif | ||||
|  | ||||
|   command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>) | ||||
|   command Gdb call win_gotoid(s:gdbwin) | ||||
| @@ -494,7 +697,11 @@ func s:SetBreakpoint() | ||||
|   let do_continue = 0 | ||||
|   if !s:stopped | ||||
|     let do_continue = 1 | ||||
|     call s:SendCommand('-exec-interrupt') | ||||
|     if s:way == 'prompt' | ||||
|       call s:PromptInterrupt() | ||||
|     else | ||||
|       call s:SendCommand('-exec-interrupt') | ||||
|     endif | ||||
|     sleep 10m | ||||
|   endif | ||||
|   " Use the fname:lnum format, older gdb can't handle --source. | ||||
|   | ||||
| @@ -763,6 +763,9 @@ static void free_buffer(buf_T *buf) | ||||
|   unref_var_dict(buf->b_vars); | ||||
|   aubuflocal_remove(buf); | ||||
|   tv_dict_unref(buf->additional_data); | ||||
|   xfree(buf->b_prompt_text); | ||||
|   callback_free(&buf->b_prompt_callback); | ||||
|   callback_free(&buf->b_prompt_interrupt); | ||||
|   clear_fmark(&buf->b_last_cursor); | ||||
|   clear_fmark(&buf->b_last_insert); | ||||
|   clear_fmark(&buf->b_last_change); | ||||
| @@ -1876,6 +1879,10 @@ buf_T * buflist_new(char_u *ffname, char_u *sfname, linenr_T lnum, int flags) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   buf->b_prompt_callback.type = kCallbackNone; | ||||
|   buf->b_prompt_interrupt.type = kCallbackNone; | ||||
|   buf->b_prompt_text = NULL; | ||||
|  | ||||
|   return buf; | ||||
| } | ||||
|  | ||||
| @@ -4824,6 +4831,12 @@ do_arg_all( | ||||
|   xfree(opened); | ||||
| } | ||||
|  | ||||
| // Return TRUE if "buf" is a prompt buffer. | ||||
| int bt_prompt(buf_T *buf) | ||||
| { | ||||
|     return buf != NULL && buf->b_p_bt[0] == 'p'; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Open a window for a number of buffers. | ||||
|  */ | ||||
| @@ -5218,14 +5231,18 @@ bool bt_nofile(const buf_T *const buf) | ||||
|   FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT | ||||
| { | ||||
|   return buf != NULL && ((buf->b_p_bt[0] == 'n' && buf->b_p_bt[2] == 'f') | ||||
|                          || buf->b_p_bt[0] == 'a' || buf->terminal); | ||||
|                          || buf->b_p_bt[0] == 'a' | ||||
|                          || buf->terminal | ||||
|                          || buf->b_p_bt[0] == 'p'); | ||||
| } | ||||
|  | ||||
| // Return true if "buf" is a "nowrite", "nofile" or "terminal" buffer. | ||||
| bool bt_dontwrite(const buf_T *const buf) | ||||
|   FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT | ||||
| { | ||||
|   return buf != NULL && (buf->b_p_bt[0] == 'n' || buf->terminal); | ||||
|   return buf != NULL && (buf->b_p_bt[0] == 'n' | ||||
|                          || buf->terminal | ||||
|                          || buf->b_p_bt[0] == 'p'); | ||||
| } | ||||
|  | ||||
| bool bt_dontwrite_msg(const buf_T *const buf) | ||||
| @@ -5278,6 +5295,9 @@ char_u *buf_spname(buf_T *buf) | ||||
|     if (buf->b_fname != NULL) { | ||||
|       return buf->b_fname; | ||||
|     } | ||||
|     if (bt_prompt(buf)) { | ||||
|       return (char_u *)_("[Prompt]"); | ||||
|     } | ||||
|     return (char_u *)_("[Scratch]"); | ||||
|   } | ||||
|   if (buf->b_fname == NULL) { | ||||
|   | ||||
| @@ -791,6 +791,12 @@ struct file_buffer { | ||||
|                                 // are not used!  Use the B_SPELL macro to | ||||
|                                 // access b_spell without #ifdef. | ||||
|  | ||||
|   char_u *b_prompt_text;        // set by prompt_setprompt() | ||||
|   Callback b_prompt_callback;   // set by prompt_setcallback() | ||||
|   Callback b_prompt_interrupt;  // set by prompt_setinterrupt() | ||||
|   int b_prompt_insert;          // value for restart_edit when entering | ||||
|                                 // a prompt buffer window. | ||||
|  | ||||
|   synblock_T b_s;               // Info related to syntax highlighting.  w_s | ||||
|                                 // normally points to this, but some windows | ||||
|                                 // may use a different synblock_T. | ||||
|   | ||||
| @@ -2432,6 +2432,10 @@ void nv_diffgetput(bool put, size_t count) | ||||
|   exarg_T ea; | ||||
|   char buf[30]; | ||||
|  | ||||
|   if (bt_prompt(curbuf)) { | ||||
|     vim_beep(BO_OPER); | ||||
|     return; | ||||
|   } | ||||
|   if (count == 0) { | ||||
|     ea.arg = (char_u *)""; | ||||
|   } else { | ||||
|   | ||||
| @@ -574,6 +574,12 @@ static int insert_check(VimState *state) | ||||
|     foldCheckClose(); | ||||
|   } | ||||
|  | ||||
|   int cmdchar_todo = s->cmdchar; | ||||
|   if (bt_prompt(curbuf)) { | ||||
|     init_prompt(cmdchar_todo); | ||||
|     cmdchar_todo = NUL; | ||||
|   } | ||||
|  | ||||
|   // If we inserted a character at the last position of the last line in the | ||||
|   // window, scroll the window one line up. This avoids an extra redraw.  This | ||||
|   // is detected when the cursor column is smaller after inserting something. | ||||
| @@ -817,6 +823,16 @@ static int insert_handle_key(InsertState *s) | ||||
|       s->nomove = true; | ||||
|       return 0;  // exit insert mode | ||||
|     } | ||||
|     if (s->c == Ctrl_C && bt_prompt(curbuf)) { | ||||
|       if (invoke_prompt_interrupt()) { | ||||
|         if (!bt_prompt(curbuf)) { | ||||
|           // buffer changed to a non-prompt buffer, get out of | ||||
|           // Insert mode | ||||
|           return 0; | ||||
|         } | ||||
|         break; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     // when 'insertmode' set, and not halfway through a mapping, don't leave | ||||
|     // Insert mode | ||||
| @@ -1143,6 +1159,15 @@ check_pum: | ||||
|       cmdwin_result = CAR; | ||||
|       return 0; | ||||
|     } | ||||
|     if (bt_prompt(curbuf)) { | ||||
|       invoke_prompt_callback(); | ||||
|       if (!bt_prompt(curbuf)) { | ||||
|         // buffer changed to a non-prompt buffer, get out of | ||||
|         // Insert mode | ||||
|         return 0; | ||||
|       } | ||||
|       break; | ||||
|     } | ||||
|     if (!ins_eol(s->c) && !p_im) { | ||||
|       return 0;  // out of memory | ||||
|     } | ||||
| @@ -1569,6 +1594,52 @@ void edit_putchar(int c, int highlight) | ||||
|   } | ||||
| } | ||||
|  | ||||
| // Return the effective prompt for the current buffer. | ||||
| char_u *prompt_text(void) | ||||
| { | ||||
|     if (curbuf->b_prompt_text == NULL) { | ||||
|       return (char_u *)"% "; | ||||
|     } | ||||
|     return curbuf->b_prompt_text; | ||||
| } | ||||
|  | ||||
| // Prepare for prompt mode: Make sure the last line has the prompt text. | ||||
| // Move the cursor to this line. | ||||
| static void init_prompt(int cmdchar_todo) | ||||
| { | ||||
|   char_u *prompt = prompt_text(); | ||||
|   char_u *text; | ||||
|  | ||||
|   curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; | ||||
|   text = get_cursor_line_ptr(); | ||||
|   if (STRNCMP(text, prompt, STRLEN(prompt)) != 0) { | ||||
|     // prompt is missing, insert it or append a line with it | ||||
|     if (*text == NUL) { | ||||
|       ml_replace(curbuf->b_ml.ml_line_count, prompt, true); | ||||
|     } else { | ||||
|       ml_append(curbuf->b_ml.ml_line_count, prompt, 0, false); | ||||
|     } | ||||
|     curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; | ||||
|     coladvance((colnr_T)MAXCOL); | ||||
|     changed_bytes(curbuf->b_ml.ml_line_count, 0); | ||||
|   } | ||||
|   if (cmdchar_todo == 'A') { | ||||
|     coladvance((colnr_T)MAXCOL); | ||||
|   } | ||||
|   if (cmdchar_todo == 'I' || curwin->w_cursor.col <= (int)STRLEN(prompt)) { | ||||
|     curwin->w_cursor.col = STRLEN(prompt); | ||||
|   } | ||||
|   // Make sure the cursor is in a valid position. | ||||
|   check_cursor(); | ||||
| } | ||||
|  | ||||
| // Return TRUE if the cursor is in the editable position of the prompt line. | ||||
| int prompt_curpos_editable(void) | ||||
| { | ||||
|     return curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count | ||||
|         && curwin->w_cursor.col >= (int)STRLEN(prompt_text()); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Undo the previous edit_putchar(). | ||||
|  */ | ||||
| @@ -8161,10 +8232,14 @@ static void ins_mouse(int c) | ||||
|     win_T   *new_curwin = curwin; | ||||
|  | ||||
|     if (curwin != old_curwin && win_valid(old_curwin)) { | ||||
|       /* Mouse took us to another window.  We need to go back to the | ||||
|        * previous one to stop insert there properly. */ | ||||
|       // Mouse took us to another window.  We need to go back to the | ||||
|       // previous one to stop insert there properly. | ||||
|       curwin = old_curwin; | ||||
|       curbuf = curwin->w_buffer; | ||||
|       if (bt_prompt(curbuf)) { | ||||
|         // Restart Insert mode when re-entering the prompt buffer. | ||||
|         curbuf->b_prompt_insert = 'A'; | ||||
|       } | ||||
|     } | ||||
|     start_arrow(curwin == old_curwin ? &tpos : NULL); | ||||
|     if (curwin != new_curwin && win_valid(new_curwin)) { | ||||
|   | ||||
| @@ -5152,6 +5152,10 @@ bool garbage_collect(bool testing) | ||||
|     } | ||||
|     // buffer ShaDa additional data | ||||
|     ABORTING(set_ref_dict)(buf->additional_data, copyID); | ||||
|  | ||||
|     // buffer callback functions | ||||
|     set_ref_in_callback(&buf->b_prompt_callback, copyID, NULL, NULL); | ||||
|     set_ref_in_callback(&buf->b_prompt_interrupt, copyID, NULL, NULL); | ||||
|   } | ||||
|  | ||||
|   FOR_ALL_TAB_WINDOWS(tp, wp) { | ||||
| @@ -7315,9 +7319,7 @@ dict_T *get_win_info(win_T *wp, int16_t tpnr, int16_t winnr) | ||||
|   return dict; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Find window specified by "vp" in tabpage "tp". | ||||
|  */ | ||||
| // Find window specified by "vp" in tabpage "tp". | ||||
| win_T * | ||||
| find_win_by_nr( | ||||
|     typval_T *vp, | ||||
| @@ -13687,3 +13689,51 @@ void ex_checkhealth(exarg_T *eap) | ||||
|  | ||||
|   xfree(buf); | ||||
| } | ||||
|  | ||||
| void invoke_prompt_callback(void) | ||||
| { | ||||
|     typval_T rettv; | ||||
|     typval_T argv[2]; | ||||
|     char_u *text; | ||||
|     char_u *prompt; | ||||
|     linenr_T lnum = curbuf->b_ml.ml_line_count; | ||||
|  | ||||
|     // Add a new line for the prompt before invoking the callback, so that | ||||
|     // text can always be inserted above the last line. | ||||
|     ml_append(lnum, (char_u  *)"", 0, false); | ||||
|     curwin->w_cursor.lnum = lnum + 1; | ||||
|     curwin->w_cursor.col = 0; | ||||
|  | ||||
|     if (curbuf->b_prompt_callback.type == kCallbackNone) { | ||||
|       return; | ||||
|     } | ||||
|     text = ml_get(lnum); | ||||
|     prompt = prompt_text(); | ||||
|     if (STRLEN(text) >= STRLEN(prompt)) { | ||||
|       text += STRLEN(prompt); | ||||
|     } | ||||
|     argv[0].v_type = VAR_STRING; | ||||
|     argv[0].vval.v_string = vim_strsave(text); | ||||
|     argv[1].v_type = VAR_UNKNOWN; | ||||
|  | ||||
|     callback_call(&curbuf->b_prompt_callback, 1, argv, &rettv); | ||||
|     tv_clear(&argv[0]); | ||||
|     tv_clear(&rettv); | ||||
| } | ||||
|  | ||||
| // Return true When the interrupt callback was invoked. | ||||
| bool invoke_prompt_interrupt(void) | ||||
| { | ||||
|     typval_T rettv; | ||||
|     typval_T argv[1]; | ||||
|  | ||||
|     if (curbuf->b_prompt_interrupt.type == kCallbackNone) { | ||||
|       return false; | ||||
|     } | ||||
|     argv[0].v_type = VAR_UNKNOWN; | ||||
|  | ||||
|     got_int = false;  // don't skip executing commands | ||||
|     callback_call(&curbuf->b_prompt_interrupt, 0, argv, &rettv); | ||||
|     tv_clear(&rettv); | ||||
|     return true; | ||||
| } | ||||
|   | ||||
| @@ -82,6 +82,7 @@ return { | ||||
|     ctxset={args={1, 2}}, | ||||
|     ctxsize={}, | ||||
|     cursor={args={1, 3}}, | ||||
|     debugbreak={args={1, 1}}, | ||||
|     deepcopy={args={1, 2}}, | ||||
|     delete={args={1,2}}, | ||||
|     deletebufline={args={2,3}}, | ||||
| @@ -243,6 +244,9 @@ return { | ||||
|     pow={args=2}, | ||||
|     prevnonblank={args=1}, | ||||
|     printf={args=varargs(1)}, | ||||
|     prompt_setcallback={args={2, 2}}, | ||||
|     prompt_setinterrupt={args={2, 2}}, | ||||
|     prompt_setprompt={args={2, 2}}, | ||||
|     pum_getpos={}, | ||||
|     pumvisible={}, | ||||
|     py3eval={args=1}, | ||||
|   | ||||
| @@ -1408,9 +1408,31 @@ static void f_cursor(typval_T *argvars, typval_T *rettv, FunPtr fptr) | ||||
|   rettv->vval.v_number = 0; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * "deepcopy()" function | ||||
|  */ | ||||
| // "debugbreak()" function | ||||
| static void f_debugbreak(typval_T *argvars, typval_T *rettv, FunPtr fptr) | ||||
| { | ||||
|   int pid; | ||||
|  | ||||
|   rettv->vval.v_number = FAIL; | ||||
|   pid = (int)tv_get_number(&argvars[0]); | ||||
|   if (pid == 0) { | ||||
|     EMSG(_(e_invarg)); | ||||
|   } else { | ||||
| #ifdef WIN32 | ||||
|     HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, pid); | ||||
|  | ||||
|     if (hProcess != NULL) { | ||||
|       DebugBreakProcess(hProcess); | ||||
|       CloseHandle(hProcess); | ||||
|       rettv->vval.v_number = OK; | ||||
|     } | ||||
| #else | ||||
|     uv_kill(pid, SIGINT); | ||||
| #endif | ||||
|   } | ||||
| } | ||||
|  | ||||
| // "deepcopy()" function | ||||
| static void f_deepcopy(typval_T *argvars, typval_T *rettv, FunPtr fptr) | ||||
| { | ||||
|   int noref = 0; | ||||
| @@ -6076,6 +6098,76 @@ static void f_printf(typval_T *argvars, typval_T *rettv, FunPtr fptr) | ||||
|   } | ||||
| } | ||||
|  | ||||
| // "prompt_setcallback({buffer}, {callback})" function | ||||
| static void f_prompt_setcallback(typval_T *argvars, | ||||
|                                  typval_T *rettv, FunPtr fptr) | ||||
| { | ||||
|     buf_T *buf; | ||||
|     Callback prompt_callback = { .type = kCallbackNone }; | ||||
|  | ||||
|     if (check_secure()) { | ||||
|       return; | ||||
|     } | ||||
|     buf = tv_get_buf(&argvars[0], false); | ||||
|     if (buf == NULL) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     if (argvars[1].v_type != VAR_STRING || *argvars[1].vval.v_string != NUL) { | ||||
|       if (!callback_from_typval(&prompt_callback, &argvars[1])) { | ||||
|         return; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     callback_free(&buf->b_prompt_callback); | ||||
|     buf->b_prompt_callback = prompt_callback; | ||||
| } | ||||
|  | ||||
| // "prompt_setinterrupt({buffer}, {callback})" function | ||||
| static void f_prompt_setinterrupt(typval_T *argvars, | ||||
|                                   typval_T *rettv, FunPtr fptr) | ||||
| { | ||||
|     buf_T *buf; | ||||
|     Callback interrupt_callback = { .type = kCallbackNone }; | ||||
|  | ||||
|     if (check_secure()) { | ||||
|       return; | ||||
|     } | ||||
|     buf = tv_get_buf(&argvars[0], false); | ||||
|     if (buf == NULL) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     if (argvars[1].v_type != VAR_STRING || *argvars[1].vval.v_string != NUL) { | ||||
|       if (!callback_from_typval(&interrupt_callback, &argvars[1])) { | ||||
|         return; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     callback_free(&buf->b_prompt_interrupt); | ||||
|     buf->b_prompt_interrupt= interrupt_callback; | ||||
| } | ||||
|  | ||||
| // "prompt_setprompt({buffer}, {text})" function | ||||
| static void f_prompt_setprompt(typval_T *argvars, | ||||
|                                typval_T *rettv, FunPtr fptr) | ||||
| { | ||||
|     buf_T *buf; | ||||
|     const char_u *text; | ||||
|  | ||||
|     if (check_secure()) { | ||||
|       return; | ||||
|     } | ||||
|     buf = tv_get_buf(&argvars[0], false); | ||||
|     if (buf == NULL) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     text = (const char_u *)tv_get_string(&argvars[1]); | ||||
|     xfree(buf->b_prompt_text); | ||||
|     buf->b_prompt_text = vim_strsave(text); | ||||
| } | ||||
|  | ||||
| // "pum_getpos()" function | ||||
| static void f_pum_getpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) | ||||
| { | ||||
|   | ||||
| @@ -3642,7 +3642,9 @@ static void nv_help(cmdarg_T *cap) | ||||
|  */ | ||||
| static void nv_addsub(cmdarg_T *cap) | ||||
| { | ||||
|   if (!VIsual_active && cap->oap->op_type == OP_NOP) { | ||||
|   if (bt_prompt(curbuf) && !prompt_curpos_editable()) { | ||||
|     clearopbeep(cap->oap); | ||||
|   } else if (!VIsual_active && cap->oap->op_type == OP_NOP) { | ||||
|     prep_redo_cmd(cap); | ||||
|     cap->oap->op_type = cap->cmdchar == Ctrl_A ? OP_NR_ADD : OP_NR_SUB; | ||||
|     op_addsub(cap->oap, cap->count1, cap->arg); | ||||
| @@ -5239,6 +5241,13 @@ static void nv_down(cmdarg_T *cap) | ||||
|     // In the cmdline window a <CR> executes the command. | ||||
|     if (cmdwin_type != 0 && cap->cmdchar == CAR) { | ||||
|       cmdwin_result = CAR; | ||||
|     } else if (bt_prompt(curbuf) && cap->cmdchar == CAR | ||||
|                && curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count) { | ||||
|       // In a prompt buffer a <CR> in the last line invokes the callback. | ||||
|       invoke_prompt_callback(); | ||||
|       if (restart_edit == 0) { | ||||
|         restart_edit = 'a'; | ||||
|       } | ||||
|     } else { | ||||
|       cap->oap->motion_type = kMTLineWise; | ||||
|       if (cursor_down(cap->count1, cap->oap->op_type == OP_NOP) == false) { | ||||
| @@ -5831,6 +5840,10 @@ static void nv_undo(cmdarg_T *cap) | ||||
| static void nv_kundo(cmdarg_T *cap) | ||||
| { | ||||
|   if (!checkclearopq(cap->oap)) { | ||||
|     if (bt_prompt(curbuf)) { | ||||
|       clearopbeep(cap->oap); | ||||
|       return; | ||||
|     } | ||||
|     u_undo((int)cap->count1); | ||||
|     curwin->w_set_curswant = true; | ||||
|   } | ||||
| @@ -5844,8 +5857,13 @@ static void nv_replace(cmdarg_T *cap) | ||||
|   char_u      *ptr; | ||||
|   int had_ctrl_v; | ||||
|  | ||||
|   if (checkclearop(cap->oap)) | ||||
|   if (checkclearop(cap->oap)) { | ||||
|     return; | ||||
|   } | ||||
|   if (bt_prompt(curbuf) && !prompt_curpos_editable()) { | ||||
|     clearopbeep(cap->oap); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   /* get another character */ | ||||
|   if (cap->nchar == Ctrl_V) { | ||||
| @@ -6182,7 +6200,11 @@ static void v_visop(cmdarg_T *cap) | ||||
|  */ | ||||
| static void nv_subst(cmdarg_T *cap) | ||||
| { | ||||
|   if (VIsual_active) {  /* "vs" and "vS" are the same as "vc" */ | ||||
|   if (bt_prompt(curbuf) && !prompt_curpos_editable()) { | ||||
|     clearopbeep(cap->oap); | ||||
|     return; | ||||
|   } | ||||
|   if (VIsual_active) {  // "vs" and "vS" are the same as "vc" | ||||
|     if (cap->cmdchar == 'S') { | ||||
|       VIsual_mode_orig = VIsual_mode; | ||||
|       VIsual_mode = 'V'; | ||||
| @@ -7120,10 +7142,15 @@ static void nv_tilde(cmdarg_T *cap) | ||||
| { | ||||
|   if (!p_to | ||||
|       && !VIsual_active | ||||
|       && cap->oap->op_type != OP_TILDE) | ||||
|       && cap->oap->op_type != OP_TILDE) { | ||||
|     if (bt_prompt(curbuf) && !prompt_curpos_editable()) { | ||||
|       clearopbeep(cap->oap); | ||||
|       return; | ||||
|     } | ||||
|     n_swapchar(cap); | ||||
|   else | ||||
|   } else { | ||||
|     nv_operator(cap); | ||||
|   } | ||||
| } | ||||
|  | ||||
| /* | ||||
| @@ -7136,6 +7163,12 @@ static void nv_operator(cmdarg_T *cap) | ||||
|  | ||||
|   op_type = get_op_type(cap->cmdchar, cap->nchar); | ||||
|  | ||||
|   if (bt_prompt(curbuf) && op_is_change(op_type) | ||||
|       && !prompt_curpos_editable()) { | ||||
|     clearopbeep(cap->oap); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   if (op_type == cap->oap->op_type)         /* double operator works on lines */ | ||||
|     nv_lineop(cap); | ||||
|   else if (!checkclearop(cap->oap)) { | ||||
| @@ -7796,8 +7829,11 @@ static void nv_put_opt(cmdarg_T *cap, bool fix_indent) | ||||
|       clearop(cap->oap); | ||||
|       assert(cap->opcount >= 0); | ||||
|       nv_diffgetput(true, (size_t)cap->opcount); | ||||
|     } else | ||||
|     } else { | ||||
|       clearopbeep(cap->oap); | ||||
|     } | ||||
|   } else if (bt_prompt(curbuf) && !prompt_curpos_editable()) { | ||||
|     clearopbeep(cap->oap); | ||||
|   } else { | ||||
|     if (fix_indent) { | ||||
|       dir = (cap->cmdchar == ']' && cap->nchar == 'p') | ||||
| @@ -7809,8 +7845,9 @@ static void nv_put_opt(cmdarg_T *cap, bool fix_indent) | ||||
|         ? BACKWARD : FORWARD; | ||||
|     } | ||||
|     prep_redo_cmd(cap); | ||||
|     if (cap->cmdchar == 'g') | ||||
|     if (cap->cmdchar == 'g') { | ||||
|       flags |= PUT_CURSEND; | ||||
|     } | ||||
|  | ||||
|     if (VIsual_active) { | ||||
|       /* Putting in Visual mode: The put text replaces the selected | ||||
| @@ -7916,10 +7953,14 @@ static void nv_open(cmdarg_T *cap) | ||||
|     clearop(cap->oap); | ||||
|     assert(cap->opcount >= 0); | ||||
|     nv_diffgetput(false, (size_t)cap->opcount); | ||||
|   } else if (VIsual_active) /* switch start and end of visual */ | ||||
|   } else if (VIsual_active) { | ||||
|     // switch start and end of visual/ | ||||
|     v_swap_corners(cap->cmdchar); | ||||
|   else | ||||
|   } else if (bt_prompt(curbuf)) { | ||||
|     clearopbeep(cap->oap); | ||||
|   } else { | ||||
|     n_opencmd(cap); | ||||
|   } | ||||
| } | ||||
|  | ||||
| // Calculate start/end virtual columns for operating in block mode. | ||||
|   | ||||
| @@ -89,6 +89,10 @@ struct block_def { | ||||
| # include "ops.c.generated.h" | ||||
| #endif | ||||
|  | ||||
| // Flags for third item in "opchars". | ||||
| #define OPF_LINES  1  // operator always works on lines | ||||
| #define OPF_CHANGE 2  // operator changes text | ||||
|  | ||||
| /* | ||||
|  * The names of operators. | ||||
|  * IMPORTANT: Index must correspond with defines in vim.h!!! | ||||
| @@ -96,36 +100,36 @@ struct block_def { | ||||
|  */ | ||||
| static char opchars[][3] = | ||||
| { | ||||
|   { NUL,    NUL, false },    // OP_NOP | ||||
|   { 'd',    NUL, false },    // OP_DELETE | ||||
|   { 'y',    NUL, false },    // OP_YANK | ||||
|   { 'c',    NUL, false },    // OP_CHANGE | ||||
|   { '<',    NUL, true },     // OP_LSHIFT | ||||
|   { '>',    NUL, true },     // OP_RSHIFT | ||||
|   { '!',    NUL, true },     // OP_FILTER | ||||
|   { 'g',    '~', false },    // OP_TILDE | ||||
|   { '=',    NUL, true },     // OP_INDENT | ||||
|   { 'g',    'q', true },     // OP_FORMAT | ||||
|   { ':',    NUL, true },     // OP_COLON | ||||
|   { 'g',    'U', false },    // OP_UPPER | ||||
|   { 'g',    'u', false },    // OP_LOWER | ||||
|   { 'J',    NUL, true },     // DO_JOIN | ||||
|   { 'g',    'J', true },     // DO_JOIN_NS | ||||
|   { 'g',    '?', false },    // OP_ROT13 | ||||
|   { 'r',    NUL, false },    // OP_REPLACE | ||||
|   { 'I',    NUL, false },    // OP_INSERT | ||||
|   { 'A',    NUL, false },    // OP_APPEND | ||||
|   { 'z',    'f', true },     // OP_FOLD | ||||
|   { 'z',    'o', true },     // OP_FOLDOPEN | ||||
|   { 'z',    'O', true },     // OP_FOLDOPENREC | ||||
|   { 'z',    'c', true },     // OP_FOLDCLOSE | ||||
|   { 'z',    'C', true },     // OP_FOLDCLOSEREC | ||||
|   { 'z',    'd', true },     // OP_FOLDDEL | ||||
|   { 'z',    'D', true },     // OP_FOLDDELREC | ||||
|   { 'g',    'w', true },     // OP_FORMAT2 | ||||
|   { 'g',    '@', false },    // OP_FUNCTION | ||||
|   { Ctrl_A, NUL, false },    // OP_NR_ADD | ||||
|   { Ctrl_X, NUL, false },    // OP_NR_SUB | ||||
|   { NUL, NUL, 0 },                       // OP_NOP | ||||
|   { 'd', NUL, OPF_CHANGE },              // OP_DELETE | ||||
|   { 'y', NUL, 0 },                       // OP_YANK | ||||
|   { 'c', NUL, OPF_CHANGE },              // OP_CHANGE | ||||
|   { '<', NUL, OPF_LINES | OPF_CHANGE },  // OP_LSHIFT | ||||
|   { '>', NUL, OPF_LINES | OPF_CHANGE },  // OP_RSHIFT | ||||
|   { '!', NUL, OPF_LINES | OPF_CHANGE },  // OP_FILTER | ||||
|   { 'g', '~', OPF_CHANGE },              // OP_TILDE | ||||
|   { '=', NUL, OPF_LINES | OPF_CHANGE },  // OP_INDENT | ||||
|   { 'g', 'q', OPF_LINES | OPF_CHANGE },  // OP_FORMAT | ||||
|   { ':', NUL, OPF_LINES },               // OP_COLON | ||||
|   { 'g', 'U', OPF_CHANGE },              // OP_UPPER | ||||
|   { 'g', 'u', OPF_CHANGE },              // OP_LOWER | ||||
|   { 'J', NUL, OPF_LINES | OPF_CHANGE },  // DO_JOIN | ||||
|   { 'g', 'J', OPF_LINES | OPF_CHANGE },  // DO_JOIN_NS | ||||
|   { 'g', '?', OPF_CHANGE },              // OP_ROT13 | ||||
|   { 'r', NUL, OPF_CHANGE },              // OP_REPLACE | ||||
|   { 'I', NUL, OPF_CHANGE },              // OP_INSERT | ||||
|   { 'A', NUL, OPF_CHANGE },              // OP_APPEND | ||||
|   { 'z', 'f', OPF_LINES },               // OP_FOLD | ||||
|   { 'z', 'o', OPF_LINES },               // OP_FOLDOPEN | ||||
|   { 'z', 'O', OPF_LINES },               // OP_FOLDOPENREC | ||||
|   { 'z', 'c', OPF_LINES },               // OP_FOLDCLOSE | ||||
|   { 'z', 'C', OPF_LINES },               // OP_FOLDCLOSEREC | ||||
|   { 'z', 'd', OPF_LINES },               // OP_FOLDDEL | ||||
|   { 'z', 'D', OPF_LINES },               // OP_FOLDDELREC | ||||
|   { 'g', 'w', OPF_LINES | OPF_CHANGE },  // OP_FORMAT2 | ||||
|   { 'g', '@', OPF_CHANGE },              // OP_FUNCTION | ||||
|   { Ctrl_A, NUL, OPF_CHANGE },           // OP_NR_ADD | ||||
|   { Ctrl_X, NUL, OPF_CHANGE },           // OP_NR_SUB | ||||
| }; | ||||
|  | ||||
| /* | ||||
| @@ -169,7 +173,13 @@ int get_op_type(int char1, int char2) | ||||
|  */ | ||||
| int op_on_lines(int op) | ||||
| { | ||||
|   return opchars[op][2]; | ||||
|   return opchars[op][2] & OPF_LINES; | ||||
| } | ||||
|  | ||||
| // Return TRUE if operator "op" changes text. | ||||
| int op_is_change(int op) | ||||
| { | ||||
|     return opchars[op][2] & OPF_CHANGE; | ||||
| } | ||||
|  | ||||
| /* | ||||
|   | ||||
| @@ -299,7 +299,8 @@ static char *(p_scbopt_values[]) =    { "ver", "hor", "jump", NULL }; | ||||
| static char *(p_debug_values[]) =     { "msg", "throw", "beep", NULL }; | ||||
| static char *(p_ead_values[]) =       { "both", "ver", "hor", NULL }; | ||||
| static char *(p_buftype_values[]) =   { "nofile", "nowrite", "quickfix", | ||||
|                                         "help", "acwrite", "terminal", NULL }; | ||||
|                                         "help", "acwrite", "terminal", | ||||
|                                         "prompt", NULL }; | ||||
|  | ||||
| static char *(p_bufhidden_values[]) = { "hide", "unload", "delete", | ||||
|                                         "wipe", NULL }; | ||||
| @@ -7091,10 +7092,13 @@ static int check_opt_wim(void) | ||||
|  */ | ||||
| bool can_bs(int what) | ||||
| { | ||||
|   if (what == BS_START && bt_prompt(curbuf)) { | ||||
|     return false; | ||||
|   } | ||||
|   switch (*p_bs) { | ||||
|   case '2':       return true; | ||||
|   case '1':       return what != BS_START; | ||||
|   case '0':       return false; | ||||
|     case '2':       return true; | ||||
|     case '1':       return what != BS_START; | ||||
|     case '0':       return false; | ||||
|   } | ||||
|   return vim_strchr(p_bs, what) != NULL; | ||||
| } | ||||
|   | ||||
| @@ -2971,7 +2971,10 @@ static char_u *u_save_line(linenr_T lnum) | ||||
| bool bufIsChanged(buf_T *buf) | ||||
|   FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT | ||||
| { | ||||
|   return !bt_dontwrite(buf) && (buf->b_changed || file_ff_differs(buf, true)); | ||||
|   // In a "prompt" buffer we do respect 'modified', so that we can control | ||||
|   // closing the window by setting or resetting that option. | ||||
|   return  (!bt_dontwrite(buf) || bt_prompt(buf)) | ||||
|     && (buf->b_changed || file_ff_differs(buf, true)); | ||||
| } | ||||
|  | ||||
| // Return true if any buffer has changes.  Also buffers that are not written. | ||||
|   | ||||
							
								
								
									
										153
									
								
								test/functional/legacy/prompt_buffer_spec.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								test/functional/legacy/prompt_buffer_spec.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,153 @@ | ||||
| local helpers = require('test.functional.helpers')(after_each) | ||||
| local Screen = require('test.functional.ui.screen') | ||||
| local feed= helpers.feed | ||||
| local source = helpers.source | ||||
| local clear = helpers.clear | ||||
| local feed_command = helpers.feed_command | ||||
|  | ||||
| describe('prompt buffer', function() | ||||
|   local screen | ||||
|  | ||||
|   before_each(function() | ||||
|     clear() | ||||
|     screen = Screen.new(25, 10) | ||||
|     screen:attach() | ||||
|     source([[ | ||||
|       func TextEntered(text) | ||||
|         if a:text == "exit" | ||||
|           set nomodified | ||||
|           stopinsert | ||||
|           close | ||||
|         else | ||||
|           call append(line("$") - 1, 'Command: "' . a:text . '"') | ||||
|           set nomodfied | ||||
|           call timer_start(20, {id -> TimerFunc(a:text)}) | ||||
|         endif | ||||
|       endfunc | ||||
|  | ||||
|       func TimerFunc(text) | ||||
|         call append(line("$") - 1, 'Result: "' . a:text .'"') | ||||
|       endfunc | ||||
|     ]]) | ||||
|     feed_command("set noshowmode | set laststatus=0") | ||||
|     feed_command("call setline(1, 'other buffer')") | ||||
|     feed_command("new") | ||||
|     feed_command("set buftype=prompt") | ||||
|     feed_command("call prompt_setcallback(bufnr(''), function('TextEntered'))") | ||||
|   end) | ||||
|  | ||||
|   after_each(function() | ||||
|     screen:detach() | ||||
|   end) | ||||
|  | ||||
|   it('works', function() | ||||
|     screen:expect([[ | ||||
|       ^                         | | ||||
|       ~                        | | ||||
|       ~                        | | ||||
|       ~                        | | ||||
|       [Prompt]                 | | ||||
|       other buffer             | | ||||
|       ~                        | | ||||
|       ~                        | | ||||
|       ~                        | | ||||
|                                | | ||||
|     ]]) | ||||
|     feed("i") | ||||
|     feed("hello\n") | ||||
|     screen:expect([[ | ||||
|       % hello                  | | ||||
|       Command: "hello"         | | ||||
|       Result: "hello"          | | ||||
|       % ^                       | | ||||
|       [Prompt] [+]             | | ||||
|       other buffer             | | ||||
|       ~                        | | ||||
|       ~                        | | ||||
|       ~                        | | ||||
|                                | | ||||
|     ]]) | ||||
|     feed("exit\n") | ||||
|     screen:expect([[ | ||||
|       ^other buffer             | | ||||
|       ~                        | | ||||
|       ~                        | | ||||
|       ~                        | | ||||
|       ~                        | | ||||
|       ~                        | | ||||
|       ~                        | | ||||
|       ~                        | | ||||
|       ~                        | | ||||
|                                | | ||||
|     ]]) | ||||
|   end) | ||||
|  | ||||
|   it('editing', function() | ||||
|     screen:expect([[ | ||||
|       ^                         | | ||||
|       ~                        | | ||||
|       ~                        | | ||||
|       ~                        | | ||||
|       [Prompt]                 | | ||||
|       other buffer             | | ||||
|       ~                        | | ||||
|       ~                        | | ||||
|       ~                        | | ||||
|                                | | ||||
|     ]]) | ||||
|     feed("i") | ||||
|     feed("hello<BS><BS>") | ||||
|     screen:expect([[ | ||||
|       % hel^                    | | ||||
|       ~                        | | ||||
|       ~                        | | ||||
|       ~                        | | ||||
|       [Prompt] [+]             | | ||||
|       other buffer             | | ||||
|       ~                        | | ||||
|       ~                        | | ||||
|       ~                        | | ||||
|                                | | ||||
|     ]]) | ||||
|     feed("<Left><Left><Left><BS>-") | ||||
|     screen:expect([[ | ||||
|       % -^hel                   | | ||||
|       ~                        | | ||||
|       ~                        | | ||||
|       ~                        | | ||||
|       [Prompt] [+]             | | ||||
|       other buffer             | | ||||
|       ~                        | | ||||
|       ~                        | | ||||
|       ~                        | | ||||
|                                | | ||||
|     ]]) | ||||
|     feed("<End>x") | ||||
|     screen:expect([[ | ||||
|       % -helx^                  | | ||||
|       ~                        | | ||||
|       ~                        | | ||||
|       ~                        | | ||||
|       [Prompt] [+]             | | ||||
|       other buffer             | | ||||
|       ~                        | | ||||
|       ~                        | | ||||
|       ~                        | | ||||
|                                | | ||||
|     ]]) | ||||
|     feed("<C-U>exit\n") | ||||
|     screen:expect([[ | ||||
|       ^other buffer             | | ||||
|       ~                        | | ||||
|       ~                        | | ||||
|       ~                        | | ||||
|       ~                        | | ||||
|       ~                        | | ||||
|       ~                        | | ||||
|       ~                        | | ||||
|       ~                        | | ||||
|                                | | ||||
|     ]]) | ||||
|   end) | ||||
|  | ||||
| end) | ||||
		Reference in New Issue
	
	Block a user
	 Justin M. Keyes
					Justin M. Keyes