home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / elvis184.zip / src / move1.c < prev    next >
C/C++ Source or Header  |  1995-05-26  |  12KB  |  671 lines

  1. /* move1.c */
  2.  
  3. /* Author:
  4.  *    Steve Kirkendall
  5.  *    1500 SW Park #326
  6.  *    Portland OR, 97201
  7.  *    kirkenda@cs.pdx.edu
  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. #ifndef CRUNCH
  249.     /* figure out where the paragraph boundary is */
  250.     MARK        pp = m_paragraph(m, 1L, cmd=='(' ? '{' : '}');
  251. #endif
  252.  
  253.     DEFAULT(1);
  254.  
  255.     /* If '(' command, then move back one word, so that if we hit '(' at
  256.      * the start of a sentence we don't simply stop at the end of the
  257.      * previous sentence and bounce back to the start of this one again.
  258.      */
  259.     if (cmd == '(')
  260.     {
  261.         m = m_bword(m, 1L, 'b');
  262.         if (!m)
  263.         {
  264.             return m;
  265.         }
  266.     }
  267.  
  268.     /* get the current line */
  269.     l = markline(m);
  270.     pfetch(l);
  271.     text = ptext + markidx(m);
  272.  
  273.     /* for each requested sentence... */
  274.     while (cnt-- > 0)
  275.     {
  276.         /* search forward for one of [.?!] followed by spaces or EOL */
  277.         do
  278.         {
  279.             if (cmd == ')')
  280.             {
  281.                 /* move forward, wrap at end of line */
  282.                 if (!text[0])
  283.                 {
  284.                     if (l == nlines)
  285.                     {
  286.                         return(MARK_EOF); /*-g.t.*/
  287.                     }
  288.                     l++;
  289.                     pfetch(l);
  290.                     text = ptext;
  291.                 }
  292.                 else
  293.                 {
  294.                     text++;
  295.                 }
  296.             }
  297.             else
  298.             {
  299.                 /* move backward, wrap at beginning of line */
  300.                 if (text == ptext)
  301.                 {
  302.                     do
  303.                     {
  304.                         if (l == 1L)
  305.                         {
  306.                             return (MARK_FIRST); /*-g.t.*/
  307.                         }
  308.                         l--;
  309.                         pfetch(l);
  310.                     } while (!*ptext);
  311.                     text = ptext + plen - 1;
  312.                 }
  313.                 else
  314.                 {
  315.                     text--;
  316.                 }
  317.             }
  318.         } while (!isperiod(text));
  319.     }
  320. BreakBreak:
  321.  
  322.     /* construct a mark for this location */
  323.     m = buildmark(text);
  324.  
  325.     /* move forward to the first word of the next sentence */
  326.     m = m_fword(m, 1L, 'w', '\0');
  327.     if (m == MARK_UNSET)
  328.     {
  329.         return(MARK_EOF); /*-g.t.*/
  330.     }
  331.  
  332. #ifndef CRUNCH
  333.     /* don't cross the paragraph boundary */
  334.     if (pp && ((cmd=='(') ? (m<pp) : (m>pp)))
  335.     {
  336.         m = pp;
  337.     }
  338. #endif
  339.  
  340.     return m;
  341. }
  342. #endif
  343.  
  344. #ifndef CRUNCH
  345. /* return TRUE iff we're at the front of a line */
  346. static int atfront (m)
  347.     MARK    m;         /* we're at this mark */
  348. {
  349.         char    *scan;
  350.         int    tm = m & ~(BLKSIZE - 1);    /* tm is at very front */
  351.   
  352.     /* move tm to the first non-whitespace character, or to m */
  353.     pfetch(markline(m));
  354.     scan = ptext;
  355.     while ((*scan == ' ' || *scan == '\t') && m != tm)
  356.     {
  357.         scan++;
  358.         tm++;
  359.     }
  360.  
  361.     return (m == tm);
  362. }
  363. #endif
  364.  
  365. MARK    m_paragraph(m, cnt, cmd)
  366.     MARK    m;    /* movement is relative to this mark */
  367.     long    cnt;    /* a numeric argument */
  368.     int    cmd;    /* either {, }, [, or ].  Internally, < and > too */
  369. {
  370.     char    *text;    /* text of the current line */
  371.     char    *pscn;    /* used to scan thru value of "paragraphs" option */
  372.     long    l, ol;    /* current line number, original line number */
  373.     int    dir;    /* -1 if we're moving up, or 1 if down */
  374.     char    col0;    /* character to expect in column 0 */
  375.     long    limit;    /* line where searching must stop */
  376. #ifndef NO_SENTENCE
  377. # define SENTENCE(x)    (x)
  378.     char    *list;    /* either o_sections or o_paragraph */
  379. #else
  380. # define SENTENCE(x)
  381. #endif
  382. #ifndef CRUNCH
  383.     MARK    ss;
  384. #endif
  385.  
  386.     DEFAULT(1);
  387.  
  388.     /* set the direction, based on the command */
  389.     switch (cmd)
  390.     {
  391.       case '{':
  392.         dir = -1;
  393.         col0 = '\0';
  394.         SENTENCE(list = o_paragraphs); 
  395. #ifndef CRUNCH
  396.         ss = m_paragraph(m, 1L, '<');
  397.         if (ss)
  398.             limit = markline(ss);
  399.         else
  400. #endif
  401.             limit = 1L;
  402.         break;
  403.  
  404.       case '}':
  405.         dir = 1;
  406.         col0 = '\0';
  407.         SENTENCE(list = o_paragraphs); 
  408. #ifndef CRUNCH
  409.         if (atfront(m))
  410.         {
  411.             force_flags |= LNMD|INCL;
  412.         }
  413.         ss = m_paragraph(m, 1L, '>');
  414.         if (ss)
  415.             limit = markline(ss);
  416.         else
  417. #endif
  418.             limit = nlines;
  419.         break;
  420.  
  421.       case '[':
  422.         col0 = getkey(0);
  423.         if (col0 != '[')
  424.         {
  425. #ifndef NO_LEARN
  426.             /* if 'a through 'z, then setup learn mode */
  427.             if (col0 >= 'a' && col0 <= 'z')
  428.             {
  429.                 if (col0 == learn)
  430.                 {
  431.                     learn = '\0';
  432.                     learnkey('\0');
  433.                 }
  434.                 learn = col0;
  435.                 return cursor;
  436.             }
  437. #endif
  438.             return MARK_UNSET;
  439.         }
  440.         /* fall through... */
  441.       case '<':
  442.         dir = -1;
  443.         col0 = '{';
  444.         SENTENCE(list = o_sections); 
  445.         limit = 1L;
  446.         break;
  447.  
  448.       case ']':
  449.         col0 = getkey(0);
  450.         if (col0 != ']')
  451.         {
  452. #ifndef NO_LEARN
  453.             /* if 'a through 'z, then end learn mode */
  454.             if (col0 >= 'a' && col0 <= 'z')
  455.             {
  456.                 if (col0 != learn)
  457.                 {
  458.                     beep();
  459.                 }
  460.                 learn = '\0';
  461.                 return cursor;
  462.             }
  463. #endif
  464.             return MARK_UNSET;
  465.         }
  466.         /* fall through... */
  467.       case '>':
  468.         dir = 1;
  469.         col0 = '{';
  470.         SENTENCE(list = o_sections); 
  471.         limit = nlines;
  472.         break;
  473.     }
  474.     ol = l = markline(m);
  475.  
  476.     /* for each paragraph that we want to travel through... */
  477.     while (l != limit && cnt-- > 0)
  478.     {
  479.         /* skip blank lines between paragraphs */
  480.         while (l != limit && col0 == *(text = fetchline(l)))
  481.         {
  482.             l += dir;
  483.         }
  484.  
  485.         /* skip non-blank lines that aren't paragraph separators
  486.          */
  487.         do
  488.         {
  489. #ifndef NO_SENTENCE
  490.             if (*text == '.' && l != ol)
  491.             {
  492.                 for (pscn = list; pscn[0] && pscn[1]; pscn += 2)
  493.                 {
  494.                     if (pscn[0] == text[1] && (pscn[1] == text[2] || pscn[1] == ' ' && !text[2]))
  495.                     {
  496.                         pscn = (char *)0;
  497.                         goto BreakBreak;
  498.                     }
  499.                 }
  500.             }
  501. #endif
  502.             if (l != limit) l += dir;
  503.         } while (l != limit && col0 != *(text = fetchline(l)));
  504. BreakBreak:    ;
  505.     }
  506.  
  507.     m = (l >= nlines) ? MARK_EOF : MARK_AT_LINE(l); /* -g.t. */
  508. #ifdef DEBUG2
  509.     debout("m_paragraph() returning %ld.%d\n", markline(m), markidx(m));
  510. #endif
  511.     return m;
  512. }
  513.  
  514.  
  515. /*ARGSUSED*/
  516. MARK    m_match(m, cnt)
  517.     MARK    m;    /* movement is relative to this mark */
  518.     long    cnt;    /* a numeric argument (normally 0) */
  519. {
  520.     long    l;
  521.     REG char    *text;
  522.     REG char    match;
  523.     REG char    nest;
  524.     REG int        count;
  525.  
  526. #ifndef NO_EXTENSIONS
  527.     /* if we're given a number, then treat it as a percentage of the file */
  528.     if (cnt > 0)
  529.     {
  530.         /* make sure it is a reasonable number */
  531.         if (cnt > 100)
  532.         {
  533.             msg("can only be from 1%% to 100%%");
  534.             return MARK_UNSET;
  535.         }
  536.  
  537.         /* return the appropriate line number */
  538.         l = (nlines - 1L) * cnt / 100L + 1L;
  539.         return MARK_AT_LINE(l);
  540.     }
  541. #endif /* undef NO_EXTENSIONS */
  542.  
  543.     /* get the current line */
  544.     l = markline(m);
  545.     pfetch(l);
  546.     text = ptext + markidx(m);
  547.  
  548.     /* search forward within line for one of "[](){}" */
  549.     for (match = '\0'; !match && *text; text++)
  550.     {
  551.         /* tricky way to recognize 'em in ASCII */
  552.         nest = *text;
  553.         if ((nest & 0xdf) == ']' || (nest & 0xdf) == '[')
  554.         {
  555.             match = nest ^ ('[' ^ ']');
  556.         }
  557.         else if ((nest & 0xfe) == '(')
  558.         {
  559.             match = nest ^ ('(' ^ ')');
  560.         }
  561.         else
  562.         {
  563.             match = 0;
  564.         }
  565.     }
  566.     if (!match)
  567.     {
  568.         return MARK_UNSET;
  569.     }
  570.     text--;
  571.  
  572.     /* search forward or backward for match */
  573.     if (match == '(' || match == '[' || match == '{')
  574.     {
  575.         /* search backward */
  576.         for (count = 1; count > 0; )
  577.         {
  578.             /* wrap at beginning of line */
  579.             if (text == ptext)
  580.             {
  581.                 do
  582.                 {
  583.                     if (l <= 1L)
  584.                     {
  585.                         return MARK_UNSET;
  586.                     }
  587.                     l--;
  588.                     pfetch(l);
  589.                 } while (!*ptext);
  590.                 text = ptext + plen - 1;
  591.             }
  592.             else
  593.             {
  594.                 text--;
  595.             }
  596.  
  597.             /* check the char */
  598.             if (*text == match)
  599.                 count--;
  600.             else if (*text == nest)
  601.                 count++;
  602.         }
  603.     }
  604.     else
  605.     {
  606.         /* search forward */
  607.         for (count = 1; count > 0; )
  608.         {
  609.             /* wrap at end of line */
  610.             if (!*text)
  611.             {
  612.                 if (l >= nlines)
  613.                 {
  614.                     return MARK_UNSET;
  615.                 }
  616.                 l++;
  617.                 pfetch(l);
  618.                 text = ptext;
  619.             }
  620.             else
  621.             {
  622.                 text++;
  623.             }
  624.  
  625.             /* check the char */
  626.             if (*text == match)
  627.                 count--;
  628.             else if (*text == nest)
  629.                 count++;
  630.         }
  631.     }
  632.  
  633.     /* construct a mark for this place */
  634.     m = buildmark(text);
  635.     return m;
  636. }
  637.  
  638. /*ARGSUSED*/
  639. MARK    m_tomark(m, cnt, key)
  640.     MARK    m;    /* movement is relative to this mark */
  641.     long    cnt;    /* (ignored) */
  642.     int    key;    /* keystroke - the mark to move to */
  643. {
  644.     /* mark '' is a special case */
  645.     if (key == '\'' || key == '`')
  646.     {
  647.         if (mark[26] == MARK_UNSET)
  648.         {
  649.             return MARK_FIRST;
  650.         }
  651.         else
  652.         {
  653.             return mark[26];
  654.         }
  655.     }
  656.  
  657.     /* if not a valid mark number, don't move */
  658.     if (key < 'a' || key > 'z')
  659.     {
  660.         return MARK_UNSET;
  661.     }
  662.  
  663.     /* return the selected mark -- may be MARK_UNSET */
  664.     if (!mark[key - 'a'])
  665.     {
  666.         msg("mark '%c is unset", key);
  667.     }
  668.     return mark[key - 'a'];
  669. }
  670.  
  671.