static void vi_cmd_mode()

in system/vi/vi.c [3401:4132]


static void vi_cmd_mode(FAR struct vi_s *vi)
{
  viinfo("Enter command mode\n");

  /* Loop while we are in command mode */

  while (vi->mode == MODE_COMMAND)
    {
      bool preserve;
      int ch;

      /* Make sure that the display reflects the current state */

      vi_showtext(vi);
      vi_showlinecol(vi);
      vi_setcursor(vi, vi->cursor.row, vi->cursor.column);

      /* Get the next character from the input */

#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
      /* Test for end of command repeat */

      if (vi->cmdrepeat && vi->cmdindex == vi->cmdcount)
        {
          /* Terminate the command repeat */

          vi->cmdrepeat = false;
        }

      /* Test for active cmdrepeat */

      if (vi->cmdrepeat)
        {
          /* Read next command from command buffer */

          ch = vi->cmdbuf[vi->cmdindex++];
        }
      else
#endif
        {
          ch = vi_getch(vi);
        }

      /* Handle numeric input.  Zero (0) with no preceding value is a
       * special case: It means to go to the beginning o the line.
       */

      if (isdigit(ch) && (vi->value > 0 || ch != '0'))
        {
          uint32_t tmp = 10 * vi->value + (ch - '0');
          if (tmp > UINT16_MAX)
            {
              tmp = UINT16_MAX;
            }

          /* Update the command repetition count */

          vi->value = tmp;

          viinfo("Value=%ld\n", vi->value);
          continue;
        }

      /* Allow the following during yank / delete modes */

      if (ch != 'f' && ch != 't' && ch != 'w')
        {
          /* Anything other than 'd' disarms line deletion */

          if (ch != 'd')
            {
              vi->delarm = false;
            }

          /* Anything other than 'y' disarms line yanking */

          if (ch != 'y')
            {
              vi->yankarm = false;
            }

          /* Anything other than 'c' disarms line yanking */

          if (ch != 'c')
            {
              vi->chgarm = false;
            }
        }

      /* Anything other than'g' disarms goto top */

      if (ch != 'g')
        {
          vi->toparm = false;
        }

      /* Anything other than'Z' disarms :wq */

      if (ch != 'Z')
        {
          vi->wqarm = false;
        }

      /* Test for empty file */

      if (vi->textsize == 0)
        {
          /* We need some text before we can do anything.  Only accept
           * text insertion commands.
           */

          if (ch != KEY_CMDMODE_APPEND    && ch != KEY_CMDMODE_INSERT    &&
              ch != KEY_CMDMODE_OPENBELOW && ch != KEY_CMDMODE_APPENDEND &&
              ch != KEY_CMDMODE_INSBEGIN  && ch != KEY_CMDMODE_OPENABOVE &&
              ch != KEY_CMDMODE_COLONMODE)
            {
              continue;
            }
        }

      /* Any key press clears the error message */

      vi->error = false;

      /* Then handle the non-numeric character.  Normally the accumulated
       * value will be reset after processing the command.  There are a few
       * exceptions; 'preserve' will be set to 'true' in those exceptional
       * cases.
       */

      preserve = false;
      vi->updatereqcol = true;
      switch (ch)
        {
        case KEY_CMDMODE_UP: /* Move the cursor up one line */
        case KEY_UP:         /* Move the cursor up one line */
          {
            vi->updatereqcol = false;
            vi_cusorup(vi, vi->value);
          }
          break;

        case KEY_CMDMODE_DOWN: /* Move the cursor down one line */
        case KEY_DOWN:         /* Move the cursor down one line */
          {
            vi->updatereqcol = false;
            vi_cursordown(vi, vi->value);
          }
          break;

        case KEY_CMDMODE_LEFT: /* Move the cursor left N characters */
        case KEY_LEFT:         /* Move the cursor left N characters */
          {
            vi->curpos = vi_cursorleft(vi, vi->curpos, vi->value);
          }
          break;

        case KEY_CMDMODE_RIGHT: /* Move the cursor right one character */
        case KEY_RIGHT:         /* Move the cursor right one character */
          {
            if (vi->text[vi->curpos] != '\n' &&
                vi->text[vi->curpos + 1] != '\n')
              {
                vi->curpos = vi_cursorright(vi, vi->curpos, vi->value);
                if (vi->curpos >= vi->textsize)
                  {
                    vi->curpos = vi->textsize - 1;
                  }
              }
          }
          break;

        case KEY_CMDMODE_BEGINLINE: /* Move cursor to start of current line */
        case KEY_HOME:
          {
            vi->curpos = vi_linebegin(vi, vi->curpos);
          }
          break;

        case KEY_CMDMODE_ENDLINE: /* Move cursor to end of current line */
        case KEY_END:
          {
            vi->curpos = vi_lineend(vi, vi->curpos);
            vi->reqcolumn = 65535;
            vi->updatereqcol = false;
          }
          break;

        case KEY_CMDMODE_PAGEUP: /* Move up (backward) one screen */
        case KEY_PPAGE:
          {
            vi->updatereqcol = false;
            vi_cusorup(vi, vi->display.row);
          }
          break;

        case KEY_CMDMODE_PAGEDOWN: /* Move down (forward) one screen */
        case KEY_NPAGE:
          {
            vi->updatereqcol = false;
            vi_cursordown(vi, vi->display.row);
          }
          break;

        case KEY_CMDMODE_HALFUP: /* Move up (backward) one screen */
          {
            vi->updatereqcol = false;
            vi_cusorup(vi, vi->display.row >> 1);
          }
          break;

        case KEY_CMDMODE_HALFDOWN: /*  Move down (forward) one half screen */
          {
            vi->updatereqcol = false;
            vi_cursordown(vi, vi->display.row >> 1);
          }
          break;

        case KEY_CMDMODE_TOP:     /* Move to top of screen */
          {
            vi->curpos = vi->winpos;
          }
          break;

        case KEY_CMDMODE_BOTTOM:  /* Move to bottom of screen */
          {
            vi_gotoscreenbottom(vi, 0);
          }
          break;

        case KEY_CMDMODE_MIDDLE:  /* Move to middle of screen */
          {
            /* Find bottom row number, then move to half that */

            off_t pos = vi_gotoscreenbottom(vi, 0);
            vi_gotoscreenbottom(vi, pos);
          }
          break;

        case KEY_CMDMODE_FIRSTCHAR:
          {
            vi_gotofirstnonwhite(vi);
          }
          break;

        case KEY_CMDMODE_GOTOTOP: /* Go to top of document */
          {
            if (vi->toparm)
              {
                vi->curpos = 0;
                vi->redrawline = true;
                vi->toparm = false;
              }
            else
              {
                vi->toparm = true;
              }
          }
          break;

        case KEY_CMDMODE_FINDNEXT:
          {
            if (vi->revfind)
              {
                vi_findprev(vi);
              }
            else
              {
                vi_findnext(vi);
              }
            break;
          }

        case KEY_CMDMODE_FINDPREV:
          {
            if (vi->revfind)
              {
                vi_findnext(vi);
              }
            else
              {
                vi_findprev(vi);
              }
            break;
          }
          break;

        case ASCII_BS:  /* Delete N characters before the cursor */
          {
            /* Move the cursor to the left */

            if (vi->curpos > 0)
              {
                vi->curpos--;

                /* If we moved to \n on the previous line, skip it */

                if (vi->curpos > 0 && vi->text[vi->curpos] == '\n')
                  {
                    vi->curpos--;
                  }
              }
          }
          break;

        case KEY_CMDMODE_DEL_LINE: /* Delete the current line */
          {
            if (vi->delarm)
              {
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
                vi_saverepeat(vi, ch);
                vi_appendrepeat(vi, ch);
#endif
                vi_delline(vi);
                vi->delarm = false;
              }
            else
              {
                vi->delarm = true;
                preserve = true;
              }
          }
          break;

        case KEY_CMDMODE_CHANGE:
          {
            if (vi->chgarm)
              {
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
                vi_saverepeat(vi, ch);
                vi_appendrepeat(vi, ch);
#endif
                vi_gotofirstnonwhite(vi);
                vi_deltoeol(vi);
                vi_setmode(vi, MODE_INSERT, 0);
                if (vi->curpos == vi->textsize)
                  {
                    vi->curpos = vi_cursorright(vi, vi->curpos, 1) + 1;
                  }
                else
                  {
                    vi->curpos = vi_cursorright(vi, vi->curpos, 1);
                  }

                vi->chgarm = false;
              }
            else
              {
                vi->chgarm = true;
                preserve = true;
              }
          }
          break;

        case KEY_CMDMODE_DELTOEOL:  /* Delete to end of current line */
          {
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
            vi_saverepeat(vi, ch);
#endif
            vi_deltoeol(vi);
          }
          break;

        case KEY_CMDMODE_DELBACKWARD:  /* Delete from cursor forward */
          {
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
            vi_saverepeat(vi, ch);
#endif
            vi_delbackward(vi);
          }
          break;

        case KEY_CMDMODE_YANK:  /* Yank the current line(s) into the buffer */
          {
            if (vi->yankarm)
              {
                vi_yank(vi, false);
                vi->yankarm = false;
              }
            else
              {
                vi->yankarm = true;
                preserve = true;
              }
          }
          break;

        case KEY_CMDMODE_PASTE: /* Paste line(s) from into text after current line */
          {
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
            vi_saverepeat(vi, ch);
#endif
            vi_paste(vi, false);
          }
          break;

        case KEY_CMDMODE_PASTEBEFORE: /* Paste text before cursor position */
          {
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
            vi_saverepeat(vi, ch);
#endif
            vi_paste(vi, true);
          }
          break;

        case KEY_CMDMODE_REPLACECH: /* Replace character(s) under cursor */
          {
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
            vi_saverepeat(vi, ch);
#endif
            vi_setmode(vi, SUBMODE_REPLACECH, vi->value);
            preserve = true;
          }
          break;

        case KEY_CMDMODE_REPLACE: /* Replace character(s) under cursor until ESC */
          {
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
            vi_saverepeat(vi, ch);
#endif
            vi_setmode(vi, MODE_REPLACE, 0);
          }
          break; /* Not implemented */

        case KEY_CMDMODE_FINDINLINE:  /* Find character(s) in current line */
        case KEY_CMDMODE_TFINDINLINE: /* Find character(s) in current line */
          {
            vi->tfind = ch == KEY_CMDMODE_TFINDINLINE;
            vi->mode = MODE_FINDINLINE;
            preserve = true;
          }
          break;

        case KEY_CMDMODE_OPENBELOW: /* Enter insertion mode in new line below current */
          {
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
            vi_saverepeat(vi, ch);
#endif
            vi_setmode(vi, MODE_INSERT, 0);

            /* Go forward to the end of the current line */

            vi->curpos = vi_lineend(vi, vi->curpos);
            if (vi->curpos != vi->textsize)
              {
                /* Include the '\n' */

                vi->curpos++;
              }

            /* Insert a newline to break the line.  The cursor now points
             * beginning of the new line.
             */

            vi_insertch(vi, '\n');

            /* Then enter insert mode */

            vi->drawtoeos = true;
          }
          break;

        case KEY_CMDMODE_OPENABOVE: /* Enter insertion mode in new line above current */
          {
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
            vi_saverepeat(vi, ch);
#endif
            /* Back up to the beginning of the end of the previous line */

            off_t pos  = vi_linebegin(vi, vi->curpos);
            if (pos == 0)
              {
                /* Insert newline at beginning of file, then move to previous
                 * line.
                 */

                vi->curpos = 0;
                vi_insertch(vi, '\n');
                vi->curpos = vi_prevline(vi, vi->curpos);
              }
            else
              {
                /* Insert a newline to open the line.  The cursor will now
                 * point to thebeginning of newly openly line before the
                 * current line.
                 */

                pos = vi_prevline(vi, pos);
                vi->curpos = vi_lineend(vi, pos)+1;
                vi_insertch(vi, '\n');
              }

            /* Then enter insert mode */

            vi_setmode(vi, MODE_INSERT, 0);
            vi->drawtoeos = true;
          }
          break;

        case KEY_CMDMODE_CHANGETOEOL:  /* Delete to end of current line */
          {
            /* First delete to end of line */

            vi_deltoeol(vi);
          }

          /* Now enter insert mode by falling through the case */

        case KEY_CMDMODE_APPEND: /* Enter insertion mode after the current
                                  * cursor position */
          {
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
            vi_saverepeat(vi, ch);
#endif
            vi_setmode(vi, MODE_INSERT, 0);

            if (vi->curpos == vi->textsize)
              {
                vi->curpos = vi_cursorright(vi, vi->curpos, 1) + 1;
              }
            else
              {
                vi->curpos = vi_cursorright(vi, vi->curpos, 1);
              }
          }
          break;

        case KEY_CMDMODE_APPENDEND: /* Enter insertion mode at the end of the current line */
          {
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
            vi_saverepeat(vi, ch);
#endif
            vi_setmode(vi, MODE_INSERT, 0);
            vi->curpos = vi_lineend(vi, vi->curpos) + 1;
          }
          break;

        case KEY_CMDMODE_SUBSTITUTE:
        case KEY_CMDMODE_DEL: /* Delete N characters at the cursor */
        case KEY_DC:
        case ASCII_DEL:
          {
            off_t pos = vi->curpos;

#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
            vi_saverepeat(vi, ch);
#endif
            /* If we are at the end of the line, then delete backward */

            if (vi->text[pos] == '\n')
              {
                /* Nothing to do */

                break;
              }
            else if (pos + 1 != vi->textsize && vi->text[pos + 1] == '\n')
              {
                if (pos > 0)
                  {
                    vi_delforward(vi);
                    vi->curpos = vi_cursorleft(vi, vi->curpos, 1);
                  }
              }
            else
              {
                vi_delforward(vi);
                vi->redrawline = true;
              }
          }

          /* For 's'ubstitute key, we go into insert mode */

          if (ch == KEY_CMDMODE_SUBSTITUTE)
            {
              vi_setmode(vi, MODE_INSERT, 0);
            }

          break;

        case KEY_CMDMODE_INSBEGIN: /* Enter insertion mode at the beginning of the current line */
          {
            vi->curpos = vi_linebegin(vi, vi->curpos);
          }

          /* Fall through */

        case KEY_CMDMODE_INSERT: /* Enter insertion mode before the current cursor position */
          {
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
            vi_saverepeat(vi, ch);
#endif
            vi_setmode(vi, MODE_INSERT, 0);
          }
          break;

        case KEY_CMDMODE_JOIN:  /* Join line below with current line */
          {
#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
            vi_saverepeat(vi, ch);
#endif
            vi_join(vi);
          }
          break;

        case KEY_CMDMODE_COLONMODE: /* Enter : command sub-mode */
          {
            vi->updatereqcol = false;
            vi_setsubmode(vi, SUBMODE_COLON, ':', 0);
          }
          break;

        case KEY_CMDMODE_SAVEQUIT:  /* Two of these is the same as :wq */
          {
            if (vi->wqarm)
              {
                /* Emulate :wq */

                strlcpy(vi->scratch, "wq", sizeof(vi->scratch));
                vi->cmdlen = 2;
                vi_parsecolon(vi);

                /* If save quit succeeds, we won't return */
              }
            else
              {
                vi->wqarm = true;
              }
          }
          break;

        case KEY_CMDMODE_FINDMODE: /* Enter / find sub-mode */
          {
            vi->updatereqcol = false;
            vi_setsubmode(vi, SUBMODE_FIND, '/', 0);
          }
          break;

        case KEY_CMDMODE_REVFINDMODE: /* Enter / find sub-mode */
          {
            vi->updatereqcol = false;
            vi_setsubmode(vi, SUBMODE_REVFIND, '?', 0);
          }
          break;

#ifdef CONFIG_SYSTEM_VI_INCLUDE_COMMAND_REPEAT
        case KEY_CMDMODE_REPEAT: /* Repeat the last command */
          {
            if (vi->cmdcount < CMD_BUFSIZE)
              {
                vi->cmdindex = 0;
                vi->cmdrepeat = true;
                vi->value = vi->value > 0 ? vi->value : vi->repeatvalue > 0 ?
                            vi->repeatvalue : 1;
                preserve = true;
              }
            else
              {
                VI_BEL(vi);
              }
          }
          break;
#endif

        case KEY_CMDMODE_GOTO:  /* Go to line specified by the accumulated value */
          {
            vi_gotoline(vi);
          }
          break;

        case KEY_CMDMODE_WORDFWD: /* Go to line specified by the accumulated value */
          {
            vi_gotonextword(vi);
          }
          break;

        case KEY_CMDMODE_WORDBACK: /* Go to line specified by the accumulated value */
          {
            vi_gotoprevword(vi);
          }
          break;

        case KEY_CMDMODE_NEXTLINE:
        case '\n': /* LF terminates line */
          {
            vi->curpos = vi_nextline(vi, vi->curpos);
            vi_gotofirstnonwhite(vi);
          }
          break;

        case KEY_CMDMODE_PREVLINE:
          {
            vi->curpos = vi_prevline(vi, vi->curpos);
            vi_gotofirstnonwhite(vi);
          }
          break;

        /* Unimplemented and invalid commands */

        case KEY_CMDMODE_REDRAW:  /* Redraws the screen */
        case KEY_CMDMODE_REDRAW2: /* Redraws the screen, removing deleted lines */
        case KEY_CMDMODE_MARK:    /* Place a mark beginning at the current cursor position */
        default:
          {
            if (ch == -1)
              {
                continue;
              }
            else
              {
                VI_BEL(vi);
              }
          }
          break;
        }

      /* Any non-numeric input will reset the accumulated value (after it has
       * been used).  There are a few exceptions:
       *
       * - For the double character sequences, we need to retain the value
       *   until the next character is entered.
       * - If we are changing modes, then we may need to preserve the 'value'
       *   as well; in some cases settings are passed to the new mode in
       *   'value' (vi_setmode() will have set or cleared 'value'
       *   appropriately).
       */

      if (!preserve)
        {
          vi->value = 0;
        }
    }
}