|
|
1.1 root 1: /* move1.c */
2:
3: /* Author:
4: * Steve Kirkendall
5: * 14407 SW Teal Blvd. #C
6: * Beaverton, OR 97005
7: * [email protected]
8: */
9:
10:
11: /* This file contains most movement functions */
12:
13: #include "config.h"
14: #include "vi.h"
15: #include "ctype.h"
16:
17: MARK m_updnto(m, cnt, cmd)
18: MARK m; /* movement is relative to this mark */
19: long cnt; /* a numeric argument */
20: int cmd; /* the command character */
21: {
22: DEFAULT(cmd == 'G' ? nlines : 1L);
23:
24: /* move up or down 'cnt' lines */
25: switch (cmd)
26: {
27: case ctrl('P'):
28: case '-':
29: case 'k':
30: m -= MARK_AT_LINE(cnt);
31: break;
32:
33: case 'G':
34: if (cnt < 1L || cnt > nlines)
35: {
36: msg("Only %ld lines", nlines);
37: return MARK_UNSET;
38: }
39: m = MARK_AT_LINE(cnt);
40: break;
41:
42: case '_':
43: cnt--;
44: /* fall through... */
45:
46: default:
47: m += MARK_AT_LINE(cnt);
48: }
49:
50: /* if that left us screwed up, then fail */
51: if (m < MARK_FIRST || markline(m) > nlines)
52: {
53: return MARK_UNSET;
54: }
55:
56: return m;
57: }
58:
59: /*ARGSUSED*/
60: MARK m_right(m, cnt, key, prevkey)
61: MARK m; /* movement is relative to this mark */
62: long cnt; /* a numeric argument */
63: int key; /* movement keystroke */
64: int prevkey;/* operator keystroke, or 0 if none */
65: {
66: int idx; /* index of the new cursor position */
67:
68: DEFAULT(1);
69:
70: /* If used with an operator, then move 1 less character, since the 'l'
71: * command includes the character that it moves onto.
72: */
73: if (prevkey != '\0')
74: {
75: cnt--;
76: }
77:
78: /* move to right, if that's OK */
79: pfetch(markline(m));
80: idx = markidx(m) + cnt;
81: if (idx < plen)
82: {
83: m += cnt;
84: }
85: else
86: {
87: return MARK_UNSET;
88: }
89:
90: return m;
91: }
92:
93: /*ARGSUSED*/
94: MARK m_left(m, cnt)
95: MARK m; /* movement is relative to this mark */
96: long cnt; /* a numeric argument */
97: {
98: DEFAULT(1);
99:
100: /* move to the left, if that's OK */
101: if (markidx(m) >= cnt)
102: {
103: m -= cnt;
104: }
105: else
106: {
107: return MARK_UNSET;
108: }
109:
110: return m;
111: }
112:
113: /*ARGSUSED*/
114: MARK m_tocol(m, cnt, cmd)
115: MARK m; /* movement is relative to this mark */
116: long cnt; /* a numeric argument */
117: int cmd; /* either ctrl('X') or '|' */
118: {
119: char *text; /* text of the line */
120: int col; /* column number */
121: int idx; /* index into the line */
122:
123:
124: /* if doing ^X, then adjust for sideways scrolling */
125: if (cmd == ctrl('X'))
126: {
127: DEFAULT(*o_columns & 0xff);
128: cnt += leftcol;
129: }
130: else
131: {
132: DEFAULT(1);
133: }
134:
135: /* internally, columns are numbered 0..COLS-1, not 1..COLS */
136: cnt--;
137:
138: /* if 0, that's easy */
139: if (cnt == 0)
140: {
141: m &= ~(BLKSIZE - 1);
142: return m;
143: }
144:
145: /* find that column within the line */
146: pfetch(markline(m));
147: text = ptext;
148: for (col = idx = 0; col < cnt && *text; text++, idx++)
149: {
150: if (*text == '\t' && !*o_list)
151: {
152: col += *o_tabstop;
153: col -= col % *o_tabstop;
154: }
155: else if (UCHAR(*text) < ' ' || *text == '\177')
156: {
157: col += 2;
158: }
159: #ifndef NO_CHARATTR
160: else if (text[0] == '\\' && text[1] == 'f' && text[2] && *o_charattr)
161: {
162: text += 2; /* plus one more as part of for loop */
163: }
164: #endif
165: else
166: {
167: col++;
168: }
169: }
170: if (!*text)
171: {
172: /* the desired column was past the end of the line, so
173: * act like the user pressed "$" instead.
174: */
175: return m | (BLKSIZE - 1);
176: }
177: else
178: {
179: m = (m & ~(BLKSIZE - 1)) + idx;
180: }
181: return m;
182: }
183:
184: /*ARGSUSED*/
185: MARK m_front(m, cnt)
186: MARK m; /* movement is relative to this mark */
187: long cnt; /* a numeric argument (ignored) */
188: {
189: char *scan;
190:
191: /* move to the first non-whitespace character */
192: pfetch(markline(m));
193: scan = ptext;
194: m &= ~(BLKSIZE - 1);
195: while (*scan == ' ' || *scan == '\t')
196: {
197: scan++;
198: m++;
199: }
200:
201: return m;
202: }
203:
204: /*ARGSUSED*/
205: MARK m_rear(m, cnt)
206: MARK m; /* movement is relative to this mark */
207: long cnt; /* a numeric argument (ignored) */
208: {
209: /* Try to move *EXTREMELY* far to the right. It is fervently hoped
210: * that other code will convert this to a more reasonable MARK before
211: * anything tries to actually use it. (See adjmove() in vi.c)
212: */
213: return m | (BLKSIZE - 1);
214: }
215:
216: #ifndef NO_SENTENCE
217: static int isperiod(ptr)
218: char *ptr; /* pointer to possible sentence-ender */
219: {
220: /* if not '.', '?', or '!', then it isn't a sentence ender */
221: if (*ptr != '.' && *ptr != '?' && *ptr != '!')
222: {
223: return FALSE;
224: }
225:
226: /* skip any intervening ')', ']', or '"' characters */
227: do
228: {
229: ptr++;
230: } while (*ptr == ')' || *ptr == ']' || *ptr == '"');
231:
232: /* do we have two spaces or EOL? */
233: if (!*ptr || ptr[0] == ' ' && ptr[1] == ' ')
234: {
235: return TRUE;
236: }
237: return FALSE;
238: }
239:
240: /*ARGSUSED*/
241: MARK m_sentence(m, cnt, cmd)
242: MARK m; /* movement is relative to this mark */
243: long cnt; /* a numeric argument */
244: int cmd; /* either '(' or ')' */
245: {
246: REG char *text;
247: REG long l;
248:
249: DEFAULT(1);
250:
251: /* If '(' command, then move back one word, so that if we hit '(' at
252: * the start of a sentence we don't simply stop at the end of the
253: * previous sentence and bounce back to the start of this one again.
254: */
255: if (cmd == '(')
256: {
257: m = m_bword(m, 1L, 'b');
258: if (!m)
259: {
260: return m;
261: }
262: }
263:
264: /* get the current line */
265: l = markline(m);
266: pfetch(l);
267: text = ptext + markidx(m);
268:
269: /* for each requested sentence... */
270: while (cnt-- > 0)
271: {
272: /* search forward for one of [.?!] followed by spaces or EOL */
273: do
274: {
275: if (cmd == ')')
276: {
277: /* move forward, wrap at end of line */
278: if (!text[0])
279: {
280: l++;
281: if (l > nlines)
282: {
283: return MARK_EOF;
284: }
285: pfetch(l);
286: text = ptext;
287: }
288: else
289: {
290: text++;
291: }
292: }
293: else
294: {
295: /* move backward, wrap at beginning of line */
296: if (text == ptext)
297: {
298: do
299: {
300: if (l <= 1)
301: {
302: return MARK_FIRST;
303: }
304: l--;
305: pfetch(l);
306: } while (!*ptext);
307: text = ptext + plen - 1;
308: }
309: else
310: {
311: text--;
312: }
313: }
314: } while (!isperiod(text));
315: }
316:
317: /* construct a mark for this location */
318: m = buildmark(text);
319:
320: /* move forward to the first word of the next sentence */
321: m = m_fword(m, 1L, 'w', '\0');
322: if (m == MARK_UNSET)
323: {
324: m = MARK_EOF;
325: }
326:
327: return m;
328: }
329: #endif
330:
331: MARK m_paragraph(m, cnt, cmd)
332: MARK m; /* movement is relative to this mark */
333: long cnt; /* a numeric argument */
334: int cmd; /* either '{' or '}' */
335: {
336: char *text; /* text of the current line */
337: char *pscn; /* used to scan thru value of "paragraphs" option */
338: long l, ol; /* current line number, original line number */
339: int dir; /* -1 if we're moving up, or 1 if down */
340: char col0; /* character to expect in column 0 */
341: #ifndef NO_SENTENCE
342: # define SENTENCE(x) (x)
343: char *list; /* either o_sections or o_paragraph */
344: #else
345: # define SENTENCE(x)
346: #endif
347:
348: DEFAULT(1);
349:
350: /* set the direction, based on the command */
351: switch (cmd)
352: {
353: case '{':
354: dir = -1;
355: col0 = '\0';
356: SENTENCE(list = o_paragraphs);
357: break;
358:
359: case '}':
360: dir = 1;
361: col0 = '\0';
362: SENTENCE(list = o_paragraphs);
363: break;
364:
365: case '[':
366: if (getkey(0) != '[')
367: {
368: return MARK_UNSET;
369: }
370: dir = -1;
371: col0 = '{';
372: SENTENCE(list = o_sections);
373: break;
374:
375: case ']':
376: if (getkey(0) != ']')
377: {
378: return MARK_UNSET;
379: }
380: dir = 1;
381: col0 = '{';
382: SENTENCE(list = o_sections);
383: break;
384: }
385: ol = l = markline(m);
386:
387: /* for each paragraph that we want to travel through... */
388: while (l > 0 && l <= nlines && cnt-- > 0)
389: {
390: /* skip blank lines between paragraphs */
391: while (l > 0 && l <= nlines && col0 == *(text = fetchline(l)))
392: {
393: l += dir;
394: }
395:
396: /* skip non-blank lines that aren't paragraph separators
397: */
398: do
399: {
400: #ifndef NO_SENTENCE
401: if (*text == '.' && l != ol)
402: {
403: for (pscn = list; pscn[0] && pscn[1]; pscn += 2)
404: {
405: if (pscn[0] == text[1] && pscn[1] == text[2])
406: {
407: pscn = (char *)0;
408: goto BreakBreak;
409: }
410: }
411: }
412: #endif
413: l += dir;
414: } while (l > 0 && l <= nlines && col0 != *(text = fetchline(l)));
415: BreakBreak: ;
416: }
417:
418: if (l > nlines)
419: {
420: m = MARK_EOF;
421: }
422: else if (l <= 0)
423: {
424: m = MARK_FIRST;
425: }
426: else
427: {
428: m = MARK_AT_LINE(l);
429: }
430: #ifdef DEBUG2
431: debout("m_paragraph() returning %ld.%d\n", markline(m), markidx(m));
432: #endif
433: return m;
434: }
435:
436:
437: /*ARGSUSED*/
438: MARK m_match(m, cnt)
439: MARK m; /* movement is relative to this mark */
440: long cnt; /* a numeric argument (normally 0) */
441: {
442: long l;
443: REG char *text;
444: REG char match;
445: REG char nest;
446: REG int count;
447:
448: #ifndef NO_EXTENSIONS
449: /* if we're given a number, then treat it as a percentage of the file */
450: if (cnt > 0)
451: {
452: /* make sure it is a reasonable number */
453: if (cnt > 100)
454: {
455: msg("can only be from 1%% to 100%%");
456: return MARK_UNSET;
457: }
458:
459: /* return the appropriate line number */
460: l = (nlines - 1L) * cnt / 100L + 1L;
461: return MARK_AT_LINE(l);
462: }
463: #endif /* undef NO_EXTENSIONS */
464:
465: /* get the current line */
466: l = markline(m);
467: pfetch(l);
468: text = ptext + markidx(m);
469:
470: /* search forward within line for one of "[](){}" */
471: for (match = '\0'; !match && *text; text++)
472: {
473: /* tricky way to recognize 'em in ASCII */
474: nest = *text;
475: if ((nest & 0xdf) == ']' || (nest & 0xdf) == '[')
476: {
477: match = nest ^ ('[' ^ ']');
478: }
479: else if ((nest & 0xfe) == '(')
480: {
481: match = nest ^ ('(' ^ ')');
482: }
483: else
484: {
485: match = 0;
486: }
487: }
488: if (!match)
489: {
490: return MARK_UNSET;
491: }
492: text--;
493:
494: /* search forward or backward for match */
495: if (match == '(' || match == '[' || match == '{')
496: {
497: /* search backward */
498: for (count = 1; count > 0; )
499: {
500: /* wrap at beginning of line */
501: if (text == ptext)
502: {
503: do
504: {
505: if (l <= 1L)
506: {
507: return MARK_UNSET;
508: }
509: l--;
510: pfetch(l);
511: } while (!*ptext);
512: text = ptext + plen - 1;
513: }
514: else
515: {
516: text--;
517: }
518:
519: /* check the char */
520: if (*text == match)
521: count--;
522: else if (*text == nest)
523: count++;
524: }
525: }
526: else
527: {
528: /* search forward */
529: for (count = 1; count > 0; )
530: {
531: /* wrap at end of line */
532: if (!*text)
533: {
534: if (l >= nlines)
535: {
536: return MARK_UNSET;
537: }
538: l++;
539: pfetch(l);
540: text = ptext;
541: }
542: else
543: {
544: text++;
545: }
546:
547: /* check the char */
548: if (*text == match)
549: count--;
550: else if (*text == nest)
551: count++;
552: }
553: }
554:
555: /* construct a mark for this place */
556: m = buildmark(text);
557: return m;
558: }
559:
560: /*ARGSUSED*/
561: MARK m_tomark(m, cnt, key)
562: MARK m; /* movement is relative to this mark */
563: long cnt; /* (ignored) */
564: int key; /* keystroke - the mark to move to */
565: {
566: /* mark '' is a special case */
567: if (key == '\'' || key == '`')
568: {
569: if (mark[26] == MARK_UNSET)
570: {
571: return MARK_FIRST;
572: }
573: else
574: {
575: return mark[26];
576: }
577: }
578:
579: /* if not a valid mark number, don't move */
580: if (key < 'a' || key > 'z')
581: {
582: return MARK_UNSET;
583: }
584:
585: /* return the selected mark -- may be MARK_UNSET */
586: if (!mark[key - 'a'])
587: {
588: msg("mark '%c is unset", key);
589: }
590: return mark[key - 'a'];
591: }
592:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.