Annotation of src/usr.bin/vim/message.c, Revision 1.1.1.1
1.1 downsj 1: /* $OpenBSD$ */
2: /* vi:set ts=4 sw=4:
3: *
4: * VIM - Vi IMproved by Bram Moolenaar
5: *
6: * Do ":help uganda" in Vim to read copying and usage conditions.
7: * Do ":help credits" in Vim to see a list of people who contributed.
8: */
9:
10: /*
11: * message.c: functions for displaying messages on the command line
12: */
13:
14: #include "vim.h"
15: #include "globals.h"
16: #define MESSAGE /* don't include prototype for smsg() */
17: #include "proto.h"
18: #include "option.h"
19:
20: static void msg_screen_outchar __ARGS((int c));
21: static int msg_check_screen __ARGS((void));
22:
23: static int lines_left = -1; /* lines left for listing */
24:
25: /*
26: * msg(s) - displays the string 's' on the status line
27: * When terminal not initialized (yet) fprintf(stderr,..) is used.
28: * return TRUE if wait_return not called
29: */
30: int
31: msg(s)
32: char_u *s;
33: {
34: msg_start();
35: if (msg_highlight)
36: start_highlight();
37: msg_outtrans(s);
38: if (msg_highlight)
39: {
40: stop_highlight();
41: msg_highlight = FALSE; /* clear for next call */
42: }
43: msg_clr_eos();
44: return msg_end();
45: }
46:
47: /*
48: * automatic prototype generation does not understand this function
49: */
50: #ifndef PROTO
51: int smsg __ARGS((char_u *, long, long, long,
52: long, long, long, long, long, long, long));
53:
54: /* VARARGS */
55: int
56: smsg(s, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)
57: char_u *s;
58: long a1, a2, a3, a4, a5, a6, a7, a8, a9, a10;
59: {
60: sprintf((char *)IObuff, (char *)s, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10);
61: return msg(IObuff);
62: }
63: #endif
64:
65: /*
66: * emsg() - display an error message
67: *
68: * Rings the bell, if appropriate, and calls message() to do the real work
69: * When terminal not initialized (yet) fprintf(stderr,..) is used.
70: *
71: * return TRUE if wait_return not called
72: */
73: int
74: emsg(s)
75: char_u *s;
76: {
77: char_u *Buf;
78: #ifdef SLEEP_IN_EMSG
79: int retval;
80: #endif
81: static int last_lnum = 0;
82: static char_u *last_sourcing_name = NULL;
83:
84: if (emsg_off) /* no error messages at the moment */
85: return TRUE;
86:
87: if (global_busy) /* break :global command */
88: ++global_busy;
89:
90: if (p_eb)
91: beep_flush(); /* also includes flush_buffers() */
92: else
93: flush_buffers(FALSE); /* flush internal buffers */
94: did_emsg = TRUE; /* flag for DoOneCmd() */
95: ++msg_scroll; /* don't overwrite a previous message */
96: (void)set_highlight('e'); /* set highlight mode for error messages */
97: msg_highlight = TRUE;
98: if (msg_scrolled)
99: need_wait_return = TRUE; /* needed in case emsg() is called after
100: * wait_return has reset need_wait_return
101: * and a redraw is expected because
102: * msg_scrolled is non-zero */
103:
104: /*
105: * First output name and line number of source of error message
106: */
107: if (sourcing_name != NULL &&
108: (sourcing_name != last_sourcing_name || sourcing_lnum != last_lnum)
109: && (Buf = alloc(MAXPATHL + 30)) != NULL)
110: {
111: ++no_wait_return;
112: if (sourcing_name != last_sourcing_name)
113: {
114: sprintf((char *)Buf, "Error detected while processing %s:",
115: sourcing_name);
116: msg(Buf);
117: msg_highlight = TRUE;
118: }
119: /* lnum is 0 when executing a command from the command line
120: * argument, we don't want a line number then */
121: if (sourcing_lnum != 0)
122: {
123: (void)set_highlight('n'); /* highlight mode for line numbers */
124: sprintf((char *)Buf, "line %4ld:", sourcing_lnum);
125: msg(Buf);
126: (void)set_highlight('e'); /* highlight mode for error messages */
127: msg_highlight = TRUE;
128: }
129: --no_wait_return;
130: last_lnum = sourcing_lnum; /* only once for each line */
131: vim_free(Buf);
132: }
133: last_sourcing_name = sourcing_name; /* do this also when it is NULL */
134:
135: #ifdef SLEEP_IN_EMSG
136: /*
137: * Msg returns TRUE if wait_return() was not called.
138: * In that case may call sleep() to give the user a chance to read the message.
139: * Don't call sleep() if dont_sleep is set.
140: */
141: retval = msg(s);
142: if (retval)
143: {
144: if (dont_sleep || need_wait_return)
145: need_sleep = TRUE; /* sleep before removing the message */
146: else
147: mch_delay(1000L, TRUE); /* give user chance to read message */
148: }
149: /* --msg_scroll; don't overwrite this message */
150: return retval;
151: #else
152: emsg_on_display = TRUE; /* remember there is an error message */
153: return msg(s);
154: #endif
155: }
156:
157: int
158: emsg2(s, a1)
159: char_u *s, *a1;
160: {
161: /* Check for NULL strings (just in case) */
162: if (a1 == NULL)
163: a1 = (char_u *)"[NULL]";
164: /* Check for very long strings (can happen with ":help ^A<CR>") */
165: if (STRLEN(s) + STRLEN(a1) >= IOSIZE)
166: a1 = (char_u *)"[string too long]";
167: sprintf((char *)IObuff, (char *)s, (char *)a1);
168: return emsg(IObuff);
169: }
170:
171: int
172: emsgn(s, n)
173: char_u *s;
174: long n;
175: {
176: sprintf((char *)IObuff, (char *)s, n);
177: return emsg(IObuff);
178: }
179:
180: /*
181: * Like msg(), but truncate to a single line if p_shm contains 't'.
182: * Careful: The string may be changed!
183: */
184: int
185: msg_trunc(s)
186: char_u *s;
187: {
188: int n;
189:
190: if (shortmess(SHM_TRUNC) && (n = (int)STRLEN(s) -
191: (int)(Rows - cmdline_row - 1) * Columns - sc_col + 1) > 0)
192: {
193: s[n] = '<';
194: return msg(s + n);
195: }
196: else
197: return msg(s);
198: }
199:
200: /*
201: * wait for the user to hit a key (normally a return)
202: * if 'redraw' is TRUE, clear and redraw the screen
203: * if 'redraw' is FALSE, just redraw the screen
204: * if 'redraw' is -1, don't redraw at all
205: */
206: void
207: wait_return(redraw)
208: int redraw;
209: {
210: int c;
211: int oldState;
212: int tmpState;
213:
214: if (redraw == TRUE)
215: must_redraw = CLEAR;
216:
217: /*
218: * With the global command (and some others) we only need one return at the
219: * end. Adjust cmdline_row to avoid the next message overwriting the last one.
220: */
221: if (no_wait_return)
222: {
223: need_wait_return = TRUE;
224: cmdline_row = msg_row;
225: return;
226: }
227: oldState = State;
228: if (quit_more)
229: {
230: c = CR; /* just pretend CR was hit */
231: quit_more = FALSE;
232: got_int = FALSE;
233: }
234: else
235: {
236: State = HITRETURN;
237: #ifdef USE_MOUSE
238: setmouse();
239: #endif
240: if (msg_didout) /* start on a new line */
241: msg_outchar('\n');
242: if (got_int)
243: MSG_OUTSTR("Interrupt: ");
244:
245: (void)set_highlight('r');
246: start_highlight();
247: #ifdef ORG_HITRETURN
248: MSG_OUTSTR("Press RETURN to continue");
249: stop_highlight();
250: do {
251: c = vgetc();
252: } while (vim_strchr((char_u *)"\r\n: ", c) == NULL);
253: if (c == ':') /* this can vi too (but not always!) */
254: stuffcharReadbuff(c);
255: #else
256: MSG_OUTSTR("Press RETURN or enter command to continue");
257: stop_highlight();
258: do
259: {
260: c = vgetc();
261: got_int = FALSE;
262: } while (c == Ctrl('C')
263: #ifdef USE_GUI
264: || c == K_SCROLLBAR || c == K_HORIZ_SCROLLBAR
265: #endif
266: #ifdef USE_MOUSE
267: || c == K_LEFTDRAG || c == K_LEFTRELEASE
268: || c == K_MIDDLEDRAG || c == K_MIDDLERELEASE
269: || c == K_RIGHTDRAG || c == K_RIGHTRELEASE
270: || c == K_IGNORE ||
271: (!mouse_has(MOUSE_RETURN) &&
272: (c == K_LEFTMOUSE ||
273: c == K_MIDDLEMOUSE ||
274: c == K_RIGHTMOUSE))
275: #endif
276: );
277: mch_breakcheck();
278: #ifdef USE_MOUSE
279: /*
280: * Avoid that the mouse-up event causes visual mode to start.
281: */
282: if (c == K_LEFTMOUSE || c == K_MIDDLEMOUSE || c == K_RIGHTMOUSE)
283: jump_to_mouse(MOUSE_SETPOS);
284: else
285: #endif
286: if (vim_strchr((char_u *)"\r\n ", c) == NULL)
287: {
288: stuffcharReadbuff(c);
289: do_redraw = TRUE; /* need a redraw even though there is
290: something in the stuff buffer */
291: }
292: #endif
293: }
294:
295: /*
296: * If the user hits ':', '?' or '/' we get a command line from the next
297: * line.
298: */
299: if (c == ':' || c == '?' || c == '/')
300: {
301: cmdline_row = msg_row;
302: skip_redraw = TRUE; /* skip redraw once */
303: do_redraw = FALSE;
304: }
305:
306: /*
307: * If the window size changed set_winsize() will redraw the screen.
308: * Otherwise the screen is only redrawn if 'redraw' is set and no ':' typed.
309: */
310: tmpState = State;
311: State = oldState; /* restore State before set_winsize */
312: #ifdef USE_MOUSE
313: setmouse();
314: #endif
315: msg_check();
316:
317: need_wait_return = FALSE;
318: emsg_on_display = FALSE; /* can delete error message now */
319: #ifdef SLEEP_IN_EMSG
320: need_sleep = FALSE; /* no need to call sleep() anymore */
321: #endif
322: msg_didany = FALSE; /* reset lines_left at next msg_start() */
323: lines_left = -1;
324: if (keep_msg != NULL && linetabsize(keep_msg) >=
325: (Rows - cmdline_row - 1) * Columns + sc_col)
326: keep_msg = NULL; /* don't redisplay message, it's too long */
327:
328: if (tmpState == SETWSIZE) /* got resize event while in vgetc() */
329: {
330: starttermcap(); /* start termcap before redrawing */
331: set_winsize(0, 0, FALSE);
332: }
333: else if (!skip_redraw && (redraw == TRUE || (msg_scrolled && redraw != -1)))
334: {
335: starttermcap(); /* start termcap before redrawing */
336: updateScreen(VALID);
337: }
338:
339: dont_wait_return = TRUE; /* don't wait again in main() */
340: }
341:
342: /*
343: * Prepare for outputting characters in the command line.
344: */
345: void
346: msg_start()
347: {
348: keep_msg = NULL; /* don't display old message now */
349: keep_msg_highlight = 0;
350: if (!msg_scroll && full_screen) /* overwrite last message */
351: msg_pos(cmdline_row, 0);
352: else if (msg_didout) /* start message on next line */
353: {
354: msg_outchar('\n');
355: cmdline_row = msg_row;
356: }
357: if (!msg_didany)
358: lines_left = cmdline_row;
359: msg_didout = FALSE; /* no output on current line yet */
360: cursor_off();
361: }
362:
363: /*
364: * Move message position. This should always be used after moving the cursor.
365: * Use negative value if row or col does not have to be changed.
366: */
367: void
368: msg_pos(row, col)
369: int row, col;
370: {
371: if (row >= 0)
372: msg_row = row;
373: if (col >= 0)
374: msg_col = col;
375: }
376:
377: void
378: msg_outchar(c)
379: int c;
380: {
381: char_u buf[4];
382:
383: if (IS_SPECIAL(c))
384: {
385: buf[0] = K_SPECIAL;
386: buf[1] = K_SECOND(c);
387: buf[2] = K_THIRD(c);
388: buf[3] = NUL;
389: }
390: else
391: {
392: buf[0] = c;
393: buf[1] = NUL;
394: }
395: msg_outstr(buf);
396: }
397:
398: void
399: msg_outnum(n)
400: long n;
401: {
402: char_u buf[20];
403:
404: sprintf((char *)buf, "%ld", n);
405: msg_outstr(buf);
406: }
407:
408: void
409: msg_home_replace(fname)
410: char_u *fname;
411: {
412: char_u *name;
413:
414: name = home_replace_save(NULL, fname);
415: if (name != NULL)
416: msg_outtrans(name);
417: vim_free(name);
418: }
419:
420: /*
421: * output 'len' characters in 'str' (including NULs) with translation
422: * if 'len' is -1, output upto a NUL character
423: * return the number of characters it takes on the screen
424: */
425: int
426: msg_outtrans(str)
427: register char_u *str;
428: {
429: return msg_outtrans_len(str, (int)STRLEN(str));
430: }
431:
432: int
433: msg_outtrans_len(str, len)
434: register char_u *str;
435: register int len;
436: {
437: int retval = 0;
438:
439: while (--len >= 0)
440: {
441: msg_outstr(transchar(*str));
442: retval += charsize(*str);
443: ++str;
444: }
445: return retval;
446: }
447:
448: /*
449: * Output the string 'str' upto a NUL character.
450: * Return the number of characters it takes on the screen.
451: *
452: * If K_SPECIAL is encountered, then it is taken in conjunction with the
453: * following character and shown as <F1>, <S-Up> etc. In addition, if 'all'
454: * is TRUE, then any other character which has its 8th bit set is shown as
455: * <M-x>, where x is the equivalent character without its 8th bit set. If a
456: * character is displayed in one of these special ways, is also highlighted
457: * (its highlight name is '8' in the p_hl variable).
458: * This function is used to show mappings, where we want to see how to type
459: * the character/string -- webb
460: */
461: int
462: msg_outtrans_special(str, all)
463: register char_u *str;
464: register int all; /* <M-a> etc as well as <F1> etc */
465: {
466: int retval = 0;
467: char_u *string;
468: int c;
469: int modifiers;
470:
471: set_highlight('8');
472: for (; *str; ++str)
473: {
474: c = *str;
475: if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL)
476: {
477: modifiers = 0x0;
478: if (str[1] == KS_MODIFIER)
479: {
480: modifiers = str[2];
481: str += 3;
482: c = *str;
483: }
484: if (c == K_SPECIAL)
485: {
486: c = TO_SPECIAL(str[1], str[2]);
487: str += 2;
488: if (c == K_ZERO) /* display <Nul> as ^@ */
489: c = NUL;
490: }
491: if (IS_SPECIAL(c) || modifiers) /* special key */
492: {
493: string = get_special_key_name(c, modifiers);
494: start_highlight();
495: msg_outstr(string);
496: retval += STRLEN(string);
497: stop_highlight();
498: flushbuf(); /* Otherwise gets overwritten by spaces */
499: continue;
500: }
501: }
502: if ((c & 0x80) && all)
503: {
504: start_highlight();
505: MSG_OUTSTR("<M-");
506: msg_outstr(transchar(c & 0x7f));
507: retval += 2 + charsize(c & 0x7f);
508: MSG_OUTSTR(">");
509: stop_highlight();
510: }
511: else
512: {
513: msg_outstr(transchar(c));
514: retval += charsize(c);
515: }
516: }
517: return retval;
518: }
519:
520: /*
521: * print line for :p command
522: */
523: void
524: msg_prt_line(s)
525: char_u *s;
526: {
527: register int si = 0;
528: register int c;
529: register int col = 0;
530:
531: int n_extra = 0;
532: int n_spaces = 0;
533: char_u *p = NULL; /* init to make SASC shut up */
534: int n;
535:
536: for (;;)
537: {
538: if (n_extra)
539: {
540: --n_extra;
541: c = *p++;
542: }
543: else if (n_spaces)
544: {
545: --n_spaces;
546: c = ' ';
547: }
548: else
549: {
550: c = s[si++];
551: if (c == TAB && !curwin->w_p_list)
552: {
553: /* tab amount depends on current column */
554: n_spaces = curbuf->b_p_ts - col % curbuf->b_p_ts - 1;
555: c = ' ';
556: }
557: else if (c == NUL && curwin->w_p_list)
558: {
559: p = (char_u *)"";
560: n_extra = 1;
561: c = '$';
562: }
563: else if (c != NUL && (n = charsize(c)) > 1)
564: {
565: n_extra = n - 1;
566: p = transchar(c);
567: c = *p++;
568: }
569: }
570:
571: if (c == NUL)
572: break;
573:
574: msg_outchar(c);
575: col++;
576: }
577: }
578:
579: /*
580: * output a string to the screen at position msg_row, msg_col
581: * Update msg_row and msg_col for the next message.
582: */
583: void
584: msg_outstr(s)
585: char_u *s;
586: {
587: int oldState;
588: char_u buf[20];
589:
590: /*
591: * If there is no valid screen, use fprintf so we can see error messages.
592: * If termcap is not active, we may be writing in an alternate console
593: * window, cursor positioning may not work correctly (window size may be
594: * different, e.g. for WIN32 console).
595: */
596: if (!msg_check_screen()
597: #ifdef WIN32
598: || !termcap_active
599: #endif
600: )
601: {
602: #ifdef WIN32
603: mch_settmode(0); /* cook so that \r and \n are handled correctly */
604: #endif
605: fprintf(stderr, (char *)s);
606: msg_didout = TRUE; /* assume that line is not empty */
607: #ifdef WIN32
608: mch_settmode(1);
609: #endif
610: return;
611: }
612:
613: msg_didany = TRUE; /* remember that something was outputted */
614: while (*s)
615: {
616: /*
617: * The screen is scrolled up when:
618: * - When outputting a newline in the last row
619: * - when outputting a character in the last column of the last row
620: * (some terminals scroll automatically, some don't. To avoid
621: * problems we scroll ourselves)
622: */
623: if (msg_row >= Rows - 1 && (*s == '\n' || msg_col >= Columns - 1 ||
624: (*s == TAB && msg_col >= ((Columns - 1) & ~7))))
625: {
626: screen_del_lines(0, 0, 1, (int)Rows, TRUE); /* always works */
627: msg_row = Rows - 2;
628: if (msg_col >= Columns) /* can happen after screen resize */
629: msg_col = Columns - 1;
630: ++msg_scrolled;
631: need_wait_return = TRUE; /* may need wait_return in main() */
632: if (cmdline_row > 0)
633: --cmdline_row;
634: /*
635: * if screen is completely filled wait for a character
636: */
637: if (p_more && --lines_left == 0 && State != HITRETURN)
638: {
639: oldState = State;
640: State = ASKMORE;
641: #ifdef USE_MOUSE
642: setmouse();
643: #endif
644: msg_moremsg(FALSE);
645: for (;;)
646: {
647: /*
648: * Get a typed character directly from the user.
649: * Don't use vgetc(), it syncs undo and eats mapped
650: * characters. Disadvantage: Special keys and mouse
651: * cannot be used here, typeahead is ignored.
652: */
653: flushbuf();
654: (void)mch_inchar(buf, 20, -1L);
655: switch (buf[0])
656: {
657: case CR: /* one extra line */
658: case NL:
659: lines_left = 1;
660: break;
661: case ':': /* start new command line */
662: stuffcharReadbuff(':');
663: cmdline_row = Rows - 1; /* put ':' on this line */
664: skip_redraw = TRUE; /* skip redraw once */
665: dont_wait_return = TRUE; /* don't wait in main() */
666: /*FALLTHROUGH*/
667: case 'q': /* quit */
668: case Ctrl('C'):
669: got_int = TRUE;
670: quit_more = TRUE;
671: break;
672: case 'd': /* Down half a page */
673: lines_left = Rows / 2;
674: break;
675: case ' ': /* one extra page */
676: lines_left = Rows - 1;
677: break;
678: default: /* no valid response */
679: msg_moremsg(TRUE);
680: continue;
681: }
682: break;
683: }
684: /* clear the --more-- message */
685: screen_fill((int)Rows - 1, (int)Rows,
686: 0, (int)Columns, ' ', ' ');
687: State = oldState;
688: #ifdef USE_MOUSE
689: setmouse();
690: #endif
691: if (quit_more)
692: {
693: msg_row = Rows - 1;
694: msg_col = 0;
695: return; /* the string is not displayed! */
696: }
697: }
698: }
699: if (*s == '\n') /* go to next line */
700: {
701: msg_didout = FALSE; /* remember that line is empty */
702: msg_col = 0;
703: if (++msg_row >= Rows) /* safety check */
704: msg_row = Rows - 1;
705: }
706: else if (*s == '\r') /* go to column 0 */
707: {
708: msg_col = 0;
709: }
710: else if (*s == '\b') /* go to previous char */
711: {
712: if (msg_col)
713: --msg_col;
714: }
715: else if (*s == TAB) /* translate into spaces */
716: {
717: do
718: msg_screen_outchar(' ');
719: while (msg_col & 7);
720: }
721: else
722: msg_screen_outchar(*s);
723: ++s;
724: }
725: }
726:
727: static void
728: msg_screen_outchar(c)
729: int c;
730: {
731: msg_didout = TRUE; /* remember that line is not empty */
732: screen_outchar(c, msg_row, msg_col);
733: if (++msg_col >= Columns)
734: {
735: msg_col = 0;
736: ++msg_row;
737: }
738: }
739:
740: void
741: msg_moremsg(full)
742: int full;
743: {
744: /*
745: * Need to restore old highlighting when we've finished with it
746: * because the output that's paging may be relying on it not
747: * changing -- webb
748: */
749: remember_highlight();
750: set_highlight('m');
751: start_highlight();
752: screen_msg((char_u *)"-- More --", (int)Rows - 1, 0);
753: if (full)
754: screen_msg((char_u *)" (RET: line, SPACE: page, d: half page, q: quit)",
755: (int)Rows - 1, 10);
756: stop_highlight();
757: recover_old_highlight();
758: }
759:
760: /*
761: * msg_check_screen - check if the screen is initialized.
762: * Also check msg_row and msg_col, if they are too big it may cause a crash.
763: */
764: static int
765: msg_check_screen()
766: {
767: if (!full_screen || !screen_valid(FALSE))
768: return FALSE;
769:
770: if (msg_row >= Rows)
771: msg_row = Rows - 1;
772: if (msg_col >= Columns)
773: msg_col = Columns - 1;
774: return TRUE;
775: }
776:
777: /*
778: * clear from current message position to end of screen
779: * Note: msg_col is not updated, so we remember the end of the message
780: * for msg_check().
781: */
782: void
783: msg_clr_eos()
784: {
785: if (!msg_check_screen()
786: #ifdef WIN32
787: || !termcap_active
788: #endif
789: )
790: return;
791: screen_fill(msg_row, msg_row + 1, msg_col, (int)Columns, ' ', ' ');
792: screen_fill(msg_row + 1, (int)Rows, 0, (int)Columns, ' ', ' ');
793: }
794:
795: /*
796: * end putting a message on the screen
797: * call wait_return if the message does not fit in the available space
798: * return TRUE if wait_return not called.
799: */
800: int
801: msg_end()
802: {
803: /*
804: * if the string is larger than the window,
805: * or the ruler option is set and we run into it,
806: * we have to redraw the window.
807: * Do not do this if we are abandoning the file or editing the command line.
808: */
809: if (!exiting && msg_check() && State != CMDLINE)
810: {
811: wait_return(FALSE);
812: return FALSE;
813: }
814: flushbuf();
815: return TRUE;
816: }
817:
818: /*
819: * If the written message has caused the screen to scroll up, or if we
820: * run into the shown command or ruler, we have to redraw the window later.
821: */
822: int
823: msg_check()
824: {
825: if (msg_scrolled || (msg_row == Rows - 1 && msg_col >= sc_col))
826: {
827: redraw_later(NOT_VALID);
828: redraw_cmdline = TRUE;
829: return TRUE;
830: }
831: return FALSE;
832: }