home *** CD-ROM | disk | FTP | other *** search
/ Fresh Fish 5 / FreshFish_July-August1994.bin / bbs / util / vim-2.0.lha / Vim-2.0 / src / undo.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-12-15  |  13.9 KB  |  593 lines

  1. /* vi:ts=4:sw=4
  2.  *
  3.  * VIM - Vi IMproved
  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. #include "ops.h"        /* for endop and startop */
  54.  
  55. struct u_entry
  56. {
  57.     struct u_entry    *ue_next;    /* pointer to next entry in list */
  58.     linenr_t        ue_top;        /* number of line above undo block */
  59.     linenr_t        ue_bot;        /* number of line below undo block */
  60.     char            *ue_botptr;    /* pointer to line below undo block */
  61.     char            **ue_array;    /* array of lines in undo block */
  62.     long            ue_size;    /* number of lines in ue_array */
  63. };
  64.  
  65. struct u_header
  66. {
  67.     struct u_header    *uh_next;    /* pointer to next header in list */
  68.     struct u_header    *uh_prev;    /* pointer to previous header in list */
  69.     struct u_entry    *uh_entry;    /* pointer to first entry */
  70.     FPOS             uh_curpos;    /* cursor position before saving */
  71. };
  72.  
  73. static struct u_header *u_oldhead = NULL;    /* pointer to oldest header */
  74. static struct u_header *u_newhead = NULL;    /* pointer to newest header */
  75. static struct u_header *u_curhead = NULL;    /* pointer to current header */
  76. static int                u_numhead = 0;        /* current number of headers */
  77. static int                u_synced = TRUE;    /* entry lists are synced */
  78.  
  79. /*
  80.  * variables for "U" command
  81.  */
  82. static char       *u_line_ptr = NULL;        /* saved line for "U" command */
  83. static linenr_t u_line_lnum;            /* line number of line in u_line */
  84. static colnr_t    u_line_colnr;            /* optional column number */
  85.  
  86. static void u_getbot __ARGS((void));
  87. static int u_savecommon __ARGS((linenr_t, linenr_t, int, linenr_t));
  88. static void u_undoredo __ARGS((void));
  89. static void u_freelist __ARGS((struct u_header *));
  90. static void u_freeentry __ARGS((struct u_entry *, long));
  91.  
  92. /*
  93.  * save the current line for both the "u" and "U" command
  94.  */
  95.     int
  96. u_saveCurpos()
  97. {
  98.     return (u_save((linenr_t)(Curpos.lnum - 1), (linenr_t)(Curpos.lnum + 1)));
  99. }
  100.  
  101. /*
  102.  * Save the lines between "top" and "bot" for both the "u" and "U" command.
  103.  * "top" may be 0 and bot may be line_count + 1.
  104.  * Returns FALSE when lines could not be saved.
  105.  */
  106.     int
  107. u_save(top, bot)
  108.     linenr_t top, bot;
  109. {
  110.     if (top > line_count || top >= bot || bot > line_count + 1)
  111.         return FALSE;    /* rely on caller to do error messages */
  112.  
  113.     if (top + 2 == bot)
  114.         u_saveline((linenr_t)(top + 1));
  115.  
  116.     return (u_savecommon(top, bot, FALSE, (linenr_t)0));
  117. }
  118.  
  119. /*
  120.  * save the line "lnum" (used by :s command)
  121.  * the line is handed over to the undo routines
  122.  * The line is replaced, so the new bottom line is lnum + 1.
  123.  */
  124.     int
  125. u_savesub(lnum)
  126.     linenr_t    lnum;
  127. {
  128.     return (u_savecommon(lnum - 1, lnum + 1, TRUE, lnum + 1));
  129. }
  130.  
  131. /*
  132.  * a new line is inserted before line "lnum" (used by :s command)
  133.  * The line is inserted, so the new bottom line is lnum + 1.
  134.  */
  135.      int
  136. u_inssub(lnum)
  137.     linenr_t    lnum;
  138. {
  139.     return (u_savecommon(lnum - 1, lnum, TRUE, lnum + 1));
  140. }
  141.  
  142. /*
  143.  * save the lines "lnum" - "lnum" + nlines (used by delete command)
  144.  * the lines are handed over to the undo routines
  145.  * The lines are deleted, so the new bottom line is lnum.
  146.  */
  147.     int
  148. u_savedel(lnum, nlines)
  149.     linenr_t    lnum;
  150.     long        nlines;
  151. {
  152.     return (u_savecommon(lnum - 1, lnum + nlines, TRUE, lnum));
  153. }
  154.  
  155.     static int 
  156. u_savecommon(top, bot, nocopy, newbot)
  157.     linenr_t top, bot;
  158.     int nocopy;
  159.     linenr_t newbot;
  160. {
  161.     linenr_t        lnum;
  162.     long            i;
  163.     struct u_header *uhp;
  164.     struct u_entry    *uep;
  165.     long            size;
  166.  
  167.     /*
  168.      * if u_synced == TRUE make a new header
  169.      */
  170.     if (u_synced)
  171.     {
  172.         /*
  173.          * if we undid more than we redid, free the entry lists before and
  174.          * including u_curhead
  175.          */
  176.         while (u_curhead != NULL)
  177.             u_freelist(u_newhead);
  178.  
  179.         /*
  180.          * free headers to keep the size right
  181.          */
  182.         while (u_numhead > p_ul && u_oldhead != NULL)
  183.             u_freelist(u_oldhead);
  184.  
  185.         if (p_ul < 0)            /* no undo at all */
  186.             goto noundo;
  187.  
  188.         /*
  189.          * make a new header entry
  190.          */
  191.         uhp = (struct u_header *)alloc_line((unsigned)sizeof(struct u_header));
  192.         if (uhp == NULL)
  193.             goto nomem;
  194.         uhp->uh_prev = NULL;
  195.         uhp->uh_next = u_newhead;
  196.         if (u_newhead != NULL)
  197.             u_newhead->uh_prev = uhp;
  198.         uhp->uh_entry = NULL;
  199.         uhp->uh_curpos = Curpos;    /* save cursor position for undo */
  200.         u_newhead = uhp;
  201.         if (u_oldhead == NULL)
  202.             u_oldhead = uhp;
  203.         ++u_numhead;
  204.     }
  205.     else    /* find line number for ue_botptr for previous u_save() */
  206.         u_getbot();
  207.  
  208.     size = bot - top - 1;
  209. #ifndef UNIX
  210.         /*
  211.          * With Amiga and MSDOS we can't handle big undo's, because then
  212.          * alloc_line would have to allocate a block larger than 32K
  213.          */
  214.     if (size >= 8000)
  215.         goto nomem;
  216. #endif
  217.  
  218.     /*
  219.      * add lines in front of entry list
  220.      */
  221.     uep = (struct u_entry *)alloc_line((unsigned)sizeof(struct u_entry));
  222.     if (uep == NULL)
  223.         goto nomem;
  224.  
  225.     uep->ue_size = size;
  226.     uep->ue_top = top;
  227.     uep->ue_botptr = NULL;
  228.     if (newbot)
  229.         uep->ue_bot = newbot;
  230.         /*
  231.          * use 0 for ue_bot if bot is below last line or if the buffer is empty, in
  232.          * which case the last line may be replaced (e.g. with 'O' command).
  233.          */
  234.     else if (bot > line_count || bufempty())
  235.         uep->ue_bot = 0;
  236.     else
  237.         uep->ue_botptr = nr2ptr(bot);    /* we have to do ptr2nr(ue_botptr) later */
  238.  
  239.     if (size)
  240.     {
  241.         if ((uep->ue_array = (char **)alloc_line((unsigned)(sizeof(char *) * size))) == NULL)
  242.         {
  243.             u_freeentry(uep, 0L);
  244.             goto nomem;
  245.         }
  246.         for (i = 0, lnum = top + 1; i < size; ++i)
  247.         {
  248.             if (nocopy)
  249.                 uep->ue_array[i] = nr2ptr(lnum++);
  250.             else if ((uep->ue_array[i] = save_line(nr2ptr(lnum++))) == NULL)
  251.             {
  252.                 u_freeentry(uep, i);
  253.                 goto nomem;
  254.             }
  255.         }
  256.     }
  257.     uep->ue_next = u_newhead->uh_entry;
  258.     u_newhead->uh_entry = uep;
  259.     u_synced = FALSE;
  260.     return TRUE;
  261.  
  262. nomem:
  263.     if (ask_yesno("no undo possible; continue anyway") == 'y')
  264.     {
  265. noundo:
  266.         if (nocopy)
  267.             for (lnum = top + 1; lnum < bot; ++lnum)
  268.                 free_line(nr2ptr(lnum));
  269.         return TRUE;
  270.     }
  271.     return FALSE;
  272. }
  273.  
  274.     void
  275. u_undo(count)
  276.     int count;
  277. {
  278.     /*
  279.      * If we get an undo command while executing a macro, we behave like the 
  280.      * original vi. If this happens twice in one macro the result will not
  281.      * be compatible.
  282.      */
  283.     if (u_synced == FALSE)
  284.     {
  285.         u_sync();
  286.         count = 1;
  287.     }
  288.  
  289.     startop.lnum = 0;            /* unset '[ mark */
  290.     endop.lnum = 0;                /* unset '] mark */
  291.     while (count--)
  292.     {
  293.         if (u_curhead == NULL)                        /* first undo */
  294.             u_curhead = u_newhead;
  295.         else if (p_ul > 0)                            /* multi level undo */
  296.             u_curhead = u_curhead->uh_next;            /* get next undo */
  297.  
  298.         if (u_numhead == 0 || u_curhead == NULL)    /* nothing to undo */
  299.         {
  300.             u_curhead = u_oldhead;                    /* stick u_curhead at end */
  301.             beep();
  302.             return;
  303.         }
  304.  
  305.         u_undoredo();
  306.     }
  307. }
  308.  
  309.     void
  310. u_redo(count)
  311.     int count;
  312. {
  313.     while (count--)
  314.     {
  315.         if (u_curhead == NULL || p_ul <= 0)        /* nothing to redo */
  316.         {
  317.             beep();
  318.             return;
  319.         }
  320.  
  321.         u_undoredo();
  322.  
  323.         u_curhead = u_curhead->uh_prev;            /* advance for next redo */
  324.     }
  325. }
  326.  
  327. /*
  328.  * u_undoredo: common code for undo and redo
  329.  *
  330.  * The lines in the file are replaced by the lines in the entry list at u_curhead.
  331.  * The replaced lines in the file are saved in the entry list for the next undo/redo.
  332.  */
  333.     static void
  334. u_undoredo()
  335. {
  336.     char        **newarray = NULL;
  337.     linenr_t    oldsize;
  338.     linenr_t    newsize;
  339.     linenr_t    top, bot;
  340.     linenr_t    lnum;
  341.     linenr_t    newlnum = INVLNUM;
  342.     long        i;
  343.     long        newcount = 0, oldcount = 0;
  344.     struct u_entry *uep, *nuep;
  345.     struct u_entry *newlist = NULL;
  346.  
  347.     CHANGED;
  348.     for (uep = u_curhead->uh_entry; uep != NULL; uep = nuep)
  349.     {
  350.         top = uep->ue_top;
  351.         bot = uep->ue_bot;
  352.         if (bot == 0)
  353.             bot = line_count + 1;
  354.         if (top > line_count || top >= bot || bot > line_count + 1)
  355.         {
  356.             emsg("u_undo: line numbers wrong");
  357.             return;
  358.         }
  359.  
  360.         if (top < newlnum)
  361.         {
  362.             newlnum = top;
  363.             Curpos.lnum = top + 1;
  364.         }
  365.         oldsize = bot - top - 1;    /* number of lines before undo */
  366.  
  367.         newsize = uep->ue_size;        /* number of lines after undo */
  368.  
  369.         /* delete the lines between top and bot and save them in newarray */
  370.         if (oldsize)
  371.         {
  372.             if ((newarray = (char **)alloc_line((unsigned)(sizeof(char *) * oldsize))) == NULL)
  373.             {
  374.                 /*
  375.                  * We have messed up the entry list, repair is impossible.
  376.                  * we have to free the rest of the list.
  377.                  */
  378.                 while (uep != NULL)
  379.                 {
  380.                     nuep = uep->ue_next;
  381.                     u_freeentry(uep, uep->ue_size);
  382.                     uep = nuep;
  383.                 }
  384.                 break;
  385.             }
  386.             /* delete backwards, it goes faster in some cases */
  387.             for (lnum = bot - 1, i = oldsize; --i >= 0; --lnum)
  388.                 newarray[i] = delsline(lnum, oldsize != newsize);
  389.         }
  390.  
  391.         /* adjust the marks if the number of lines does not change */
  392.         if (oldsize == newsize)
  393.             for (i = 0; i < oldsize; ++i)
  394.                 adjustmark(newarray[i], uep->ue_array[i]);
  395.  
  396.         /* insert the lines in u_array between top and bot */
  397.         if (newsize)
  398.         {
  399.             for (lnum = top, i = 0; i < newsize; ++i, ++lnum)
  400.                 appendline(lnum, uep->ue_array[i]);
  401.             free_line((char *)uep->ue_array);
  402.         }
  403.         newcount += newsize;
  404.         oldcount += oldsize;
  405.         uep->ue_size = oldsize;
  406.         uep->ue_array = newarray;
  407.         uep->ue_bot = top + newsize + 1;
  408.  
  409.         /*
  410.          * insert this entry in front of the new entry list
  411.          */
  412.         nuep = uep->ue_next;
  413.         uep->ue_next = newlist;
  414.         newlist = uep;
  415.     }
  416.  
  417.     u_curhead->uh_entry = newlist;
  418.  
  419.     /*
  420.      * If we deleted or added lines, report the number of less/more lines.
  421.      * Otherwise, report the number of changes (this may be incorrect
  422.      * in some cases, but it's better than nothing).
  423.      */
  424.     if ((oldcount -= newcount) != 0)
  425.         msgmore(-oldcount);
  426.     else if (newcount > p_report)
  427.         smsg("%ld change%s", newcount, plural(newcount));
  428.  
  429.     if (u_curhead->uh_curpos.lnum == Curpos.lnum)
  430.         Curpos.col = u_curhead->uh_curpos.col;
  431.     else
  432.         Curpos.col = 0;
  433.     updateScreen(CURSUPD);
  434. }
  435.  
  436. /*
  437.  * u_sync: stop adding to the current entry list
  438.  */
  439.     void
  440. u_sync()
  441. {
  442.     if (u_synced)
  443.         return;                /* already synced */
  444.     u_getbot();                /* compute ue_bot of previous u_undo */
  445.     u_curhead = NULL;
  446. }
  447.  
  448. /*
  449.  * u_getbot(): compute the line number of the previous u_undo
  450.  */
  451.     static void
  452. u_getbot()
  453. {
  454.     register struct u_entry *uep;
  455.  
  456.     if (u_newhead == NULL || (uep = u_newhead->uh_entry) == NULL)
  457.     {
  458.         emsg("undo list corrupt");
  459.         return;
  460.     }
  461.  
  462.     if (uep->ue_botptr != NULL)
  463.         if ((uep->ue_bot = ptr2nr(uep->ue_botptr, uep->ue_top)) == 0)
  464.         {
  465.             emsg("undo line missing");
  466.             uep->ue_bot = uep->ue_top + 1;    /* guess what it is */
  467.         }
  468.  
  469.     u_synced = TRUE;
  470. }
  471.  
  472. /*
  473.  * u_freelist: free one entry list and adjust the pointers
  474.  */
  475.     static void
  476. u_freelist(uhp)
  477.     struct u_header *uhp;
  478. {
  479.     register struct u_entry *uep, *nuep;
  480.  
  481.     for (uep = uhp->uh_entry; uep != NULL; uep = nuep)
  482.     {
  483.         nuep = uep->ue_next;
  484.         u_freeentry(uep, uep->ue_size);
  485.     }
  486.  
  487.     if (u_curhead == uhp)
  488.         u_curhead = NULL;
  489.  
  490.     if (uhp->uh_next == NULL)
  491.         u_oldhead = uhp->uh_prev;
  492.     else
  493.         uhp->uh_next->uh_prev = uhp->uh_prev;
  494.  
  495.     if (uhp->uh_prev == NULL)
  496.         u_newhead = uhp->uh_next;
  497.     else
  498.         uhp->uh_prev->uh_next = uhp->uh_next;
  499.  
  500.     free_line((char *)uhp);
  501.     --u_numhead;
  502. }
  503.  
  504. /*
  505.  * free entry 'uep' and 'n' lines in uep->ue_array[]
  506.  */
  507.     static void
  508. u_freeentry(uep, n)
  509.     struct u_entry *uep;
  510.     register long n;
  511. {
  512.     while (n)
  513.         free_line(uep->ue_array[--n]);
  514.     free_line((char *)uep);
  515. }
  516.  
  517. /*
  518.  * invalidate the undo buffer; called when storage has already been released
  519.  */
  520.  
  521.     void
  522. u_clearall()
  523. {
  524.     u_newhead = u_oldhead = u_curhead = NULL;
  525.     u_synced = TRUE;
  526.     u_numhead = 0;
  527.     u_line_ptr = NULL;
  528.     u_line_lnum = 0;
  529. }
  530.  
  531. /*
  532.  * save the line "lnum" for the "U" command
  533.  */
  534.     void
  535. u_saveline(lnum)
  536.     linenr_t lnum;
  537. {
  538.     if (lnum == u_line_lnum)        /* line is already saved */
  539.         return;
  540.     if (lnum < 1 || lnum > line_count)    /* should never happen */
  541.         return;
  542.     u_clearline();
  543.     u_line_lnum = lnum;
  544.     if (Curpos.lnum == lnum)
  545.         u_line_colnr = Curpos.col;
  546.     else
  547.         u_line_colnr = 0;
  548.     u_line_ptr = save_line(nr2ptr(lnum));    /* when out of mem alloc() will give a warning */
  549. }
  550.  
  551. /*
  552.  * clear the line saved for the "U" command
  553.  * (this is used externally for crossing a line while in insert mode)
  554.  */
  555.     void
  556. u_clearline()
  557. {
  558.     if (u_line_ptr != NULL)
  559.     {
  560.         free_line(u_line_ptr);
  561.         u_line_ptr = NULL;
  562.         u_line_lnum = 0;
  563.     }
  564. }
  565.  
  566. /*
  567.  * Implementation of the "U" command.
  568.  * Differentiation from vi: "U" can be undone with the next "U".
  569.  * We also allow the cursor to be in another line.
  570.  */
  571.     void
  572. u_undoline()
  573. {
  574.     colnr_t t;
  575.  
  576.     if (u_line_ptr == NULL || u_line_lnum > line_count)
  577.     {
  578.         beep();
  579.         return;
  580.     }
  581.         /* first save the line for the 'u' command */
  582.     u_savecommon(u_line_lnum - 1, u_line_lnum + 1, FALSE, (linenr_t)0);
  583.     u_line_ptr = replaceline(u_line_lnum, u_line_ptr);
  584.  
  585.     t = u_line_colnr;
  586.     if (Curpos.lnum == u_line_lnum)
  587.         u_line_colnr = Curpos.col;
  588.     Curpos.col = t;
  589.     Curpos.lnum = u_line_lnum;
  590.     cursupdate();
  591.     updateScreen(VALID_TO_CURSCHAR);
  592. }
  593.