diff options
Diffstat (limited to 'usr.bin/vim/message.c')
-rw-r--r-- | usr.bin/vim/message.c | 832 |
1 files changed, 832 insertions, 0 deletions
diff --git a/usr.bin/vim/message.c b/usr.bin/vim/message.c new file mode 100644 index 00000000000..1aa4177308f --- /dev/null +++ b/usr.bin/vim/message.c @@ -0,0 +1,832 @@ +/* $OpenBSD: message.c,v 1.1 1996/09/07 21:40:25 downsj Exp $ */ +/* vi:set ts=4 sw=4: + * + * 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. + */ + +/* + * message.c: functions for displaying messages on the command line + */ + +#include "vim.h" +#include "globals.h" +#define MESSAGE /* don't include prototype for smsg() */ +#include "proto.h" +#include "option.h" + +static void msg_screen_outchar __ARGS((int c)); +static int msg_check_screen __ARGS((void)); + +static int lines_left = -1; /* lines left for listing */ + +/* + * msg(s) - displays the string 's' on the status line + * When terminal not initialized (yet) fprintf(stderr,..) is used. + * return TRUE if wait_return not called + */ + int +msg(s) + char_u *s; +{ + msg_start(); + if (msg_highlight) + start_highlight(); + msg_outtrans(s); + if (msg_highlight) + { + stop_highlight(); + msg_highlight = FALSE; /* clear for next call */ + } + msg_clr_eos(); + return msg_end(); +} + +/* + * automatic prototype generation does not understand this function + */ +#ifndef PROTO +int smsg __ARGS((char_u *, long, long, long, + long, long, long, long, long, long, long)); + +/* VARARGS */ + int +smsg(s, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) + char_u *s; + long a1, a2, a3, a4, a5, a6, a7, a8, a9, a10; +{ + sprintf((char *)IObuff, (char *)s, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); + return msg(IObuff); +} +#endif + +/* + * emsg() - display an error message + * + * Rings the bell, if appropriate, and calls message() to do the real work + * When terminal not initialized (yet) fprintf(stderr,..) is used. + * + * return TRUE if wait_return not called + */ + int +emsg(s) + char_u *s; +{ + char_u *Buf; +#ifdef SLEEP_IN_EMSG + int retval; +#endif + static int last_lnum = 0; + static char_u *last_sourcing_name = NULL; + + if (emsg_off) /* no error messages at the moment */ + return TRUE; + + if (global_busy) /* break :global command */ + ++global_busy; + + if (p_eb) + beep_flush(); /* also includes flush_buffers() */ + else + flush_buffers(FALSE); /* flush internal buffers */ + did_emsg = TRUE; /* flag for DoOneCmd() */ + ++msg_scroll; /* don't overwrite a previous message */ + (void)set_highlight('e'); /* set highlight mode for error messages */ + msg_highlight = TRUE; + if (msg_scrolled) + need_wait_return = TRUE; /* needed in case emsg() is called after + * wait_return has reset need_wait_return + * and a redraw is expected because + * msg_scrolled is non-zero */ + +/* + * First output name and line number of source of error message + */ + if (sourcing_name != NULL && + (sourcing_name != last_sourcing_name || sourcing_lnum != last_lnum) + && (Buf = alloc(MAXPATHL + 30)) != NULL) + { + ++no_wait_return; + if (sourcing_name != last_sourcing_name) + { + sprintf((char *)Buf, "Error detected while processing %s:", + sourcing_name); + msg(Buf); + msg_highlight = TRUE; + } + /* lnum is 0 when executing a command from the command line + * argument, we don't want a line number then */ + if (sourcing_lnum != 0) + { + (void)set_highlight('n'); /* highlight mode for line numbers */ + sprintf((char *)Buf, "line %4ld:", sourcing_lnum); + msg(Buf); + (void)set_highlight('e'); /* highlight mode for error messages */ + msg_highlight = TRUE; + } + --no_wait_return; + last_lnum = sourcing_lnum; /* only once for each line */ + vim_free(Buf); + } + last_sourcing_name = sourcing_name; /* do this also when it is NULL */ + +#ifdef SLEEP_IN_EMSG +/* + * Msg returns TRUE if wait_return() was not called. + * In that case may call sleep() to give the user a chance to read the message. + * Don't call sleep() if dont_sleep is set. + */ + retval = msg(s); + if (retval) + { + if (dont_sleep || need_wait_return) + need_sleep = TRUE; /* sleep before removing the message */ + else + mch_delay(1000L, TRUE); /* give user chance to read message */ + } + /* --msg_scroll; don't overwrite this message */ + return retval; +#else + emsg_on_display = TRUE; /* remember there is an error message */ + return msg(s); +#endif +} + + int +emsg2(s, a1) + char_u *s, *a1; +{ + /* Check for NULL strings (just in case) */ + if (a1 == NULL) + a1 = (char_u *)"[NULL]"; + /* Check for very long strings (can happen with ":help ^A<CR>") */ + if (STRLEN(s) + STRLEN(a1) >= IOSIZE) + a1 = (char_u *)"[string too long]"; + sprintf((char *)IObuff, (char *)s, (char *)a1); + return emsg(IObuff); +} + + int +emsgn(s, n) + char_u *s; + long n; +{ + sprintf((char *)IObuff, (char *)s, n); + return emsg(IObuff); +} + +/* + * Like msg(), but truncate to a single line if p_shm contains 't'. + * Careful: The string may be changed! + */ + int +msg_trunc(s) + char_u *s; +{ + int n; + + if (shortmess(SHM_TRUNC) && (n = (int)STRLEN(s) - + (int)(Rows - cmdline_row - 1) * Columns - sc_col + 1) > 0) + { + s[n] = '<'; + return msg(s + n); + } + else + return msg(s); +} + +/* + * wait for the user to hit a key (normally a return) + * if 'redraw' is TRUE, clear and redraw the screen + * if 'redraw' is FALSE, just redraw the screen + * if 'redraw' is -1, don't redraw at all + */ + void +wait_return(redraw) + int redraw; +{ + int c; + int oldState; + int tmpState; + + if (redraw == TRUE) + must_redraw = CLEAR; + +/* + * With the global command (and some others) we only need one return at the + * end. Adjust cmdline_row to avoid the next message overwriting the last one. + */ + if (no_wait_return) + { + need_wait_return = TRUE; + cmdline_row = msg_row; + return; + } + oldState = State; + if (quit_more) + { + c = CR; /* just pretend CR was hit */ + quit_more = FALSE; + got_int = FALSE; + } + else + { + State = HITRETURN; +#ifdef USE_MOUSE + setmouse(); +#endif + if (msg_didout) /* start on a new line */ + msg_outchar('\n'); + if (got_int) + MSG_OUTSTR("Interrupt: "); + + (void)set_highlight('r'); + start_highlight(); +#ifdef ORG_HITRETURN + MSG_OUTSTR("Press RETURN to continue"); + stop_highlight(); + do { + c = vgetc(); + } while (vim_strchr((char_u *)"\r\n: ", c) == NULL); + if (c == ':') /* this can vi too (but not always!) */ + stuffcharReadbuff(c); +#else + MSG_OUTSTR("Press RETURN or enter command to continue"); + stop_highlight(); + do + { + c = vgetc(); + got_int = FALSE; + } while (c == Ctrl('C') +#ifdef USE_GUI + || c == K_SCROLLBAR || c == K_HORIZ_SCROLLBAR +#endif +#ifdef USE_MOUSE + || c == K_LEFTDRAG || c == K_LEFTRELEASE + || c == K_MIDDLEDRAG || c == K_MIDDLERELEASE + || c == K_RIGHTDRAG || c == K_RIGHTRELEASE + || c == K_IGNORE || + (!mouse_has(MOUSE_RETURN) && + (c == K_LEFTMOUSE || + c == K_MIDDLEMOUSE || + c == K_RIGHTMOUSE)) +#endif + ); + mch_breakcheck(); +#ifdef USE_MOUSE + /* + * Avoid that the mouse-up event causes visual mode to start. + */ + if (c == K_LEFTMOUSE || c == K_MIDDLEMOUSE || c == K_RIGHTMOUSE) + jump_to_mouse(MOUSE_SETPOS); + else +#endif + if (vim_strchr((char_u *)"\r\n ", c) == NULL) + { + stuffcharReadbuff(c); + do_redraw = TRUE; /* need a redraw even though there is + something in the stuff buffer */ + } +#endif + } + + /* + * If the user hits ':', '?' or '/' we get a command line from the next + * line. + */ + if (c == ':' || c == '?' || c == '/') + { + cmdline_row = msg_row; + skip_redraw = TRUE; /* skip redraw once */ + do_redraw = FALSE; + } + +/* + * If the window size changed set_winsize() will redraw the screen. + * Otherwise the screen is only redrawn if 'redraw' is set and no ':' typed. + */ + tmpState = State; + State = oldState; /* restore State before set_winsize */ +#ifdef USE_MOUSE + setmouse(); +#endif + msg_check(); + + need_wait_return = FALSE; + emsg_on_display = FALSE; /* can delete error message now */ +#ifdef SLEEP_IN_EMSG + need_sleep = FALSE; /* no need to call sleep() anymore */ +#endif + msg_didany = FALSE; /* reset lines_left at next msg_start() */ + lines_left = -1; + if (keep_msg != NULL && linetabsize(keep_msg) >= + (Rows - cmdline_row - 1) * Columns + sc_col) + keep_msg = NULL; /* don't redisplay message, it's too long */ + + if (tmpState == SETWSIZE) /* got resize event while in vgetc() */ + { + starttermcap(); /* start termcap before redrawing */ + set_winsize(0, 0, FALSE); + } + else if (!skip_redraw && (redraw == TRUE || (msg_scrolled && redraw != -1))) + { + starttermcap(); /* start termcap before redrawing */ + updateScreen(VALID); + } + + dont_wait_return = TRUE; /* don't wait again in main() */ +} + +/* + * Prepare for outputting characters in the command line. + */ + void +msg_start() +{ + keep_msg = NULL; /* don't display old message now */ + keep_msg_highlight = 0; + if (!msg_scroll && full_screen) /* overwrite last message */ + msg_pos(cmdline_row, 0); + else if (msg_didout) /* start message on next line */ + { + msg_outchar('\n'); + cmdline_row = msg_row; + } + if (!msg_didany) + lines_left = cmdline_row; + msg_didout = FALSE; /* no output on current line yet */ + cursor_off(); +} + +/* + * Move message position. This should always be used after moving the cursor. + * Use negative value if row or col does not have to be changed. + */ + void +msg_pos(row, col) + int row, col; +{ + if (row >= 0) + msg_row = row; + if (col >= 0) + msg_col = col; +} + + void +msg_outchar(c) + int c; +{ + char_u buf[4]; + + if (IS_SPECIAL(c)) + { + buf[0] = K_SPECIAL; + buf[1] = K_SECOND(c); + buf[2] = K_THIRD(c); + buf[3] = NUL; + } + else + { + buf[0] = c; + buf[1] = NUL; + } + msg_outstr(buf); +} + + void +msg_outnum(n) + long n; +{ + char_u buf[20]; + + sprintf((char *)buf, "%ld", n); + msg_outstr(buf); +} + + void +msg_home_replace(fname) + char_u *fname; +{ + char_u *name; + + name = home_replace_save(NULL, fname); + if (name != NULL) + msg_outtrans(name); + vim_free(name); +} + +/* + * output 'len' characters in 'str' (including NULs) with translation + * if 'len' is -1, output upto a NUL character + * return the number of characters it takes on the screen + */ + int +msg_outtrans(str) + register char_u *str; +{ + return msg_outtrans_len(str, (int)STRLEN(str)); +} + + int +msg_outtrans_len(str, len) + register char_u *str; + register int len; +{ + int retval = 0; + + while (--len >= 0) + { + msg_outstr(transchar(*str)); + retval += charsize(*str); + ++str; + } + return retval; +} + +/* + * Output the string 'str' upto a NUL character. + * Return the number of characters it takes on the screen. + * + * If K_SPECIAL is encountered, then it is taken in conjunction with the + * following character and shown as <F1>, <S-Up> etc. In addition, if 'all' + * is TRUE, then any other character which has its 8th bit set is shown as + * <M-x>, where x is the equivalent character without its 8th bit set. If a + * character is displayed in one of these special ways, is also highlighted + * (its highlight name is '8' in the p_hl variable). + * This function is used to show mappings, where we want to see how to type + * the character/string -- webb + */ + int +msg_outtrans_special(str, all) + register char_u *str; + register int all; /* <M-a> etc as well as <F1> etc */ +{ + int retval = 0; + char_u *string; + int c; + int modifiers; + + set_highlight('8'); + for (; *str; ++str) + { + c = *str; + if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL) + { + modifiers = 0x0; + if (str[1] == KS_MODIFIER) + { + modifiers = str[2]; + str += 3; + c = *str; + } + if (c == K_SPECIAL) + { + c = TO_SPECIAL(str[1], str[2]); + str += 2; + if (c == K_ZERO) /* display <Nul> as ^@ */ + c = NUL; + } + if (IS_SPECIAL(c) || modifiers) /* special key */ + { + string = get_special_key_name(c, modifiers); + start_highlight(); + msg_outstr(string); + retval += STRLEN(string); + stop_highlight(); + flushbuf(); /* Otherwise gets overwritten by spaces */ + continue; + } + } + if ((c & 0x80) && all) + { + start_highlight(); + MSG_OUTSTR("<M-"); + msg_outstr(transchar(c & 0x7f)); + retval += 2 + charsize(c & 0x7f); + MSG_OUTSTR(">"); + stop_highlight(); + } + else + { + msg_outstr(transchar(c)); + retval += charsize(c); + } + } + return retval; +} + +/* + * print line for :p command + */ + void +msg_prt_line(s) + char_u *s; +{ + register int si = 0; + register int c; + register int col = 0; + + int n_extra = 0; + int n_spaces = 0; + char_u *p = NULL; /* init to make SASC shut up */ + int n; + + for (;;) + { + if (n_extra) + { + --n_extra; + c = *p++; + } + else if (n_spaces) + { + --n_spaces; + c = ' '; + } + else + { + c = s[si++]; + if (c == TAB && !curwin->w_p_list) + { + /* tab amount depends on current column */ + n_spaces = curbuf->b_p_ts - col % curbuf->b_p_ts - 1; + c = ' '; + } + else if (c == NUL && curwin->w_p_list) + { + p = (char_u *)""; + n_extra = 1; + c = '$'; + } + else if (c != NUL && (n = charsize(c)) > 1) + { + n_extra = n - 1; + p = transchar(c); + c = *p++; + } + } + + if (c == NUL) + break; + + msg_outchar(c); + col++; + } +} + +/* + * output a string to the screen at position msg_row, msg_col + * Update msg_row and msg_col for the next message. + */ + void +msg_outstr(s) + char_u *s; +{ + int oldState; + char_u buf[20]; + + /* + * If there is no valid screen, use fprintf so we can see error messages. + * If termcap is not active, we may be writing in an alternate console + * window, cursor positioning may not work correctly (window size may be + * different, e.g. for WIN32 console). + */ + if (!msg_check_screen() +#ifdef WIN32 + || !termcap_active +#endif + ) + { +#ifdef WIN32 + mch_settmode(0); /* cook so that \r and \n are handled correctly */ +#endif + fprintf(stderr, (char *)s); + msg_didout = TRUE; /* assume that line is not empty */ +#ifdef WIN32 + mch_settmode(1); +#endif + return; + } + + msg_didany = TRUE; /* remember that something was outputted */ + while (*s) + { + /* + * The screen is scrolled up when: + * - When outputting a newline in the last row + * - when outputting a character in the last column of the last row + * (some terminals scroll automatically, some don't. To avoid + * problems we scroll ourselves) + */ + if (msg_row >= Rows - 1 && (*s == '\n' || msg_col >= Columns - 1 || + (*s == TAB && msg_col >= ((Columns - 1) & ~7)))) + { + screen_del_lines(0, 0, 1, (int)Rows, TRUE); /* always works */ + msg_row = Rows - 2; + if (msg_col >= Columns) /* can happen after screen resize */ + msg_col = Columns - 1; + ++msg_scrolled; + need_wait_return = TRUE; /* may need wait_return in main() */ + if (cmdline_row > 0) + --cmdline_row; + /* + * if screen is completely filled wait for a character + */ + if (p_more && --lines_left == 0 && State != HITRETURN) + { + oldState = State; + State = ASKMORE; +#ifdef USE_MOUSE + setmouse(); +#endif + msg_moremsg(FALSE); + for (;;) + { + /* + * Get a typed character directly from the user. + * Don't use vgetc(), it syncs undo and eats mapped + * characters. Disadvantage: Special keys and mouse + * cannot be used here, typeahead is ignored. + */ + flushbuf(); + (void)mch_inchar(buf, 20, -1L); + switch (buf[0]) + { + case CR: /* one extra line */ + case NL: + lines_left = 1; + break; + case ':': /* start new command line */ + stuffcharReadbuff(':'); + cmdline_row = Rows - 1; /* put ':' on this line */ + skip_redraw = TRUE; /* skip redraw once */ + dont_wait_return = TRUE; /* don't wait in main() */ + /*FALLTHROUGH*/ + case 'q': /* quit */ + case Ctrl('C'): + got_int = TRUE; + quit_more = TRUE; + break; + case 'd': /* Down half a page */ + lines_left = Rows / 2; + break; + case ' ': /* one extra page */ + lines_left = Rows - 1; + break; + default: /* no valid response */ + msg_moremsg(TRUE); + continue; + } + break; + } + /* clear the --more-- message */ + screen_fill((int)Rows - 1, (int)Rows, + 0, (int)Columns, ' ', ' '); + State = oldState; +#ifdef USE_MOUSE + setmouse(); +#endif + if (quit_more) + { + msg_row = Rows - 1; + msg_col = 0; + return; /* the string is not displayed! */ + } + } + } + if (*s == '\n') /* go to next line */ + { + msg_didout = FALSE; /* remember that line is empty */ + msg_col = 0; + if (++msg_row >= Rows) /* safety check */ + msg_row = Rows - 1; + } + else if (*s == '\r') /* go to column 0 */ + { + msg_col = 0; + } + else if (*s == '\b') /* go to previous char */ + { + if (msg_col) + --msg_col; + } + else if (*s == TAB) /* translate into spaces */ + { + do + msg_screen_outchar(' '); + while (msg_col & 7); + } + else + msg_screen_outchar(*s); + ++s; + } +} + + static void +msg_screen_outchar(c) + int c; +{ + msg_didout = TRUE; /* remember that line is not empty */ + screen_outchar(c, msg_row, msg_col); + if (++msg_col >= Columns) + { + msg_col = 0; + ++msg_row; + } +} + + void +msg_moremsg(full) + int full; +{ + /* + * Need to restore old highlighting when we've finished with it + * because the output that's paging may be relying on it not + * changing -- webb + */ + remember_highlight(); + set_highlight('m'); + start_highlight(); + screen_msg((char_u *)"-- More --", (int)Rows - 1, 0); + if (full) + screen_msg((char_u *)" (RET: line, SPACE: page, d: half page, q: quit)", + (int)Rows - 1, 10); + stop_highlight(); + recover_old_highlight(); +} + +/* + * msg_check_screen - check if the screen is initialized. + * Also check msg_row and msg_col, if they are too big it may cause a crash. + */ + static int +msg_check_screen() +{ + if (!full_screen || !screen_valid(FALSE)) + return FALSE; + + if (msg_row >= Rows) + msg_row = Rows - 1; + if (msg_col >= Columns) + msg_col = Columns - 1; + return TRUE; +} + +/* + * clear from current message position to end of screen + * Note: msg_col is not updated, so we remember the end of the message + * for msg_check(). + */ + void +msg_clr_eos() +{ + if (!msg_check_screen() +#ifdef WIN32 + || !termcap_active +#endif + ) + return; + screen_fill(msg_row, msg_row + 1, msg_col, (int)Columns, ' ', ' '); + screen_fill(msg_row + 1, (int)Rows, 0, (int)Columns, ' ', ' '); +} + +/* + * end putting a message on the screen + * call wait_return if the message does not fit in the available space + * return TRUE if wait_return not called. + */ + int +msg_end() +{ + /* + * if the string is larger than the window, + * or the ruler option is set and we run into it, + * we have to redraw the window. + * Do not do this if we are abandoning the file or editing the command line. + */ + if (!exiting && msg_check() && State != CMDLINE) + { + wait_return(FALSE); + return FALSE; + } + flushbuf(); + return TRUE; +} + +/* + * If the written message has caused the screen to scroll up, or if we + * run into the shown command or ruler, we have to redraw the window later. + */ + int +msg_check() +{ + if (msg_scrolled || (msg_row == Rows - 1 && msg_col >= sc_col)) + { + redraw_later(NOT_VALID); + redraw_cmdline = TRUE; + return TRUE; + } + return FALSE; +} |