home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-385-Vol-1of3.iso / v / vim_src.zip / UNDO.C < prev    next >
C/C++ Source or Header  |  1993-01-12  |  13KB  |  544 lines

  1. /* vi:ts=4:sw=4
  2.  *
  3.  * VIM - Vi IMitation
  4.  *
  5.  * Code Contributions By:    Bram Moolenaar            mool@oce.nl
  6.  *                            Tim Thompson            twitch!tjt
  7.  *                            Tony Andrews            onecom!wldrdg!tony 
  8.  *                            G. R. (Fred) Walter        watmath!watcgl!grwalter 
  9.  */
  10.  
  11. /*
  12.  * undo.c: multi level undo facility
  13.  *
  14.  * The saved lines are stored in a list of lists:
  15.  *
  16.  * u_oldhead----------------------------------------------+
  17.  *                                                        |
  18.  *                                                        V
  19.  *              +--------------+    +--------------+    +--------------+
  20.  * u_newhead--->| u_header     |    | u_header     |    | u_header     |
  21.  *              |     uh_next------>|     uh_next------>|     uh_next---->NULL
  22.  *       NULL<--------uh_prev  |<---------uh_prev  |<---------uh_prev  |
  23.  *              |     uh_entry |    |     uh_entry |    |     uh_entry |
  24.  *              +--------|-----+    +--------|-----+    +--------|-----+
  25.  *                       |                   |                   |
  26.  *                       V                   V                   V
  27.  *              +--------------+    +--------------+    +--------------+
  28.  *              | u_entry      |    | u_entry      |    | u_entry      |
  29.  *              |     ue_next  |    |     ue_next  |    |     ue_next  |
  30.  *              +--------|-----+    +--------|-----+    +--------|-----+
  31.  *                       |                   |                   |
  32.  *                       V                   V                   V
  33.  *              +--------------+            NULL                NULL
  34.  *              | u_entry      |
  35.  *              |     ue_next  |
  36.  *              +--------|-----+
  37.  *                       |
  38.  *                       V
  39.  *                      etc.
  40.  *
  41.  * Each u_entry list contains the information for one undo or redo.
  42.  * u_curhead points to the header of the last undo (the next redo), or is
  43.  * NULL if nothing has been undone.
  44.  *
  45.  * All data is allocated with alloc_line(), thus it will be freed as soon as
  46.  * we switch files!
  47.  */
  48.  
  49. #include "vim.h"
  50. #include "globals.h"
  51. #include "proto.h"
  52. #include "param.h"
  53.  
  54. struct u_entry
  55. {
  56.     struct u_entry    *ue_next;    /* pointer to next entry in list */
  57.     linenr_t        ue_top;        /* number of line above undo block */
  58.     linenr_t        ue_bot;        /* number of line below undo block */
  59.     char            *ue_botptr;    /* pointer to line below undo block */
  60.     char            **ue_array;    /* array of lines in undo block */
  61.     long            ue_size;    /* number of lines in ue_array */
  62. };
  63.  
  64. struct u_header
  65. {
  66.     struct u_header    *uh_next;    /* pointer to next header in list */
  67.     struct u_header    *uh_prev;    /* pointer to previous header in list */
  68.     struct u_entry    *uh_entry;    /* pointer to first entry */
  69.     FPOS             uh_curpos;    /* cursor position before saving */
  70. };
  71.  
  72. static struct u_header *u_oldhead = NULL;    /* pointer to oldest header */
  73. static struct u_header *u_newhead = NULL;    /* pointer to newest header */
  74. static struct u_header *u_curhead = NULL;    /* pointer to current header */
  75. static int                u_numhead = 0;        /* current number of headers */
  76. static int                u_synced = TRUE;    /* entry lists are synced */
  77.  
  78. /*
  79.  * variables for "U" command
  80.  */
  81. static char       *u_line_ptr = NULL;        /* saved line for "U" command */
  82. static linenr_t u_line_lnum;            /* line number of line in u_line */
  83. static colnr_t    u_line_colnr;            /* optional column number */
  84.  
  85. static void u_getbot __ARGS((void));
  86. static int u_savecommon __ARGS((linenr_t, linenr_t, int, char *));
  87. static void u_undoredo __ARGS((void));
  88. static void u_freelist __ARGS((struct u_header *));
  89. static void u_freeentry __ARGS((struct u_entry *, long));
  90.  
  91. /*
  92.  * save the current line for both the "u" and "U" command
  93.  */
  94.     int
  95. u_saveCurpos()
  96. {
  97.     return (u_save((linenr_t)(Curpos.lnum - 1), (linenr_t)(Curpos.lnum + 1)));
  98. }
  99.  
  100. /*
  101.  * Save the lines between "top" and "bot" for both the "u" and "U" command.
  102.  * "top" may be 0 and bot may be line_count + 1.
  103.  * Returns FALSE when lines could not be saved.
  104.  */
  105.     int
  106. u_save(top, bot)
  107.     linenr_t top, bot;
  108. {
  109.     if (top > line_count || top >= bot || bot > line_count + 1)
  110.         return FALSE;    /* rely on caller to do error messages */
  111.  
  112.     if (top + 2 == bot)
  113.         u_saveline((linenr_t)(top + 1));
  114.  
  115.     return (u_savecommon(top, bot, 0, (char *)0));
  116. }
  117.  
  118. /*
  119.  * save the line "lnum", pointed at by "ptr" (used by :g//s commands)
  120.  * "ptr" is handed over to the undo routines
  121.  */
  122.     int
  123. u_savesub(lnum, ptr)
  124.     linenr_t    lnum;
  125.     char        *ptr;
  126. {
  127.     return (u_savecommon(lnum - 1, lnum + 1, 1, ptr));
  128. }
  129.  
  130. /*
  131.  * save the line "lnum", pointed at by "ptr" (used by :g//d commands)
  132.  * "ptr" is handed over to the undo routines
  133.  */
  134.     int
  135. u_savedel(lnum, ptr)
  136.     linenr_t    lnum;
  137.     char        *ptr;
  138. {
  139.     return (u_savecommon(lnum - 1, lnum, 1, ptr));
  140. }
  141.  
  142.     static int 
  143. u_savecommon(top, bot, flag, ptr)
  144.     linenr_t top, bot;
  145.     int flag;
  146.     char *ptr;
  147. {
  148.     linenr_t        lnum;
  149.     long            i;
  150.     struct u_header *uhp;
  151.     struct u_entry    *uep;
  152.     long            size;
  153.  
  154.     /*
  155.      * if u_synced == TRUE make a new header
  156.      */
  157.     if (u_synced)
  158.     {
  159.         /*
  160.          * if we undid more than we redid, free the entry lists before and
  161.          * including u_curhead
  162.          */
  163.         while (u_curhead != NULL)
  164.             u_freelist(u_newhead);
  165.  
  166.         /*
  167.          * free headers to keep the size right
  168.          */
  169.         while (u_numhead > p_ul && u_oldhead != NULL)
  170.             u_freelist(u_oldhead);
  171.  
  172.         /*
  173.          * make a new header entry
  174.          */
  175.         uhp = (struct u_header *)alloc_line((unsigned)sizeof(struct u_header));
  176.         if (uhp == NULL)
  177.             goto nomem;
  178.         uhp->uh_prev = NULL;
  179.         uhp->uh_next = u_newhead;
  180.         if (u_newhead != NULL)
  181.             u_newhead->uh_prev = uhp;
  182.         uhp->uh_entry = NULL;
  183.         uhp->uh_curpos = Curpos;    /* save cursor position for undo */
  184.         u_newhead = uhp;
  185.         if (u_oldhead == NULL)
  186.             u_oldhead = uhp;
  187.         ++u_numhead;
  188.     }
  189.     else    /* find line number for ue_botptr for previous u_save() */
  190.         u_getbot();
  191.  
  192.     /*
  193.      * add lines in front of entry list
  194.      */
  195.     uep = (struct u_entry *)alloc_line((unsigned)sizeof(struct u_entry));
  196.     if (uep == NULL)
  197.         goto nomem;
  198.  
  199.     if (flag)
  200.         size = 1;
  201.     else
  202.         size = bot - top - 1;
  203.     uep->ue_size = size;
  204.     uep->ue_top = top;
  205.     uep->ue_botptr = NULL;
  206.     if (flag)
  207.         uep->ue_bot = bot;
  208.         /*
  209.          * use 0 for ue_bot if bot is below last line or if the buffer is empty, in
  210.          * which case the last line may be replaced (e.g. with 'O' command).
  211.          */
  212.     else if (bot > line_count || bufempty())
  213.         uep->ue_bot = 0;
  214.     else
  215.         uep->ue_botptr = nr2ptr(bot);    /* we have to do ptr2nr(ue_botptr) later */
  216.  
  217.     if (size)
  218.     {
  219.         if ((uep->ue_array = (char **)alloc_line((unsigned)(sizeof(char *) * size))) == NULL)
  220.         {
  221.             u_freeentry(uep, 0L);
  222.             goto nomem;
  223.         }
  224.         if (flag)
  225.             uep->ue_array[0] = ptr;
  226.         else
  227.             for (i = 0, lnum = top + 1; i < size; ++i)
  228.                 if ((uep->ue_array[i] = save_line(nr2ptr(lnum++))) == NULL)
  229.                 {
  230.                     u_freeentry(uep, i);
  231.                     goto nomem;
  232.                 }
  233.     }
  234.     uep->ue_next = u_newhead->uh_entry;
  235.     u_newhead->uh_entry = uep;
  236.     u_synced = FALSE;
  237.     return TRUE;
  238.  
  239. nomem:
  240.     if (flag)
  241.         free_line(ptr);
  242.     else if (ask_yesno("no undo possible; continue anyway") == 'y')
  243.         return TRUE;
  244.     return FALSE;
  245. }
  246.  
  247.     void
  248. u_undo(count)
  249.     int count;
  250. {
  251.     while (count--)
  252.     {
  253.         if (u_curhead == NULL)                        /* first undo */
  254.             u_curhead = u_newhead;
  255.         else if (p_ul != 0)                        /* multi level undo */
  256.             u_curhead = u_curhead->uh_next;            /* get next undo */
  257.  
  258.         if (u_numhead == 0 || u_curhead == NULL)    /* nothing to undo */
  259.         {
  260.             u_curhead = u_oldhead;                    /* stick u_curhead at end */
  261.             beep();
  262.             return;
  263.         }
  264.  
  265.         u_undoredo();
  266.     }
  267. }
  268.  
  269.     void
  270. u_redo(count)
  271.     int count;
  272. {
  273.     while (count--)
  274.     {
  275.         if (u_curhead == NULL || p_ul == 0)        /* nothing to redo */
  276.         {
  277.             beep();
  278.             return;
  279.         }
  280.  
  281.         u_undoredo();
  282.  
  283.         u_curhead = u_curhead->uh_prev;            /* advance for next redo */
  284.     }
  285. }
  286.  
  287. /*
  288.  * u_undoredo: common code for undo and redo
  289.  *
  290.  * The lines in the file are replaced by the lines in the entry list at u_curhead.
  291.  * The replaced lines in the file are saved in the entry list for the next undo/redo.
  292.  */
  293.     static void
  294. u_undoredo()
  295. {
  296.     char        **newarray = NULL;
  297.     linenr_t    oldsize;
  298.     linenr_t    newsize;
  299.     linenr_t    top, bot;
  300.     linenr_t    lnum = 0;
  301.     linenr_t    newlnum = INVLNUM;
  302.     long        i;
  303.     long        count = 0;
  304.     struct u_entry *uep, *nuep;
  305.     struct u_entry *newlist = NULL;
  306.  
  307.     if (u_synced == FALSE)
  308.     {
  309.         emsg("undo not synced");
  310.         return;
  311.     }
  312.  
  313.     CHANGED;
  314.     for (uep = u_curhead->uh_entry; uep != NULL; uep = nuep)
  315.     {
  316.             top = uep->ue_top;
  317.             bot = uep->ue_bot;
  318.             if (bot == 0)
  319.                 bot = line_count + 1;
  320.             if (top > line_count || top >= bot)
  321.             {
  322.                 emsg("u_undo: line numbers wrong");
  323.                 return;
  324.             }
  325.  
  326.             if (top < newlnum)
  327.             {
  328.                 newlnum = top;
  329.                 Curpos.lnum = top + 1;
  330.             }
  331.             oldsize = bot - top - 1;    /* number of lines before undo */
  332.  
  333.             newsize = uep->ue_size;        /* number of lines after undo */
  334.  
  335.             /* delete the lines between top and bot and save them in newarray */
  336.             if (oldsize)
  337.             {
  338.                 if ((newarray = (char **)alloc_line((unsigned)(sizeof(char *) * oldsize))) == NULL)
  339.                 {
  340.                     /*
  341.                      * We have messed up the entry list, repair is impossible.
  342.                      * we have to free the rest of the list.
  343.                      */
  344.                     while (uep != NULL)
  345.                     {
  346.                         nuep = uep->ue_next;
  347.                         u_freeentry(uep, uep->ue_size);
  348.                         uep = nuep;
  349.                     }
  350.                     break;
  351.                 }
  352.                 /* delete backwards, it goes faster in some cases */
  353.                 for (lnum = bot - 1, i = oldsize; --i >= 0; --lnum)
  354.                     newarray[i] = delsline(lnum);
  355.             }
  356.             /* insert the lines in u_array between top and bot */
  357.             if (newsize)
  358.             {
  359.                 for (lnum = top, i = 0; i < newsize; ++i, ++lnum)
  360.                     appendline(lnum, uep->ue_array[i]);
  361.                 free_line((char *)uep->ue_array);
  362.             }
  363.             count += newsize - oldsize;
  364.             uep->ue_size = oldsize;
  365.             uep->ue_array = newarray;
  366.             uep->ue_bot = lnum + 1;
  367.  
  368.             /*
  369.              * insert this entry in front of the new entry list
  370.              */
  371.             nuep = uep->ue_next;
  372.             uep->ue_next = newlist;
  373.             newlist = uep;
  374.     }
  375.  
  376.     u_curhead->uh_entry = newlist;
  377.  
  378.     msgmore(count);
  379.  
  380.     if (u_curhead->uh_curpos.lnum == Curpos.lnum)
  381.         Curpos.col = u_curhead->uh_curpos.col;
  382.     else
  383.         Curpos.col = 0;
  384.     updateScreen(CURSUPD);
  385. }
  386.  
  387. /*
  388.  * u_sync: stop adding to the current entry list
  389.  */
  390.     void
  391. u_sync()
  392. {
  393.     if (u_synced)
  394.         return;                /* already synced */
  395.     u_getbot();                /* compute ue_bot of previous u_undo */
  396.     u_curhead = NULL;
  397. }
  398.  
  399. /*
  400.  * u_getbot(): compute the line number of the previous u_undo
  401.  */
  402.     static void
  403. u_getbot()
  404. {
  405.     register struct u_entry *uep;
  406.  
  407.     if (u_newhead == NULL || (uep = u_newhead->uh_entry) == NULL)
  408.     {
  409.         emsg("undo list corrupt");
  410.         return;
  411.     }
  412.  
  413.     if (uep->ue_botptr != NULL)
  414.         if ((uep->ue_bot = ptr2nr(uep->ue_botptr, uep->ue_top)) == 0)
  415.         {
  416.             emsg("undo line missing");
  417.             uep->ue_bot = uep->ue_top + 1;    /* guess what it is */
  418.         }
  419.  
  420.     u_synced = TRUE;
  421. }
  422.  
  423. /*
  424.  * u_freelist: free one entry list and adjust the pointers
  425.  */
  426.     static void
  427. u_freelist(uhp)
  428.     struct u_header *uhp;
  429. {
  430.     register struct u_entry *uep, *nuep;
  431.  
  432.     for (uep = uhp->uh_entry; uep != NULL; uep = nuep)
  433.     {
  434.         nuep = uep->ue_next;
  435.         u_freeentry(uep, uep->ue_size);
  436.     }
  437.  
  438.     if (u_curhead == uhp)
  439.         u_curhead = NULL;
  440.  
  441.     if (uhp->uh_next == NULL)
  442.         u_oldhead = uhp->uh_prev;
  443.     else
  444.         uhp->uh_next->uh_prev = uhp->uh_prev;
  445.  
  446.     if (uhp->uh_prev == NULL)
  447.         u_newhead = uhp->uh_next;
  448.     else
  449.         uhp->uh_prev->uh_next = uhp->uh_next;
  450.  
  451.     free_line((char *)uhp);
  452.     --u_numhead;
  453. }
  454.  
  455. /*
  456.  * free entry 'uep' and 'n' lines in uep->ue_array[]
  457.  */
  458.     static void
  459. u_freeentry(uep, n)
  460.     struct u_entry *uep;
  461.     register long n;
  462. {
  463.     while (n)
  464.         free_line(uep->ue_array[--n]);
  465.     free_line((char *)uep);
  466. }
  467.  
  468. /*
  469.  * invalidate the undo buffer; called when storage has already been released
  470.  */
  471.  
  472.     void
  473. u_clearall()
  474. {
  475.     u_newhead = u_oldhead = u_curhead = NULL;
  476.     u_synced = TRUE;
  477.     u_numhead = 0;
  478.     u_line_ptr = NULL;
  479.     u_line_lnum = 0;
  480. }
  481.  
  482. /*
  483.  * save the line "lnum" for the "U" command
  484.  */
  485.     void
  486. u_saveline(lnum)
  487.     linenr_t lnum;
  488. {
  489.     if (lnum == u_line_lnum)        /* line is already saved */
  490.         return;
  491.     if (lnum < 1 || lnum > line_count)    /* should never happen */
  492.         return;
  493.     u_clearline();
  494.     u_line_lnum = lnum;
  495.     if (Curpos.lnum == lnum)
  496.         u_line_colnr = Curpos.col;
  497.     else
  498.         u_line_colnr = 0;
  499.     u_line_ptr = save_line(nr2ptr(lnum));    /* when out of mem alloc() will give a warning */
  500. }
  501.  
  502. /*
  503.  * clear the line saved for the "U" command
  504.  * (this is used externally for crossing a line while in insert mode)
  505.  */
  506.     void
  507. u_clearline()
  508. {
  509.     if (u_line_ptr != NULL)
  510.     {
  511.         free_line(u_line_ptr);
  512.         u_line_ptr = NULL;
  513.         u_line_lnum = 0;
  514.     }
  515. }
  516.  
  517. /*
  518.  * Implementation of the "U" command.
  519.  * Differentiation from vi: "U" can be undone with the next "U".
  520.  * We also allow the cursor to be in another line.
  521.  */
  522.     void
  523. u_undoline()
  524. {
  525.     colnr_t t;
  526.  
  527.     if (u_line_ptr == NULL || u_line_lnum > line_count)
  528.     {
  529.         beep();
  530.         return;
  531.     }
  532.         /* first save the line for the 'u' command */
  533.     u_savecommon(u_line_lnum - 1, u_line_lnum + 1, 0, (char *)0);
  534.     u_line_ptr = replaceline(u_line_lnum, u_line_ptr);
  535.  
  536.     t = u_line_colnr;
  537.     if (Curpos.lnum == u_line_lnum)
  538.         u_line_colnr = Curpos.col;
  539.     Curpos.col = t;
  540.     Curpos.lnum = u_line_lnum;
  541.     cursupdate();
  542.     updateScreen(VALID_TO_CURSCHAR);
  543. }
  544.