Mercurial > vim
view src/ui.c @ 33000:bc3b293a965c v9.0.1792
patch 9.0.1792: problem with gj/gk/gM and virtual text
Commit: https://github.com/vim/vim/commit/d809c0a90387a23aed21ba37d0b65332fb5dafe7
Author: zeertzjq <zeertzjq@outlook.com>
Date: Sun Aug 27 11:17:39 2023 +0200
patch 9.0.1792: problem with gj/gk/gM and virtual text
Problem: Normal mode "gM", "gj", "gk" commands behave incorrectly with
virtual text.
Solution: Use linetabsize() instead of linetabsize_str().
closes: #12909
Signed-off-by: Christian Brabandt <cb@256bit.org>
Co-authored-by: zeertzjq <zeertzjq@outlook.com>
author | Christian Brabandt <cb@256bit.org> |
---|---|
date | Sun, 27 Aug 2023 11:30:08 +0200 |
parents | 1009c33499e7 |
children | 664ee4c0daca |
line wrap: on
line source
/* vi:set ts=8 sts=4 sw=4 noet: * * VIM - Vi IMproved by Bram Moolenaar * * Do ":help uganda" in Vim to read copying and usage conditions. * Do ":help credits" in Vim to see a list of people who contributed. * See README.txt for an overview of the Vim source code. */ /* * ui.c: functions that handle the user interface. * 1. Keyboard input stuff, and a bit of windowing stuff. These are called * before the machine specific stuff (mch_*) so that we can call the GUI * stuff instead if the GUI is running. * 2. Input buffer stuff. */ #include "vim.h" void ui_write(char_u *s, int len, int console UNUSED) { #ifdef FEAT_GUI if (gui.in_use && !gui.dying && !gui.starting # ifndef NO_CONSOLE && !console # endif ) { gui_write(s, len); if (p_wd) gui_wait_for_chars(p_wd, typebuf.tb_change_cnt); return; } #endif #ifndef NO_CONSOLE // Don't output anything in silent mode ("ex -s") unless 'verbose' set if (!(silent_mode && p_verbose == 0)) { # if !defined(MSWIN) char_u *tofree = NULL; if (output_conv.vc_type != CONV_NONE) { // Convert characters from 'encoding' to 'termencoding'. tofree = string_convert(&output_conv, s, &len); if (tofree != NULL) s = tofree; } # endif mch_write(s, len); # if defined(HAVE_FSYNC) if (console && s[len - 1] == '\n') vim_fsync(1); # endif # if !defined(MSWIN) if (output_conv.vc_type != CONV_NONE) vim_free(tofree); # endif } #endif } #if defined(UNIX) || defined(VMS) || defined(PROTO) || defined(MSWIN) /* * When executing an external program, there may be some typed characters that * are not consumed by it. Give them back to ui_inchar() and they are stored * here for the next call. */ static char_u *ta_str = NULL; static int ta_off; // offset for next char to use when ta_str != NULL static int ta_len; // length of ta_str when it's not NULL void ui_inchar_undo(char_u *s, int len) { char_u *new; int newlen; newlen = len; if (ta_str != NULL) newlen += ta_len - ta_off; new = alloc(newlen); if (new == NULL) return; if (ta_str != NULL) { mch_memmove(new, ta_str + ta_off, (size_t)(ta_len - ta_off)); mch_memmove(new + ta_len - ta_off, s, (size_t)len); vim_free(ta_str); } else mch_memmove(new, s, (size_t)len); ta_str = new; ta_len = newlen; ta_off = 0; } #endif /* * ui_inchar(): low level input function. * Get characters from the keyboard. * Return the number of characters that are available. * If "wtime" == 0 do not wait for characters. * If "wtime" == -1 wait forever for characters. * If "wtime" > 0 wait "wtime" milliseconds for a character. * * "tb_change_cnt" is the value of typebuf.tb_change_cnt if "buf" points into * it. When typebuf.tb_change_cnt changes (e.g., when a message is received * from a remote client) "buf" can no longer be used. "tb_change_cnt" is NULL * otherwise. */ int ui_inchar( char_u *buf, int maxlen, long wtime, // don't use "time", MIPS cannot handle it int tb_change_cnt) { int retval = 0; #if defined(FEAT_GUI) && (defined(UNIX) || defined(VMS)) /* * Use the typeahead if there is any. */ if (ta_str != NULL) { if (maxlen >= ta_len - ta_off) { mch_memmove(buf, ta_str + ta_off, (size_t)ta_len); VIM_CLEAR(ta_str); return ta_len; } mch_memmove(buf, ta_str + ta_off, (size_t)maxlen); ta_off += maxlen; return maxlen; } #endif #ifdef FEAT_PROFILE if (do_profiling == PROF_YES && wtime != 0) prof_inchar_enter(); #endif #ifdef NO_CONSOLE_INPUT // Don't wait for character input when the window hasn't been opened yet. // Do try reading, this works when redirecting stdin from a file. // Must return something, otherwise we'll loop forever. If we run into // this very often we probably got stuck, exit Vim. if (no_console_input()) { static int count = 0; # ifndef NO_CONSOLE retval = mch_inchar(buf, maxlen, wtime, tb_change_cnt); if (retval > 0 || typebuf_changed(tb_change_cnt) || wtime >= 0) goto theend; # endif if (wtime == -1 && ++count == 1000) read_error_exit(); buf[0] = CAR; retval = 1; goto theend; } #endif // If we are going to wait for some time or block... if (wtime == -1 || wtime > 100L) { // ... allow signals to kill us. (void)vim_handle_signal(SIGNAL_UNBLOCK); // ... there is no need for CTRL-C to interrupt something, don't let // it set got_int when it was mapped. if ((mapped_ctrl_c | curbuf->b_mapped_ctrl_c) & get_real_state()) ctrl_c_interrupts = FALSE; } /* * Here we call gui_inchar() or mch_inchar(), the GUI or machine-dependent * input function. The functionality they implement is like this: * * while (not timed out) * { * handle-resize; * parse-queued-messages; * if (waited for 'updatetime') * trigger-cursorhold; * ui_wait_for_chars_or_timer() * if (character available) * break; * } * * ui_wait_for_chars_or_timer() does: * * while (not timed out) * { * if (any-timer-triggered) * invoke-timer-callback; * wait-for-character(); * if (character available) * break; * } * * wait-for-character() does: * while (not timed out) * { * Wait for event; * if (something on channel) * read/write channel; * else if (resized) * handle_resize(); * else if (system event) * deal-with-system-event; * else if (character available) * break; * } * */ #ifdef FEAT_GUI if (gui.in_use) retval = gui_inchar(buf, maxlen, wtime, tb_change_cnt); #endif #ifndef NO_CONSOLE # ifdef FEAT_GUI else # endif retval = mch_inchar(buf, maxlen, wtime, tb_change_cnt); #endif if (wtime == -1 || wtime > 100L) // block SIGHUP et al. (void)vim_handle_signal(SIGNAL_BLOCK); ctrl_c_interrupts = TRUE; #ifdef NO_CONSOLE_INPUT theend: #endif #ifdef FEAT_PROFILE if (do_profiling == PROF_YES && wtime != 0) prof_inchar_exit(); #endif return retval; } #if defined(UNIX) || defined(VMS) || defined(FEAT_GUI) || defined(PROTO) /* * Common code for mch_inchar() and gui_inchar(): Wait for a while or * indefinitely until characters are available, dealing with timers and * messages on channels. * * "buf" may be NULL if the available characters are not to be returned, only * check if they are available. * * Return the number of characters that are available. * If "wtime" == 0 do not wait for characters. * If "wtime" == n wait a short time for characters. * If "wtime" == -1 wait forever for characters. */ int inchar_loop( char_u *buf, int maxlen, long wtime, // don't use "time", MIPS cannot handle it int tb_change_cnt, int (*wait_func)(long wtime, int *interrupted, int ignore_input), int (*resize_func)(int check_only)) { int len; int interrupted = FALSE; int did_call_wait_func = FALSE; int did_start_blocking = FALSE; long wait_time; long elapsed_time = 0; #ifdef ELAPSED_FUNC elapsed_T start_tv; ELAPSED_INIT(start_tv); #endif // repeat until we got a character or waited long enough for (;;) { // Check if window changed size while we were busy, perhaps the ":set // columns=99" command was used. if (resize_func != NULL) resize_func(FALSE); #ifdef MESSAGE_QUEUE // Only process messages when waiting. if (wtime != 0) { parse_queued_messages(); // If input was put directly in typeahead buffer bail out here. if (typebuf_changed(tb_change_cnt)) return 0; } #endif if (wtime < 0 && did_start_blocking) // blocking and already waited for p_ut wait_time = -1; else { if (wtime >= 0) wait_time = wtime; else // going to block after p_ut wait_time = p_ut; #ifdef ELAPSED_FUNC elapsed_time = ELAPSED_FUNC(start_tv); #endif wait_time -= elapsed_time; // If the waiting time is now zero or less, we timed out. However, // loop at least once to check for characters and events. Matters // when "wtime" is zero. if (wait_time <= 0 && did_call_wait_func) { if (wtime >= 0) // no character available within "wtime" return 0; // No character available within 'updatetime'. did_start_blocking = TRUE; if (trigger_cursorhold() && maxlen >= 3 && !typebuf_changed(tb_change_cnt)) { // Put K_CURSORHOLD in the input buffer or return it. if (buf == NULL) { char_u ibuf[3]; ibuf[0] = CSI; ibuf[1] = KS_EXTRA; ibuf[2] = (int)KE_CURSORHOLD; add_to_input_buf(ibuf, 3); } else { buf[0] = K_SPECIAL; buf[1] = KS_EXTRA; buf[2] = (int)KE_CURSORHOLD; } return 3; } // There is no character available within 'updatetime' seconds: // flush all the swap files to disk. Also done when // interrupted by SIGWINCH. before_blocking(); continue; } } #ifdef FEAT_JOB_CHANNEL if (wait_time < 0 || wait_time > 100L) { // Checking if a job ended requires polling. Do this at least // every 100 msec. if (has_pending_job()) wait_time = 100L; // If there is readahead then parse_queued_messages() timed out and // we should call it again soon. if (channel_any_readahead()) wait_time = 10L; } #endif #ifdef FEAT_BEVAL_GUI if (p_beval && wait_time > 100L) // The 'balloonexpr' may indirectly invoke a callback while waiting // for a character, need to check often. wait_time = 100L; #endif // Wait for a character to be typed or another event, such as the winch // signal or an event on the monitored file descriptors. did_call_wait_func = TRUE; if (wait_func(wait_time, &interrupted, FALSE)) { // If input was put directly in typeahead buffer bail out here. if (typebuf_changed(tb_change_cnt)) return 0; // We might have something to return now. if (buf == NULL) // "buf" is NULL, we were just waiting, not actually getting // input. return input_available(); len = read_from_input_buf(buf, (long)maxlen); if (len > 0) return len; continue; } // Timed out or interrupted with no character available. #ifndef ELAPSED_FUNC // estimate the elapsed time elapsed_time += wait_time; #endif if ((resize_func != NULL && resize_func(TRUE)) #if defined(FEAT_CLIENTSERVER) && defined(UNIX) || server_waiting() #endif #ifdef MESSAGE_QUEUE || interrupted #endif || wait_time > 0 || (wtime < 0 && !did_start_blocking)) // no character available, but something to be done, keep going continue; // no character available or interrupted, return zero break; } return 0; } #endif #if defined(FEAT_TIMERS) || defined(PROTO) /* * Wait for a timer to fire or "wait_func" to return non-zero. * Returns OK when something was read. * Returns FAIL when it timed out or was interrupted. */ int ui_wait_for_chars_or_timer( long wtime, int (*wait_func)(long wtime, int *interrupted, int ignore_input), int *interrupted, int ignore_input) { int due_time; long remaining = wtime; int tb_change_cnt = typebuf.tb_change_cnt; # ifdef FEAT_JOB_CHANNEL int brief_wait = FALSE; # endif // When waiting very briefly don't trigger timers. if (wtime >= 0 && wtime < 10L) return wait_func(wtime, NULL, ignore_input); while (wtime < 0 || remaining > 0) { // Trigger timers and then get the time in wtime until the next one is // due. Wait up to that time. due_time = check_due_timer(); if (typebuf.tb_change_cnt != tb_change_cnt) { // timer may have used feedkeys() return FAIL; } if (due_time <= 0 || (wtime > 0 && due_time > remaining)) due_time = remaining; # if defined(FEAT_JOB_CHANNEL) || defined(FEAT_SOUND_CANBERRA) || defined(FEAT_SOUND_MACOSX) if ((due_time < 0 || due_time > 10L) && ( # if defined(FEAT_JOB_CHANNEL) ( # if defined(FEAT_GUI) !gui.in_use && # endif (has_pending_job() || channel_any_readahead())) # if defined(FEAT_SOUND_CANBERRA) || defined(FEAT_SOUND_MACOSX) || # endif # endif # if defined(FEAT_SOUND_CANBERRA) || defined(FEAT_SOUND_MACOSX) has_any_sound_callback() # endif )) { // There is a pending job or channel, should return soon in order // to handle them ASAP. Do check for input briefly. due_time = 10L; # ifdef FEAT_JOB_CHANNEL brief_wait = TRUE; # endif } # endif if (wait_func(due_time, interrupted, ignore_input)) return OK; if ((interrupted != NULL && *interrupted) # ifdef FEAT_JOB_CHANNEL || brief_wait # endif ) // Nothing available, but need to return so that side effects get // handled, such as handling a message on a channel. return FAIL; if (wtime > 0) remaining -= due_time; } return FAIL; } #endif /* * Return non-zero if a character is available. */ int ui_char_avail(void) { #ifdef FEAT_GUI if (gui.in_use) { gui_mch_update(); return input_available(); } #endif #ifndef NO_CONSOLE # ifdef NO_CONSOLE_INPUT if (no_console_input()) return 0; # endif return mch_char_avail(); #else return 0; #endif } /* * Delay for the given number of milliseconds. If ignoreinput is FALSE then we * cancel the delay if a key is hit. */ void ui_delay(long msec_arg, int ignoreinput) { long msec = msec_arg; #ifdef FEAT_EVAL if (ui_delay_for_testing > 0) msec = ui_delay_for_testing; ch_log(NULL, "ui_delay(%ld)", msec); #endif #ifdef FEAT_GUI if (gui.in_use && !ignoreinput) gui_wait_for_chars(msec, typebuf.tb_change_cnt); else #endif mch_delay(msec, ignoreinput ? MCH_DELAY_IGNOREINPUT : 0); } /* * If the machine has job control, use it to suspend the program, * otherwise fake it by starting a new shell. * When running the GUI iconify the window. */ void ui_suspend(void) { #ifdef FEAT_GUI if (gui.in_use) { gui_mch_iconify(); return; } #endif mch_suspend(); } #if !defined(UNIX) || !defined(SIGTSTP) || defined(PROTO) /* * When the OS can't really suspend, call this function to start a shell. * This is never called in the GUI. */ void suspend_shell(void) { if (*p_sh == NUL) emsg(_(e_shell_option_is_empty)); else { msg_puts(_("new shell started\n")); do_shell(NULL, 0); } } #endif /* * Try to get the current Vim shell size. Put the result in Rows and Columns. * Use the new sizes as defaults for 'columns' and 'lines'. * Return OK when size could be determined, FAIL otherwise. */ int ui_get_shellsize(void) { int retval; #ifdef FEAT_GUI if (gui.in_use) retval = gui_get_shellsize(); else #endif retval = mch_get_shellsize(); check_shellsize(); // adjust the default for 'lines' and 'columns' if (retval == OK) { set_number_default("lines", Rows); set_number_default("columns", Columns); } return retval; } /* * Set the size of the Vim shell according to Rows and Columns, if possible. * The gui_set_shellsize() or mch_set_shellsize() function will try to set the * new size. If this is not possible, it will adjust Rows and Columns. */ void ui_set_shellsize( int mustset UNUSED) // set by the user { #ifdef FEAT_GUI if (gui.in_use) gui_set_shellsize(mustset, TRUE, RESIZE_BOTH); else #endif mch_set_shellsize(); } /* * Called when Rows and/or Columns changed. Adjust scroll region and mouse * region. */ void ui_new_shellsize(void) { if (full_screen && !exiting) { #ifdef FEAT_GUI if (gui.in_use) gui_new_shellsize(); else #endif mch_new_shellsize(); } } #if ((defined(FEAT_EVAL) || defined(FEAT_TERMINAL)) \ && (defined(FEAT_GUI) \ || defined(MSWIN) \ || (defined(HAVE_TGETENT) && defined(FEAT_TERMRESPONSE)))) \ || defined(PROTO) /* * Get the window position in pixels, if possible. * Return FAIL when not possible. */ int ui_get_winpos(int *x, int *y, varnumber_T timeout UNUSED) { # ifdef FEAT_GUI if (gui.in_use) return gui_mch_get_winpos(x, y); # endif # if defined(MSWIN) && (!defined(FEAT_GUI) || defined(VIMDLL)) return mch_get_winpos(x, y); # else # if defined(HAVE_TGETENT) && defined(FEAT_TERMRESPONSE) return term_get_winpos(x, y, timeout); # else return FAIL; # endif # endif } #endif void ui_breakcheck(void) { ui_breakcheck_force(FALSE); } /* * When "force" is true also check when the terminal is not in raw mode. * This is useful to read input on channels. */ void ui_breakcheck_force(int force) { static int recursive = FALSE; int save_updating_screen = updating_screen; // We could be called recursively if stderr is redirected, calling // fill_input_buf() calls settmode() when stdin isn't a tty. settmode() // calls vgetorpeek() which calls ui_breakcheck() again. if (recursive) return; recursive = TRUE; // We do not want gui_resize_shell() to redraw the screen here. ++updating_screen; #ifdef FEAT_GUI if (gui.in_use) gui_mch_update(); else #endif mch_breakcheck(force); if (save_updating_screen) updating_screen = TRUE; else after_updating_screen(FALSE); recursive = FALSE; } ////////////////////////////////////////////////////////////////////////////// // Functions that handle the input buffer. // This is used for any GUI version, and the unix terminal version. // // For Unix, the input characters are buffered to be able to check for a // CTRL-C. This should be done with signals, but I don't know how to do that // in a portable way for a tty in RAW mode. // // For the client-server code in the console the received keys are put in the // input buffer. #if defined(USE_INPUT_BUF) || defined(PROTO) /* * Internal typeahead buffer. Includes extra space for long key code * descriptions which would otherwise overflow. The buffer is considered full * when only this extra space (or part of it) remains. */ #if defined(FEAT_JOB_CHANNEL) || defined(FEAT_CLIENTSERVER) /* * NetBeans stuffs debugger commands into the input buffer. * This requires a larger buffer... * (Madsen) Go with this for remote input as well ... */ # define INBUFLEN 4096 #else # define INBUFLEN 250 #endif static char_u inbuf[INBUFLEN + MAX_KEY_CODE_LEN]; static int inbufcount = 0; // number of chars in inbuf[] /* * vim_is_input_buf_full(), vim_is_input_buf_empty(), add_to_input_buf(), and * trash_input_buf() are functions for manipulating the input buffer. These * are used by the gui_* calls when a GUI is used to handle keyboard input. */ int vim_is_input_buf_full(void) { return (inbufcount >= INBUFLEN); } int vim_is_input_buf_empty(void) { return (inbufcount == 0); } #if defined(FEAT_OLE) || defined(PROTO) int vim_free_in_input_buf(void) { return (INBUFLEN - inbufcount); } #endif #if defined(FEAT_GUI_GTK) || defined(PROTO) int vim_used_in_input_buf(void) { return inbufcount; } #endif /* * Return the current contents of the input buffer and make it empty. * The returned pointer must be passed to set_input_buf() later. */ char_u * get_input_buf(void) { garray_T *gap; // We use a growarray to store the data pointer and the length. gap = ALLOC_ONE(garray_T); if (gap != NULL) { // Add one to avoid a zero size. gap->ga_data = alloc(inbufcount + 1); if (gap->ga_data != NULL) mch_memmove(gap->ga_data, inbuf, (size_t)inbufcount); gap->ga_len = inbufcount; } trash_input_buf(); return (char_u *)gap; } /* * Restore the input buffer with a pointer returned from get_input_buf(). * The allocated memory is freed, this only works once! * When "overwrite" is FALSE input typed later is kept. */ void set_input_buf(char_u *p, int overwrite) { garray_T *gap = (garray_T *)p; if (gap == NULL) return; if (gap->ga_data != NULL) { if (overwrite || inbufcount + gap->ga_len >= INBUFLEN) { mch_memmove(inbuf, gap->ga_data, gap->ga_len); inbufcount = gap->ga_len; } else { mch_memmove(inbuf + gap->ga_len, inbuf, inbufcount); mch_memmove(inbuf, gap->ga_data, gap->ga_len); inbufcount += gap->ga_len; } vim_free(gap->ga_data); } vim_free(gap); } /* * Add the given bytes to the input buffer * Special keys start with CSI. A real CSI must have been translated to * CSI KS_EXTRA KE_CSI. K_SPECIAL doesn't require translation. */ void add_to_input_buf(char_u *s, int len) { if (inbufcount + len > INBUFLEN + MAX_KEY_CODE_LEN) return; // Shouldn't ever happen! while (len--) inbuf[inbufcount++] = *s++; } /* * Add "str[len]" to the input buffer while escaping CSI bytes. */ void add_to_input_buf_csi(char_u *str, int len) { int i; char_u buf[2]; for (i = 0; i < len; ++i) { add_to_input_buf(str + i, 1); if (str[i] == CSI) { // Turn CSI into K_CSI. buf[0] = KS_EXTRA; buf[1] = (int)KE_CSI; add_to_input_buf(buf, 2); } } } /* * Remove everything from the input buffer. Called when ^C is found. */ void trash_input_buf(void) { inbufcount = 0; } /* * Read as much data from the input buffer as possible up to maxlen, and store * it in buf. */ int read_from_input_buf(char_u *buf, long maxlen) { if (inbufcount == 0) // if the buffer is empty, fill it fill_input_buf(TRUE); if (maxlen > inbufcount) maxlen = inbufcount; mch_memmove(buf, inbuf, (size_t)maxlen); inbufcount -= maxlen; // check "maxlen" to avoid clang warning if (inbufcount > 0 && maxlen > 0) mch_memmove(inbuf, inbuf + maxlen, (size_t)inbufcount); return (int)maxlen; } void fill_input_buf(int exit_on_error UNUSED) { #if defined(UNIX) || defined(VMS) || defined(MACOS_X) int len; int try; static int did_read_something = FALSE; static char_u *rest = NULL; // unconverted rest of previous read static int restlen = 0; int unconverted; #endif #ifdef FEAT_GUI if (gui.in_use # ifdef NO_CONSOLE_INPUT // Don't use the GUI input when the window hasn't been opened yet. // We get here from ui_inchar() when we should try reading from stdin. && !no_console_input() # endif ) { gui_mch_update(); return; } #endif #if defined(UNIX) || defined(VMS) || defined(MACOS_X) if (vim_is_input_buf_full()) return; /* * Fill_input_buf() is only called when we really need a character. * If we can't get any, but there is some in the buffer, just return. * If we can't get any, and there isn't any in the buffer, we give up and * exit Vim. */ if (rest != NULL) { // Use remainder of previous call, starts with an invalid character // that may become valid when reading more. if (restlen > INBUFLEN - inbufcount) unconverted = INBUFLEN - inbufcount; else unconverted = restlen; mch_memmove(inbuf + inbufcount, rest, unconverted); if (unconverted == restlen) VIM_CLEAR(rest); else { restlen -= unconverted; mch_memmove(rest, rest + unconverted, restlen); } inbufcount += unconverted; } else unconverted = 0; len = 0; // to avoid gcc warning for (try = 0; try < 100; ++try) { size_t readlen = (size_t)((INBUFLEN - inbufcount) / input_conv.vc_factor); # ifdef VMS len = vms_read((char *)inbuf + inbufcount, readlen); # else len = read(read_cmd_fd, (char *)inbuf + inbufcount, readlen); # endif # ifdef FEAT_EVAL if (len > 0) { inbuf[inbufcount + len] = NUL; ch_log(NULL, "raw key input: \"%s\"", inbuf + inbufcount); } # endif if (len > 0 || got_int) break; /* * If reading stdin results in an error, continue reading stderr. * This helps when using "foo | xargs vim". */ if (!did_read_something && !isatty(read_cmd_fd) && read_cmd_fd == 0) { int m = cur_tmode; // We probably set the wrong file descriptor to raw mode. Switch // back to cooked mode, use another descriptor and set the mode to // what it was. settmode(TMODE_COOK); #ifdef HAVE_DUP // Use stderr for stdin, also works for shell commands. close(0); vim_ignored = dup(2); #else read_cmd_fd = 2; // read from stderr instead of stdin #endif settmode(m); } if (!exit_on_error) return; } if (len <= 0 && !got_int) read_error_exit(); if (len > 0) did_read_something = TRUE; if (got_int) { // Interrupted, pretend a CTRL-C was typed. inbuf[0] = 3; inbufcount = 1; } else { /* * May perform conversion on the input characters. * Include the unconverted rest of the previous call. * If there is an incomplete char at the end it is kept for the next * time, reading more bytes should make conversion possible. * Don't do this in the unlikely event that the input buffer is too * small ("rest" still contains more bytes). */ if (input_conv.vc_type != CONV_NONE) { inbufcount -= unconverted; len = convert_input_safe(inbuf + inbufcount, len + unconverted, INBUFLEN - inbufcount, rest == NULL ? &rest : NULL, &restlen); } while (len > 0) { // If a CTRL-C was typed, remove it from the buffer and set // got_int. Also recognize CTRL-C with modifyOtherKeys set, lower // and upper case, in two forms. if (ctrl_c_interrupts && (inbuf[inbufcount] == 3 || (len >= 10 && STRNCMP(inbuf + inbufcount, "\033[27;5;99~", 10) == 0) || (len >= 10 && STRNCMP(inbuf + inbufcount, "\033[27;5;67~", 10) == 0) || (len >= 7 && STRNCMP(inbuf + inbufcount, "\033[99;5u", 7) == 0) || (len >= 7 && STRNCMP(inbuf + inbufcount, "\033[67;5u", 7) == 0))) { // remove everything typed before the CTRL-C mch_memmove(inbuf, inbuf + inbufcount, (size_t)(len)); inbufcount = 0; got_int = TRUE; } --len; ++inbufcount; } } #endif // UNIX || VMS || MACOS_X } #endif // USE_INPUT_BUF /* * Exit because of an input read error. */ void read_error_exit(void) { if (silent_mode) // Normal way to exit for "ex -s" getout(0); STRCPY(IObuff, _("Vim: Error reading input, exiting...\n")); preserve_exit(); } #if defined(CURSOR_SHAPE) || defined(PROTO) /* * May update the shape of the cursor. */ void ui_cursor_shape_forced(int forced) { # ifdef FEAT_GUI if (gui.in_use) gui_update_cursor_later(); else # endif term_cursor_mode(forced); # ifdef MCH_CURSOR_SHAPE mch_update_cursor(); # endif # ifdef FEAT_CONCEAL conceal_check_cursor_line(FALSE); # endif } void ui_cursor_shape(void) { ui_cursor_shape_forced(FALSE); } #endif /* * Check bounds for column number */ int check_col(int col) { if (col < 0) return 0; if (col >= screen_Columns) return screen_Columns - 1; return col; } /* * Check bounds for row number */ int check_row(int row) { if (row < 0) return 0; if (row >= screen_Rows) return screen_Rows - 1; return row; } /* * Return length of line "lnum" in screen cells for horizontal scrolling. */ long scroll_line_len(linenr_T lnum) { char_u *p = ml_get(lnum); colnr_T col = 0; if (*p != NUL) for (;;) { int w = chartabsize(p, col); MB_PTR_ADV(p); if (*p == NUL) // don't count the last character break; col += w; } return col; } /* * Find the longest visible line number. This is used for horizontal * scrolling. If this is not possible (or not desired, by setting 'h' in * "guioptions") then the current line number is returned. */ linenr_T ui_find_longest_lnum(void) { linenr_T ret = 0; // Calculate maximum for horizontal scrollbar. Check for reasonable // line numbers, topline and botline can be invalid when displaying is // postponed. if ( # ifdef FEAT_GUI (!gui.in_use || vim_strchr(p_go, GO_HORSCROLL) == NULL) && # endif curwin->w_topline <= curwin->w_cursor.lnum && curwin->w_botline > curwin->w_cursor.lnum && curwin->w_botline <= curbuf->b_ml.ml_line_count + 1) { linenr_T lnum; long n; long max = 0; // Use maximum of all visible lines. Remember the lnum of the // longest line, closest to the cursor line. Used when scrolling // below. for (lnum = curwin->w_topline; lnum < curwin->w_botline; ++lnum) { n = scroll_line_len(lnum); if (n > max) { max = n; ret = lnum; } else if (n == max && abs((int)(lnum - curwin->w_cursor.lnum)) < abs((int)(ret - curwin->w_cursor.lnum))) ret = lnum; } } else // Use cursor line only. ret = curwin->w_cursor.lnum; return ret; } /* * Called when focus changed. Used for the GUI or for systems where this can * be done in the console (Win32). */ void ui_focus_change( int in_focus) // TRUE if focus gained. { static time_t last_time = (time_t)0; int need_redraw = FALSE; // When activated: Check if any file was modified outside of Vim. // Only do this when not done within the last two seconds (could get // several events in a row). if (in_focus && last_time + 2 < time(NULL)) { need_redraw = check_timestamps( # ifdef FEAT_GUI gui.in_use # else FALSE # endif ); last_time = time(NULL); } #ifdef FEAT_TERMINAL term_focus_change(in_focus); #endif /* * Fire the focus gained/lost autocommand. */ need_redraw |= apply_autocmds(in_focus ? EVENT_FOCUSGAINED : EVENT_FOCUSLOST, NULL, NULL, FALSE, curbuf); if (need_redraw) redraw_after_callback(TRUE, TRUE); // File may have been changed from 'readonly' to 'noreadonly' if (need_maketitle) maketitle(); } #if defined(HAVE_INPUT_METHOD) || defined(PROTO) /* * Save current Input Method status to specified place. */ void im_save_status(long *psave) { // Don't save when 'imdisable' is set or "xic" is NULL, IM is always // disabled then (but might start later). // Also don't save when inside a mapping, vgetc_im_active has not been set // then. // And don't save when the keys were stuffed (e.g., for a "." command). // And don't save when the GUI is running but our window doesn't have // input focus (e.g., when a find dialog is open). if (!p_imdisable && KeyTyped && !KeyStuffed # ifdef FEAT_XIM && xic != NULL # endif # ifdef FEAT_GUI && (!gui.in_use || gui.in_focus) # endif ) { // Do save when IM is on, or IM is off and saved status is on. if (vgetc_im_active) *psave = B_IMODE_IM; else if (*psave == B_IMODE_IM) *psave = B_IMODE_NONE; } } #endif