Commit | Line | Data |
---|---|---|
15637ed4 RG |
1 | /* vcmd.c */ |
2 | ||
3 | /* Author: | |
4 | * Steve Kirkendall | |
5 | * 14407 SW Teal Blvd. #C | |
6 | * Beaverton, OR 97005 | |
7 | * kirkenda@cs.pdx.edu | |
8 | */ | |
9 | ||
10 | ||
11 | /* This file contains the functions that handle VI commands */ | |
12 | ||
13 | ||
14 | #include "config.h" | |
15 | #include "ctype.h" | |
16 | #include "vi.h" | |
17 | #if MSDOS | |
18 | # include <process.h> | |
19 | # include <string.h> | |
20 | #endif | |
21 | #if TOS | |
22 | # include <osbind.h> | |
23 | # include <string.h> | |
24 | #endif | |
25 | #if OSK | |
26 | # include <stdio.h> | |
27 | #endif | |
28 | ||
29 | ||
30 | /* This function puts the editor in EX mode */ | |
31 | MARK v_quit() | |
32 | { | |
33 | move(LINES - 1, 0); | |
34 | mode = MODE_EX; | |
35 | return cursor; | |
36 | } | |
37 | ||
38 | /* This function causes the screen to be redrawn */ | |
39 | MARK v_redraw() | |
40 | { | |
41 | redraw(MARK_UNSET, FALSE); | |
42 | return cursor; | |
43 | } | |
44 | ||
45 | /* This function executes a string of EX commands, and waits for a user keystroke | |
46 | * before returning to the VI screen. If that keystroke is another ':', then | |
47 | * another EX command is read and executed. | |
48 | */ | |
49 | /*ARGSUSED*/ | |
50 | MARK v_1ex(m, text) | |
51 | MARK m; /* the current line */ | |
52 | char *text; /* the first command to execute */ | |
53 | { | |
54 | /* run the command. be careful about modes & output */ | |
55 | exwrote = (mode == MODE_COLON); | |
56 | doexcmd(text); | |
57 | exrefresh(); | |
58 | ||
59 | /* if mode is no longer MODE_VI, then we should quit right away! */ | |
60 | if (mode != MODE_VI && mode != MODE_COLON) | |
61 | return cursor; | |
62 | ||
63 | /* The command did some output. Wait for a keystoke. */ | |
64 | if (exwrote) | |
65 | { | |
66 | mode = MODE_VI; | |
67 | msg("[Hit <RETURN> to continue]"); | |
68 | if (getkey(0) == ':') | |
69 | { mode = MODE_COLON; | |
70 | addch('\n'); | |
71 | } | |
72 | else | |
73 | redraw(MARK_UNSET, FALSE); | |
74 | } | |
75 | ||
76 | return cursor; | |
77 | } | |
78 | ||
79 | /* This function undoes the last change */ | |
80 | /*ARGSUSED*/ | |
81 | MARK v_undo(m) | |
82 | MARK m; /* (ignored) */ | |
83 | { | |
84 | if (undo()) | |
85 | { | |
86 | redraw(MARK_UNSET, FALSE); | |
87 | } | |
88 | return cursor; | |
89 | } | |
90 | ||
91 | /* This function deletes the character(s) that the cursor is on */ | |
92 | MARK v_xchar(m, cnt, cmd) | |
93 | MARK m; /* where to start deletions */ | |
94 | long cnt; /* number of chars to delete */ | |
95 | int cmd; /* either 'x' or 'X' */ | |
96 | { | |
97 | DEFAULT(1); | |
98 | ||
99 | /* for 'X', adjust so chars are deleted *BEFORE* cursor */ | |
100 | if (cmd == 'X') | |
101 | { | |
102 | if (markidx(m) < cnt) | |
103 | return MARK_UNSET; | |
104 | m -= cnt; | |
105 | } | |
106 | ||
107 | /* make sure we don't try to delete more thars than there are */ | |
108 | pfetch(markline(m)); | |
109 | if (markidx(m + cnt) > plen) | |
110 | { | |
111 | cnt = plen - markidx(m); | |
112 | } | |
113 | if (cnt == 0L) | |
114 | { | |
115 | return MARK_UNSET; | |
116 | } | |
117 | ||
118 | /* do it */ | |
119 | ChangeText | |
120 | { | |
121 | cut(m, m + cnt); | |
122 | delete(m, m + cnt); | |
123 | } | |
124 | return m; | |
125 | } | |
126 | ||
127 | /* This function defines a mark */ | |
128 | /*ARGSUSED*/ | |
129 | MARK v_mark(m, count, key) | |
130 | MARK m; /* where the mark will be */ | |
131 | long count; /* (ignored) */ | |
132 | int key; /* the ASCII label of the mark */ | |
133 | { | |
134 | if (key < 'a' || key > 'z') | |
135 | { | |
136 | msg("Marks must be from a to z"); | |
137 | } | |
138 | else | |
139 | { | |
140 | mark[key - 'a'] = m; | |
141 | } | |
142 | return m; | |
143 | } | |
144 | ||
145 | /* This function toggles upper & lower case letters */ | |
146 | MARK v_ulcase(m, cnt) | |
147 | MARK m; /* where to make the change */ | |
148 | long cnt; /* number of chars to flip */ | |
149 | { | |
150 | REG char *pos; | |
151 | REG int j; | |
152 | ||
153 | DEFAULT(1); | |
154 | ||
155 | /* fetch the current version of the line */ | |
156 | pfetch(markline(m)); | |
157 | ||
158 | /* for each position in the line */ | |
159 | for (j = 0, pos = &ptext[markidx(m)]; j < cnt && *pos; j++, pos++) | |
160 | { | |
161 | if (isupper(*pos)) | |
162 | { | |
163 | tmpblk.c[j] = tolower(*pos); | |
164 | } | |
165 | else | |
166 | { | |
167 | tmpblk.c[j] = toupper(*pos); | |
168 | } | |
169 | } | |
170 | ||
171 | /* if the new text is different from the old, then change it */ | |
172 | if (strncmp(tmpblk.c, &ptext[markidx(m)], j)) | |
173 | { | |
174 | ChangeText | |
175 | { | |
176 | tmpblk.c[j] = '\0'; | |
177 | change(m, m + j, tmpblk.c); | |
178 | } | |
179 | } | |
180 | ||
181 | return m + j; | |
182 | } | |
183 | ||
184 | ||
185 | MARK v_replace(m, cnt, key) | |
186 | MARK m; /* first char to be replaced */ | |
187 | long cnt; /* number of chars to replace */ | |
188 | int key; /* what to replace them with */ | |
189 | { | |
190 | REG char *text; | |
191 | REG int i; | |
192 | ||
193 | DEFAULT(1); | |
194 | ||
195 | /* map ^M to '\n' */ | |
196 | if (key == '\r') | |
197 | { | |
198 | key = '\n'; | |
199 | } | |
200 | ||
201 | /* make sure the resulting line isn't too long */ | |
202 | if (cnt > BLKSIZE - 2 - markidx(m)) | |
203 | { | |
204 | cnt = BLKSIZE - 2 - markidx(m); | |
205 | } | |
206 | ||
207 | /* build a string of the desired character with the desired length */ | |
208 | for (text = tmpblk.c, i = cnt; i > 0; i--) | |
209 | { | |
210 | *text++ = key; | |
211 | } | |
212 | *text = '\0'; | |
213 | ||
214 | /* make sure cnt doesn't extend past EOL */ | |
215 | pfetch(markline(m)); | |
216 | key = markidx(m); | |
217 | if (key + cnt > plen) | |
218 | { | |
219 | cnt = plen - key; | |
220 | } | |
221 | ||
222 | /* do the replacement */ | |
223 | ChangeText | |
224 | { | |
225 | change(m, m + cnt, tmpblk.c); | |
226 | } | |
227 | ||
228 | if (*tmpblk.c == '\n') | |
229 | { | |
230 | return (m & ~(BLKSIZE - 1)) + cnt * BLKSIZE; | |
231 | } | |
232 | else | |
233 | { | |
234 | return m + cnt - 1; | |
235 | } | |
236 | } | |
237 | ||
238 | MARK v_overtype(m) | |
239 | MARK m; /* where to start overtyping */ | |
240 | { | |
241 | MARK end; /* end of a substitution */ | |
242 | static long width; /* width of a single-line replace */ | |
243 | ||
244 | /* the "doingdot" version of replace is really a substitution */ | |
245 | if (doingdot) | |
246 | { | |
247 | /* was the last one really repeatable? */ | |
248 | if (width < 0) | |
249 | { | |
250 | msg("Can't repeat a multi-line overtype command"); | |
251 | return MARK_UNSET; | |
252 | } | |
253 | ||
254 | /* replacing nothing by nothing? Don't bother */ | |
255 | if (width == 0) | |
256 | { | |
257 | return m; | |
258 | } | |
259 | ||
260 | /* replace some chars by repeated text */ | |
261 | return v_subst(m, width); | |
262 | } | |
263 | ||
264 | /* Normally, we input starting here, in replace mode */ | |
265 | ChangeText | |
266 | { | |
08746e8b | 267 | end = input(m, m, WHEN_VIREP, 0); |
15637ed4 RG |
268 | } |
269 | ||
270 | /* if we ended on the same line we started on, then this | |
271 | * overtype is repeatable via the dot key. | |
272 | */ | |
273 | if (markline(end) == markline(m) && end >= m - 1L) | |
274 | { | |
275 | width = end - m + 1L; | |
276 | } | |
277 | else /* it isn't repeatable */ | |
278 | { | |
279 | width = -1L; | |
280 | } | |
281 | ||
282 | return end; | |
283 | } | |
284 | ||
285 | ||
286 | /* This function selects which cut buffer to use */ | |
287 | /*ARGSUSED*/ | |
288 | MARK v_selcut(m, cnt, key) | |
289 | MARK m; | |
290 | long cnt; | |
291 | int key; | |
292 | { | |
293 | cutname(key); | |
294 | return m; | |
295 | } | |
296 | ||
297 | /* This function pastes text from a cut buffer */ | |
298 | /*ARGSUSED*/ | |
299 | MARK v_paste(m, cnt, cmd) | |
300 | MARK m; /* where to paste the text */ | |
301 | long cnt; /* (ignored) */ | |
302 | int cmd; /* either 'p' or 'P' */ | |
303 | { | |
304 | MARK dest; | |
305 | ||
306 | ChangeText | |
307 | { | |
308 | /* paste the text, and find out where it ends */ | |
309 | dest = paste(m, cmd == 'p', TRUE); | |
310 | ||
311 | /* was that a line-mode paste? */ | |
312 | if (dest && markline(dest) != markline(m)) | |
313 | { | |
314 | /* line-mode pastes leave the cursor at the front | |
315 | * of the first pasted line. | |
316 | */ | |
317 | dest = m; | |
318 | if (cmd == 'p') | |
319 | { | |
320 | dest += BLKSIZE; | |
321 | } | |
322 | force_flags |= FRNT; | |
323 | } | |
324 | } | |
325 | return dest; | |
326 | } | |
327 | ||
328 | /* This function yanks text into a cut buffer */ | |
329 | MARK v_yank(m, n) | |
330 | MARK m, n; /* range of text to yank */ | |
331 | { | |
332 | cut(m, n); | |
333 | return m; | |
334 | } | |
335 | ||
336 | /* This function deletes a range of text */ | |
337 | MARK v_delete(m, n) | |
338 | MARK m, n; /* range of text to delete */ | |
339 | { | |
340 | /* illegal to try and delete nothing */ | |
341 | if (n <= m) | |
342 | { | |
343 | return MARK_UNSET; | |
344 | } | |
345 | ||
346 | /* Do it */ | |
347 | ChangeText | |
348 | { | |
349 | cut(m, n); | |
350 | delete(m, n); | |
351 | } | |
352 | return m; | |
353 | } | |
354 | ||
355 | ||
356 | /* This starts input mode without deleting anything */ | |
357 | MARK v_insert(m, cnt, key) | |
358 | MARK m; /* where to start (sort of) */ | |
359 | long cnt; /* repeat how many times? */ | |
360 | int key; /* what command is this for? {a,A,i,I,o,O} */ | |
361 | { | |
362 | int wasdot; | |
363 | long reps; | |
08746e8b | 364 | int delta = 0;/* 1 to take autoindent from line below, -1 for above */ |
15637ed4 RG |
365 | |
366 | DEFAULT(1); | |
367 | ||
368 | ChangeText | |
369 | { | |
370 | /* tweak the insertion point, based on command key */ | |
15637ed4 RG |
371 | switch (key) |
372 | { | |
373 | case 'i': | |
374 | break; | |
375 | ||
376 | case 'a': | |
377 | pfetch(markline(m)); | |
378 | if (plen > 0) | |
379 | { | |
380 | m++; | |
381 | } | |
382 | break; | |
383 | ||
384 | case 'I': | |
385 | m = m_front(m, 1L); | |
386 | break; | |
387 | ||
388 | case 'A': | |
389 | pfetch(markline(m)); | |
390 | m = (m & ~(BLKSIZE - 1)) + plen; | |
391 | break; | |
392 | ||
393 | case 'O': | |
394 | m &= ~(BLKSIZE - 1); | |
395 | add(m, "\n"); | |
08746e8b | 396 | delta = 1; |
15637ed4 RG |
397 | break; |
398 | ||
399 | case 'o': | |
400 | m = (m & ~(BLKSIZE - 1)) + BLKSIZE; | |
401 | add(m, "\n"); | |
08746e8b | 402 | delta = -1; |
15637ed4 RG |
403 | break; |
404 | } | |
405 | ||
406 | /* insert the same text once or more */ | |
407 | for (reps = cnt, wasdot = doingdot; reps > 0; reps--, doingdot = TRUE) | |
408 | { | |
08746e8b | 409 | m = input(m, m, WHEN_VIINP, delta) + 1; |
15637ed4 RG |
410 | } |
411 | if (markidx(m) > 0) | |
412 | { | |
413 | m--; | |
414 | } | |
415 | ||
416 | doingdot = wasdot; | |
417 | } | |
418 | ||
419 | #ifndef CRUNCH | |
420 | # ifndef NO_EXTENSIONS | |
421 | if (key == 'i' && *o_inputmode && mode == MODE_VI) | |
422 | { | |
423 | msg("Now in command mode! To return to input mode, hit <i>"); | |
424 | } | |
425 | # endif | |
426 | #endif | |
427 | ||
428 | return m; | |
429 | } | |
430 | ||
431 | /* This starts input mode with some text deleted */ | |
432 | MARK v_change(m, n) | |
433 | MARK m, n; /* the range of text to change */ | |
434 | { | |
435 | int lnmode; /* is this a line-mode change? */ | |
436 | ||
437 | /* swap them if they're in reverse order */ | |
438 | if (m > n) | |
439 | { | |
440 | MARK tmp; | |
441 | tmp = m; | |
442 | m = n; | |
443 | n = tmp; | |
444 | } | |
445 | ||
446 | /* for line mode, retain the last newline char */ | |
447 | lnmode = (markidx(m) == 0 && markidx(n) == 0 && m != n); | |
448 | if (lnmode) | |
449 | { | |
450 | n -= BLKSIZE; | |
451 | pfetch(markline(n)); | |
452 | n = (n & ~(BLKSIZE - 1)) + plen; | |
453 | } | |
454 | ||
455 | ChangeText | |
456 | { | |
457 | cut(m, n); | |
08746e8b | 458 | m = input(m, n, WHEN_VIINP, 0); |
15637ed4 RG |
459 | } |
460 | ||
461 | return m; | |
462 | } | |
463 | ||
464 | /* This function replaces a given number of characters with input */ | |
465 | MARK v_subst(m, cnt) | |
466 | MARK m; /* where substitutions start */ | |
467 | long cnt; /* number of chars to replace */ | |
468 | { | |
469 | DEFAULT(1); | |
470 | ||
471 | /* make sure we don't try replacing past EOL */ | |
472 | pfetch(markline(m)); | |
473 | if (markidx(m) + cnt > plen) | |
474 | { | |
475 | cnt = plen - markidx(m); | |
476 | } | |
477 | ||
478 | /* Go for it! */ | |
479 | ChangeText | |
480 | { | |
481 | cut(m, m + cnt); | |
08746e8b | 482 | m = input(m, m + cnt, WHEN_VIINP, 0); |
15637ed4 RG |
483 | } |
484 | return m; | |
485 | } | |
486 | ||
487 | /* This calls the ex "join" command to join some lines together */ | |
488 | MARK v_join(m, cnt) | |
489 | MARK m; /* the first line to be joined */ | |
490 | long cnt; /* number of other lines to join */ | |
491 | { | |
492 | MARK joint; /* where the lines were joined */ | |
493 | ||
494 | DEFAULT(1); | |
495 | ||
496 | /* figure out where the joint will be */ | |
497 | pfetch(markline(m)); | |
498 | joint = (m & ~(BLKSIZE - 1)) + plen; | |
499 | ||
500 | /* join the lines */ | |
501 | cmd_join(m, m + MARK_AT_LINE(cnt), CMD_JOIN, 0, ""); | |
502 | ||
503 | /* the cursor should be left at the joint */ | |
504 | return joint; | |
505 | } | |
506 | ||
507 | ||
508 | /* This calls the ex "<" command to shift some lines left */ | |
509 | MARK v_lshift(m, n) | |
510 | MARK m, n; /* range of lines to shift */ | |
511 | { | |
512 | /* adjust for inclusive endmarks in ex */ | |
513 | n -= BLKSIZE; | |
514 | ||
515 | cmd_shift(m, n, CMD_SHIFTL, FALSE, (char *)0); | |
516 | ||
517 | return m; | |
518 | } | |
519 | ||
520 | /* This calls the ex ">" command to shift some lines right */ | |
521 | MARK v_rshift(m, n) | |
522 | MARK m, n; /* range of lines to shift */ | |
523 | { | |
524 | /* adjust for inclusive endmarks in ex */ | |
525 | n -= BLKSIZE; | |
526 | ||
527 | cmd_shift(m, n, CMD_SHIFTR, FALSE, (char *)0); | |
528 | ||
529 | return m; | |
530 | } | |
531 | ||
532 | /* This filters some lines through a preset program, to reformat them */ | |
533 | MARK v_reformat(m, n) | |
534 | MARK m, n; /* range of lines to shift */ | |
535 | { | |
536 | /* adjust for inclusive endmarks in ex */ | |
537 | n -= BLKSIZE; | |
538 | ||
539 | /* run the filter command */ | |
540 | filter(m, n, o_equalprg, TRUE); | |
541 | ||
542 | redraw(MARK_UNSET, FALSE); | |
543 | return m; | |
544 | } | |
545 | ||
546 | ||
547 | /* This runs some lines through a filter program */ | |
548 | MARK v_filter(m, n) | |
549 | MARK m, n; /* range of lines to shift */ | |
550 | { | |
551 | char cmdln[150]; /* a shell command line */ | |
552 | ||
553 | /* adjust for inclusive endmarks in ex */ | |
554 | n -= BLKSIZE; | |
555 | ||
556 | if (vgets('!', cmdln, sizeof(cmdln)) > 0) | |
557 | { | |
558 | filter(m, n, cmdln, TRUE); | |
559 | } | |
560 | ||
561 | redraw(MARK_UNSET, FALSE); | |
562 | return m; | |
563 | } | |
564 | ||
565 | ||
566 | /* This function runs the ex "file" command to show the file's status */ | |
567 | MARK v_status() | |
568 | { | |
569 | cmd_file(cursor, cursor, CMD_FILE, 0, ""); | |
570 | return cursor; | |
571 | } | |
572 | ||
573 | ||
574 | /* This function runs the ":&" command to repeat the previous :s// */ | |
575 | MARK v_again(m, n) | |
576 | MARK m, n; | |
577 | { | |
578 | cmd_substitute(m, n - BLKSIZE, CMD_SUBAGAIN, TRUE, ""); | |
579 | return cursor; | |
580 | } | |
581 | ||
582 | ||
583 | ||
584 | /* This function switches to the previous file, if possible */ | |
585 | MARK v_switch() | |
586 | { | |
587 | if (!*prevorig) | |
588 | msg("No previous file"); | |
589 | else | |
590 | { strcpy(tmpblk.c, prevorig); | |
591 | cmd_edit(cursor, cursor, CMD_EDIT, 0, tmpblk.c); | |
592 | } | |
593 | return cursor; | |
594 | } | |
595 | ||
596 | /* This function does a tag search on a keyword */ | |
597 | /*ARGSUSED*/ | |
598 | MARK v_tag(keyword, m, cnt) | |
599 | char *keyword; | |
600 | MARK m; | |
601 | long cnt; | |
602 | { | |
603 | /* move the cursor to the start of the tag name, where m is */ | |
604 | cursor = m; | |
605 | ||
606 | /* perform the tag search */ | |
607 | cmd_tag(cursor, cursor, CMD_TAG, 0, keyword); | |
608 | ||
609 | return cursor; | |
610 | } | |
611 | ||
612 | #ifndef NO_EXTENSIONS | |
613 | /* This function looks up a keyword by calling the helpprog program */ | |
614 | /*ARGSUSED*/ | |
615 | MARK v_keyword(keyword, m, cnt) | |
616 | char *keyword; | |
617 | MARK m; | |
618 | long cnt; | |
619 | { | |
620 | int waswarn; | |
621 | char cmdline[130]; | |
622 | ||
623 | move(LINES - 1, 0); | |
624 | addstr("---------------------------------------------------------\n"); | |
625 | clrtoeol(); | |
626 | refresh(); | |
627 | sprintf(cmdline, "%s %s", o_keywordprg, keyword); | |
628 | waswarn = *o_warn; | |
629 | *o_warn = FALSE; | |
630 | suspend_curses(); | |
631 | if (system(cmdline)) | |
632 | { | |
633 | addstr("<<< failed >>>\n"); | |
634 | } | |
635 | resume_curses(FALSE); | |
636 | mode = MODE_VI; | |
637 | redraw(MARK_UNSET, FALSE); | |
638 | *o_warn = waswarn; | |
639 | ||
640 | return m; | |
641 | } | |
642 | ||
643 | ||
644 | ||
645 | MARK v_increment(keyword, m, cnt) | |
646 | char *keyword; | |
647 | MARK m; | |
648 | long cnt; | |
649 | { | |
650 | static sign; | |
651 | char newval[12]; | |
652 | long atol(); | |
653 | ||
654 | DEFAULT(1); | |
655 | ||
656 | /* get one more keystroke, unless doingdot */ | |
657 | if (!doingdot) | |
658 | { | |
659 | sign = getkey(0); | |
660 | } | |
661 | ||
662 | /* adjust the number, based on that second keystroke */ | |
663 | switch (sign) | |
664 | { | |
665 | case '+': | |
666 | case '#': | |
667 | cnt = atol(keyword) + cnt; | |
668 | break; | |
669 | ||
670 | case '-': | |
671 | cnt = atol(keyword) - cnt; | |
672 | break; | |
673 | ||
674 | case '=': | |
675 | break; | |
676 | ||
677 | default: | |
678 | return MARK_UNSET; | |
679 | } | |
680 | sprintf(newval, "%ld", cnt); | |
681 | ||
682 | ChangeText | |
683 | { | |
684 | change(m, m + strlen(keyword), newval); | |
685 | } | |
686 | ||
687 | return m; | |
688 | } | |
689 | #endif | |
690 | ||
691 | ||
692 | /* This function acts like the EX command "xit" */ | |
693 | /*ARGSUSED*/ | |
694 | MARK v_xit(m, cnt, key) | |
695 | MARK m; /* ignored */ | |
696 | long cnt; /* ignored */ | |
697 | int key; /* must be a second 'Z' */ | |
698 | { | |
699 | /* if second char wasn't 'Z', fail */ | |
700 | if (key != 'Z') | |
701 | { | |
702 | return MARK_UNSET; | |
703 | } | |
704 | ||
705 | /* move the cursor to the bottom of the screen */ | |
706 | move(LINES - 1, 0); | |
707 | clrtoeol(); | |
708 | ||
709 | /* do the xit command */ | |
710 | cmd_xit(m, m, CMD_XIT, FALSE, ""); | |
711 | ||
712 | /* return the cursor */ | |
713 | return m; | |
714 | } | |
715 | ||
716 | ||
717 | /* This function undoes changes to a single line, if possible */ | |
718 | MARK v_undoline(m) | |
719 | MARK m; /* where we hope to undo the change */ | |
720 | { | |
721 | /* make sure we have the right line in the buffer */ | |
722 | if (markline(m) != U_line) | |
723 | { | |
724 | return MARK_UNSET; | |
725 | } | |
726 | ||
727 | /* fix it */ | |
728 | ChangeText | |
729 | { | |
730 | strcat(U_text, "\n"); | |
731 | change(MARK_AT_LINE(U_line), MARK_AT_LINE(U_line + 1), U_text); | |
732 | } | |
733 | ||
734 | /* nothing in the buffer anymore */ | |
735 | U_line = -1L; | |
736 | ||
737 | /* return, with the cursor at the front of the line */ | |
738 | return m & ~(BLKSIZE - 1); | |
739 | } | |
740 | ||
741 | ||
742 | #ifndef NO_ERRLIST | |
743 | MARK v_errlist(m) | |
744 | MARK m; | |
745 | { | |
746 | cmd_errlist(m, m, CMD_ERRLIST, FALSE, ""); | |
747 | return cursor; | |
748 | } | |
749 | #endif | |
750 | ||
751 | ||
752 | #ifndef NO_AT | |
753 | /*ARGSUSED*/ | |
754 | MARK v_at(m, cnt, key) | |
755 | MARK m; | |
756 | long cnt; | |
757 | int key; | |
758 | { | |
759 | int size; | |
760 | ||
761 | size = cb2str(key, tmpblk.c, BLKSIZE); | |
762 | if (size <= 0 || size == BLKSIZE) | |
763 | { | |
764 | return MARK_UNSET; | |
765 | } | |
766 | ||
767 | execmap(0, tmpblk.c, FALSE); | |
768 | return cursor; | |
769 | } | |
770 | #endif | |
771 | ||
772 | ||
773 | #ifdef SIGTSTP | |
774 | MARK v_suspend() | |
775 | { | |
776 | cmd_suspend(MARK_UNSET, MARK_UNSET, CMD_SUSPEND, FALSE, ""); | |
777 | return MARK_UNSET; | |
778 | } | |
779 | #endif | |
780 | ||
781 | ||
782 | #ifndef NO_VISIBLE | |
783 | /*ARGSUSED*/ | |
784 | MARK v_start(m, cnt, cmd) | |
785 | MARK m; /* starting point for a v or V command */ | |
786 | long cnt; /* ignored */ | |
787 | int cmd; /* either 'v' or 'V' */ | |
788 | { | |
789 | if (V_from) | |
790 | { | |
791 | V_from = MARK_UNSET; | |
792 | } | |
793 | else | |
794 | { | |
795 | V_from = m; | |
796 | V_linemd = isupper(cmd); | |
797 | } | |
798 | return m; | |
799 | } | |
800 | #endif | |
801 | ||
802 | #ifndef NO_POPUP | |
803 | # define MENU_HEIGHT 11 | |
804 | # define MENU_WIDTH 23 | |
805 | MARK v_popup(m, n) | |
806 | MARK m, n; /* the range of text to change */ | |
807 | { | |
808 | int i; | |
809 | int y, x; /* position where the window will pop up at */ | |
810 | int key; /* keystroke from the user */ | |
811 | int sel; /* index of the selected operation */ | |
812 | static int dfltsel;/* default value of sel */ | |
813 | static char *labels[11] = | |
814 | { | |
815 | "ESC cancel! ", | |
816 | " d delete (cut) ", | |
817 | " y yank (copy) ", | |
818 | " p paste after ", | |
819 | " P paste before ", | |
820 | " > more indented ", | |
821 | " < less indented ", | |
822 | " = reformat ", | |
823 | " ! external filter ", | |
824 | " ZZ save & exit ", | |
825 | " u undo previous " | |
826 | }; | |
827 | ||
828 | /* try to position the menu near the cursor */ | |
829 | x = physcol - (MENU_WIDTH / 2); | |
830 | if (x < 0) | |
831 | x = 0; | |
832 | else if (x + MENU_WIDTH + 2 > COLS) | |
833 | x = COLS - MENU_WIDTH - 2; | |
834 | if (markline(cursor) < topline || markline(cursor) > botline) | |
835 | y = 0; | |
836 | else if (markline(cursor) + 1L + MENU_HEIGHT > botline) | |
837 | y = (int)(markline(cursor) - topline) - MENU_HEIGHT; | |
838 | else | |
839 | y = (int)(markline(cursor) - topline) + 1L; | |
840 | ||
841 | /* draw the menu */ | |
842 | for (sel = 0; sel < MENU_HEIGHT; sel++) | |
843 | { | |
844 | move(y + sel, x); | |
845 | do_POPUP(); | |
846 | if (sel == dfltsel) | |
847 | qaddstr("-->"); | |
848 | else | |
849 | qaddstr(" "); | |
850 | qaddstr(labels[sel]); | |
851 | do_SE(); | |
852 | } | |
853 | ||
854 | /* get a selection */ | |
855 | move(y + dfltsel, x + 4); | |
856 | for (sel = dfltsel; (key = getkey(WHEN_POPUP)) != '\\' && key != '\r'; ) | |
857 | { | |
858 | /* erase the selection arrow */ | |
859 | move(y + sel, x); | |
860 | do_POPUP(); | |
861 | qaddstr(" "); | |
862 | qaddstr(labels[sel]); | |
863 | do_SE(); | |
864 | ||
865 | /* process the user's keystroke */ | |
866 | if (key == 'j' && sel < MENU_HEIGHT - 1) | |
867 | { | |
868 | sel++; | |
869 | } | |
870 | else if (key == 'k' && sel > 0) | |
871 | { | |
872 | sel--; | |
873 | } | |
874 | else if (key == '\033') | |
875 | { | |
876 | sel = 0; | |
877 | break; | |
878 | } | |
879 | else | |
880 | { | |
881 | for (i = 1; i < MENU_HEIGHT && labels[i][1] != key; i++) | |
882 | { | |
883 | } | |
884 | if (i < MENU_HEIGHT) | |
885 | { | |
886 | sel = i; | |
887 | break; | |
888 | } | |
889 | } | |
890 | ||
891 | /* redraw the arrow, possibly in a new place */ | |
892 | move(y + sel, x); | |
893 | do_POPUP(); | |
894 | qaddstr("-->"); | |
895 | qaddstr(labels[sel]); | |
896 | do_SE(); | |
897 | move(y + sel, x + 4); | |
898 | } | |
899 | ||
900 | /* in most cases, the default selection is "paste after" */ | |
901 | dfltsel = 3; | |
902 | ||
903 | /* perform the appropriate action */ | |
904 | switch (sel) | |
905 | { | |
906 | case 0: | |
907 | m = cursor; | |
908 | dfltsel = 0; | |
909 | break; | |
910 | ||
911 | case 1: /* delete (cut) */ | |
912 | m = v_delete(m, n); | |
913 | break; | |
914 | ||
915 | case 2: /* yank (copy) */ | |
916 | m = v_yank(m, n); | |
917 | break; | |
918 | ||
919 | case 3: /* paste after */ | |
920 | m = v_paste(n, 1L, 'P'); | |
921 | break; | |
922 | ||
923 | case 4: /* paste before */ | |
924 | m = v_paste(m, 1L, 'P'); | |
925 | dfltsel = 4; | |
926 | break; | |
927 | ||
928 | case 5: /* more indented */ | |
929 | m = v_rshift(m, n); | |
930 | dfltsel = 5; | |
931 | break; | |
932 | ||
933 | case 6: /* less indented */ | |
934 | m = v_lshift(m, n); | |
935 | dfltsel = 6; | |
936 | break; | |
937 | ||
938 | case 7: /* reformat */ | |
939 | m = v_reformat(m, n); | |
940 | dfltsel = 7; | |
941 | break; | |
942 | ||
943 | case 8: /* external filter */ | |
944 | m = v_filter(m, n); | |
945 | dfltsel = 0; | |
946 | break; | |
947 | ||
948 | case 9: /* save & exit */ | |
949 | /* get confirmation first */ | |
950 | do | |
951 | { | |
952 | key = getkey(0); | |
953 | } while (key != '\\' && key != 'Z' && key != '\r' /* good */ | |
954 | && key != '\033'); /* bad */ | |
955 | if (key != '\033') | |
956 | { | |
957 | m = v_xit(m, 0L, 'Z'); | |
958 | } | |
959 | break; | |
960 | ||
961 | case 10: /* undo previous */ | |
962 | m = v_undo(m); | |
963 | dfltsel = 9; | |
964 | break; | |
965 | } | |
966 | ||
08746e8b | 967 | /* arrange for the menu to be erased (except "save & exit" doesn't care) |
15637ed4 | 968 | */ |
08746e8b | 969 | if (mode == MODE_VI) |
15637ed4 RG |
970 | redraw(MARK_UNSET, FALSE); |
971 | ||
972 | return m; | |
973 | } | |
974 | #endif /* undef NO_POPUP */ | |
08746e8b AM |
975 | |
976 | #ifndef NO_TAGSTACK | |
977 | MARK v_pop(m, cnt, cmd) | |
978 | MARK m; /* original cursor position (ignored) */ | |
979 | long cnt; /* number of levels to pop */ | |
980 | int cmd; /* command key -- ^T (ignored) */ | |
981 | { | |
982 | DEFAULT(1L); | |
983 | sprintf(tmpblk.c, "%ld", cnt); | |
984 | cmd_pop(m, m, CMD_POP, FALSE, tmpblk.c); | |
985 | return cursor; | |
986 | } | |
987 | #endif |