|
|
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.