comparison src/undo.c @ 2280:941ff1cd317a vim73

Add file save counter to undo information. Add undotree() function.
author Bram Moolenaar <bram@vim.org>
date Sun, 27 Jun 2010 01:15:55 +0200
parents 2b33a7678e7b
children e41433ea71df
comparison
equal deleted inserted replaced
2279:420f001db3d2 2280:941ff1cd317a
104 static void u_free_uhp __ARGS((u_header_T *uhp)); 104 static void u_free_uhp __ARGS((u_header_T *uhp));
105 static size_t fwrite_crypt __ARGS((buf_T *buf UNUSED, char_u *ptr, size_t len, FILE *fp)); 105 static size_t fwrite_crypt __ARGS((buf_T *buf UNUSED, char_u *ptr, size_t len, FILE *fp));
106 static char_u *read_string_decrypt __ARGS((buf_T *buf UNUSED, FILE *fd, int len)); 106 static char_u *read_string_decrypt __ARGS((buf_T *buf UNUSED, FILE *fd, int len));
107 static int serialize_header __ARGS((FILE *fp, buf_T *buf, char_u *hash)); 107 static int serialize_header __ARGS((FILE *fp, buf_T *buf, char_u *hash));
108 static int serialize_uhp __ARGS((FILE *fp, buf_T *buf, u_header_T *uhp)); 108 static int serialize_uhp __ARGS((FILE *fp, buf_T *buf, u_header_T *uhp));
109 static u_header_T *unserialize_uhp __ARGS((FILE *fp, char_u *file_name)); 109 static u_header_T *unserialize_uhp __ARGS((FILE *fp, char_u *file_name, int new_version));
110 static int serialize_uep __ARGS((FILE *fp, buf_T *buf, u_entry_T *uep)); 110 static int serialize_uep __ARGS((FILE *fp, buf_T *buf, u_entry_T *uep));
111 static u_entry_T *unserialize_uep __ARGS((FILE *fp, int *error, char_u *file_name)); 111 static u_entry_T *unserialize_uep __ARGS((FILE *fp, int *error, char_u *file_name));
112 static void serialize_pos __ARGS((pos_T pos, FILE *fp)); 112 static void serialize_pos __ARGS((pos_T pos, FILE *fp));
113 static void unserialize_pos __ARGS((pos_T *pos, FILE *fp)); 113 static void unserialize_pos __ARGS((pos_T *pos, FILE *fp));
114 static void serialize_visualinfo __ARGS((visualinfo_T *info, FILE *fp)); 114 static void serialize_visualinfo __ARGS((visualinfo_T *info, FILE *fp));
471 curbuf->b_u_newhead->uh_prev.ptr = uhp; 471 curbuf->b_u_newhead->uh_prev.ptr = uhp;
472 472
473 uhp->uh_seq = ++curbuf->b_u_seq_last; 473 uhp->uh_seq = ++curbuf->b_u_seq_last;
474 curbuf->b_u_seq_cur = uhp->uh_seq; 474 curbuf->b_u_seq_cur = uhp->uh_seq;
475 uhp->uh_time = time(NULL); 475 uhp->uh_time = time(NULL);
476 curbuf->b_u_seq_time = uhp->uh_time + 1; 476 uhp->uh_save_nr = 0;
477 curbuf->b_u_time_cur = uhp->uh_time + 1;
477 478
478 uhp->uh_walk = 0; 479 uhp->uh_walk = 0;
479 uhp->uh_entry = NULL; 480 uhp->uh_entry = NULL;
480 uhp->uh_getbot_entry = NULL; 481 uhp->uh_getbot_entry = NULL;
481 uhp->uh_cursor = curwin->w_cursor; /* save cursor pos. for undo */ 482 uhp->uh_cursor = curwin->w_cursor; /* save cursor pos. for undo */
669 # define UF_START_MAGIC_LEN 9 670 # define UF_START_MAGIC_LEN 9
670 # define UF_HEADER_MAGIC 0x5fd0 /* magic at start of header */ 671 # define UF_HEADER_MAGIC 0x5fd0 /* magic at start of header */
671 # define UF_HEADER_END_MAGIC 0xe7aa /* magic after last header */ 672 # define UF_HEADER_END_MAGIC 0xe7aa /* magic after last header */
672 # define UF_ENTRY_MAGIC 0xf518 /* magic at start of entry */ 673 # define UF_ENTRY_MAGIC 0xf518 /* magic at start of entry */
673 # define UF_ENTRY_END_MAGIC 0x3581 /* magic after last entry */ 674 # define UF_ENTRY_END_MAGIC 0x3581 /* magic after last entry */
674 # define UF_VERSION 1 /* 2-byte undofile version number */ 675 # define UF_VERSION_PREV 1 /* 2-byte undofile version number */
675 # define UF_VERSION_CRYPT 0x8001 /* idem, encrypted */ 676 # define UF_VERSION 2 /* 2-byte undofile version number */
677 # define UF_VERSION_CRYPT_PREV 0x8001 /* idem, encrypted */
678 # define UF_VERSION_CRYPT 0x8002 /* idem, encrypted */
679
680 /* extra fields for header */
681 # define UF_LAST_SAVE_NR 1
682
683 /* extra fields for uhp */
684 # define UHP_SAVE_NR 1
676 685
677 static char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s"); 686 static char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s");
678 687
679 /* 688 /*
680 * Compute the hash for the current buffer text into hash[UNDO_HASH_SIZE]. 689 * Compute the hash for the current buffer text into hash[UNDO_HASH_SIZE].
916 put_header_ptr(fp, buf->b_u_curhead); 925 put_header_ptr(fp, buf->b_u_curhead);
917 926
918 put_bytes(fp, (long_u)buf->b_u_numhead, 4); 927 put_bytes(fp, (long_u)buf->b_u_numhead, 4);
919 put_bytes(fp, (long_u)buf->b_u_seq_last, 4); 928 put_bytes(fp, (long_u)buf->b_u_seq_last, 4);
920 put_bytes(fp, (long_u)buf->b_u_seq_cur, 4); 929 put_bytes(fp, (long_u)buf->b_u_seq_cur, 4);
921 put_time(fp, buf->b_u_seq_time); 930 put_time(fp, buf->b_u_time_cur);
931
932 /* Optional fields. */
933 putc(4, fp);
934 putc(UF_LAST_SAVE_NR, fp);
935 put_bytes(fp, (long_u)buf->b_u_last_save_nr, 4);
936
937 putc(0, fp); /* end marker */
922 938
923 return OK; 939 return OK;
924 } 940 }
925 941
926 static int 942 static int
960 serialize_visualinfo(&info, fp); 976 serialize_visualinfo(&info, fp);
961 } 977 }
962 #endif 978 #endif
963 put_time(fp, uhp->uh_time); 979 put_time(fp, uhp->uh_time);
964 980
981 /* Optional fields. */
982 putc(4, fp);
983 putc(UHP_SAVE_NR, fp);
984 put_bytes(fp, (long_u)uhp->uh_save_nr, 4);
985
986 putc(0, fp); /* end marker */
987
965 /* Write all the entries. */ 988 /* Write all the entries. */
966 for (uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next) 989 for (uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next)
967 { 990 {
968 put_bytes(fp, (long_u)UF_ENTRY_MAGIC, 2); 991 put_bytes(fp, (long_u)UF_ENTRY_MAGIC, 2);
969 if (serialize_uep(fp, buf, uep) == FAIL) 992 if (serialize_uep(fp, buf, uep) == FAIL)
972 put_bytes(fp, (long_u)UF_ENTRY_END_MAGIC, 2); 995 put_bytes(fp, (long_u)UF_ENTRY_END_MAGIC, 2);
973 return OK; 996 return OK;
974 } 997 }
975 998
976 static u_header_T * 999 static u_header_T *
977 unserialize_uhp(fp, file_name) 1000 unserialize_uhp(fp, file_name, new_version)
978 FILE *fp; 1001 FILE *fp;
979 char_u *file_name; 1002 char_u *file_name;
1003 int new_version;
980 { 1004 {
981 u_header_T *uhp; 1005 u_header_T *uhp;
982 int i; 1006 int i;
983 u_entry_T *uep, *last_uep; 1007 u_entry_T *uep, *last_uep;
984 int c; 1008 int c;
1018 visualinfo_T info; 1042 visualinfo_T info;
1019 unserialize_visualinfo(&info, fp); 1043 unserialize_visualinfo(&info, fp);
1020 } 1044 }
1021 #endif 1045 #endif
1022 uhp->uh_time = get8ctime(fp); 1046 uhp->uh_time = get8ctime(fp);
1047
1048 /* Optional fields. */
1049 if (new_version)
1050 for (;;)
1051 {
1052 int len = getc(fp);
1053 int what;
1054
1055 if (len == 0)
1056 break;
1057 what = getc(fp);
1058 switch (what)
1059 {
1060 case UHP_SAVE_NR:
1061 uhp->uh_save_nr = get4c(fp);
1062 break;
1063 default:
1064 /* field not supported, skip */
1065 while (--len >= 0)
1066 (void)getc(fp);
1067 }
1068 }
1023 1069
1024 /* Unserialize the uep list. */ 1070 /* Unserialize the uep list. */
1025 last_uep = NULL; 1071 last_uep = NULL;
1026 while ((c = get2c(fp)) == UF_ENTRY_MAGIC) 1072 while ((c = get2c(fp)) == UF_ENTRY_MAGIC)
1027 { 1073 {
1396 } 1442 }
1397 1443
1398 /* Undo must be synced. */ 1444 /* Undo must be synced. */
1399 u_sync(TRUE); 1445 u_sync(TRUE);
1400 1446
1447 /* Increase the write count, store it in the last undo header, what would
1448 * be used for "u". */
1449 ++buf->b_u_last_save_nr;
1450 uhp = buf->b_u_curhead;
1451 if (uhp != NULL)
1452 uhp = uhp->uh_next.ptr;
1453 else
1454 uhp = buf->b_u_newhead;
1455 if (uhp != NULL)
1456 uhp->uh_save_nr = buf->b_u_last_save_nr;
1457
1401 /* 1458 /*
1402 * Write the header. 1459 * Write the header.
1403 */ 1460 */
1404 if (serialize_header(fp, buf, hash) == FAIL) 1461 if (serialize_header(fp, buf, hash) == FAIL)
1405 goto write_error; 1462 goto write_error;
1494 char_u *orig_name; 1551 char_u *orig_name;
1495 { 1552 {
1496 char_u *file_name; 1553 char_u *file_name;
1497 FILE *fp; 1554 FILE *fp;
1498 long version, str_len; 1555 long version, str_len;
1556 int new_version;
1499 char_u *line_ptr = NULL; 1557 char_u *line_ptr = NULL;
1500 linenr_T line_lnum; 1558 linenr_T line_lnum;
1501 colnr_T line_colnr; 1559 colnr_T line_colnr;
1502 linenr_T line_count; 1560 linenr_T line_count;
1503 int num_head = 0; 1561 int num_head = 0;
1504 long old_header_seq, new_header_seq, cur_header_seq; 1562 long old_header_seq, new_header_seq, cur_header_seq;
1505 long seq_last, seq_cur; 1563 long seq_last, seq_cur;
1564 long_u last_save_nr = 0;
1506 short old_idx = -1, new_idx = -1, cur_idx = -1; 1565 short old_idx = -1, new_idx = -1, cur_idx = -1;
1507 long num_read_uhps = 0; 1566 long num_read_uhps = 0;
1508 time_t seq_time; 1567 time_t seq_time;
1509 int i, j; 1568 int i, j;
1510 int c; 1569 int c;
1573 { 1632 {
1574 EMSG2(_("E823: Not an undo file: %s"), file_name); 1633 EMSG2(_("E823: Not an undo file: %s"), file_name);
1575 goto error; 1634 goto error;
1576 } 1635 }
1577 version = get2c(fp); 1636 version = get2c(fp);
1578 if (version == UF_VERSION_CRYPT) 1637 if (version == UF_VERSION_CRYPT || version == UF_VERSION_CRYPT_PREV)
1579 { 1638 {
1580 #ifdef FEAT_CRYPT 1639 #ifdef FEAT_CRYPT
1581 if (*curbuf->b_p_key == NUL) 1640 if (*curbuf->b_p_key == NUL)
1582 { 1641 {
1583 EMSG2(_("E832: Non-encrypted file has encrypted undo file: %s"), 1642 EMSG2(_("E832: Non-encrypted file has encrypted undo file: %s"),
1593 #else 1652 #else
1594 EMSG2(_("E827: Undo file is encrypted: %s"), file_name); 1653 EMSG2(_("E827: Undo file is encrypted: %s"), file_name);
1595 goto error; 1654 goto error;
1596 #endif 1655 #endif
1597 } 1656 }
1598 else if (version != UF_VERSION) 1657 else if (version != UF_VERSION && version != UF_VERSION_PREV)
1599 { 1658 {
1600 EMSG2(_("E824: Incompatible undo file: %s"), file_name); 1659 EMSG2(_("E824: Incompatible undo file: %s"), file_name);
1601 goto error; 1660 goto error;
1602 } 1661 }
1662 new_version = (version == UF_VERSION || version == UF_VERSION_CRYPT);
1603 1663
1604 if (fread(read_hash, UNDO_HASH_SIZE, 1, fp) != 1) 1664 if (fread(read_hash, UNDO_HASH_SIZE, 1, fp) != 1)
1605 { 1665 {
1606 corruption_error("hash", file_name); 1666 corruption_error("hash", file_name);
1607 goto error; 1667 goto error;
1643 num_head = get4c(fp); 1703 num_head = get4c(fp);
1644 seq_last = get4c(fp); 1704 seq_last = get4c(fp);
1645 seq_cur = get4c(fp); 1705 seq_cur = get4c(fp);
1646 seq_time = get8ctime(fp); 1706 seq_time = get8ctime(fp);
1647 1707
1708 /* Optional header fields, not in previous version. */
1709 if (new_version)
1710 for (;;)
1711 {
1712 int len = getc(fp);
1713 int what;
1714
1715 if (len == 0 || len == EOF)
1716 break;
1717 what = getc(fp);
1718 switch (what)
1719 {
1720 case UF_LAST_SAVE_NR:
1721 last_save_nr = get4c(fp);
1722 break;
1723 default:
1724 /* field not supported, skip */
1725 while (--len >= 0)
1726 (void)getc(fp);
1727 }
1728 }
1729
1648 /* uhp_table will store the freshly created undo headers we allocate 1730 /* uhp_table will store the freshly created undo headers we allocate
1649 * until we insert them into curbuf. The table remains sorted by the 1731 * until we insert them into curbuf. The table remains sorted by the
1650 * sequence numbers of the headers. 1732 * sequence numbers of the headers.
1651 * When there are no headers uhp_table is NULL. */ 1733 * When there are no headers uhp_table is NULL. */
1652 if (num_head > 0) 1734 if (num_head > 0)
1663 { 1745 {
1664 corruption_error("num_head too small", file_name); 1746 corruption_error("num_head too small", file_name);
1665 goto error; 1747 goto error;
1666 } 1748 }
1667 1749
1668 uhp = unserialize_uhp(fp, file_name); 1750 uhp = unserialize_uhp(fp, file_name, new_version);
1669 if (uhp == NULL) 1751 if (uhp == NULL)
1670 goto error; 1752 goto error;
1671 uhp_table[num_read_uhps++] = uhp; 1753 uhp_table[num_read_uhps++] = uhp;
1672 } 1754 }
1673 1755
1764 curbuf->b_u_line_lnum = line_lnum; 1846 curbuf->b_u_line_lnum = line_lnum;
1765 curbuf->b_u_line_colnr = line_colnr; 1847 curbuf->b_u_line_colnr = line_colnr;
1766 curbuf->b_u_numhead = num_head; 1848 curbuf->b_u_numhead = num_head;
1767 curbuf->b_u_seq_last = seq_last; 1849 curbuf->b_u_seq_last = seq_last;
1768 curbuf->b_u_seq_cur = seq_cur; 1850 curbuf->b_u_seq_cur = seq_cur;
1769 curbuf->b_u_seq_time = seq_time; 1851 curbuf->b_u_time_cur = seq_time;
1852 curbuf->b_u_last_save_nr = last_save_nr;
1770 1853
1771 curbuf->b_u_synced = TRUE; 1854 curbuf->b_u_synced = TRUE;
1772 vim_free(uhp_table); 1855 vim_free(uhp_table);
1773 1856
1774 #ifdef U_DEBUG 1857 #ifdef U_DEBUG
1960 else 2043 else
1961 { 2044 {
1962 /* When doing computations with time_t subtract starttime, because 2045 /* When doing computations with time_t subtract starttime, because
1963 * time_t converted to a long may result in a wrong number. */ 2046 * time_t converted to a long may result in a wrong number. */
1964 if (sec) 2047 if (sec)
1965 target = (long)(curbuf->b_u_seq_time - starttime) + step; 2048 target = (long)(curbuf->b_u_time_cur - starttime) + step;
1966 else 2049 else
1967 target = curbuf->b_u_seq_cur + step; 2050 target = curbuf->b_u_seq_cur + step;
1968 if (step < 0) 2051 if (step < 0)
1969 { 2052 {
1970 if (target < 0) 2053 if (target < 0)
2456 * work we compute this as being just above the just undone change. */ 2539 * work we compute this as being just above the just undone change. */
2457 --curbuf->b_u_seq_cur; 2540 --curbuf->b_u_seq_cur;
2458 2541
2459 /* The timestamp can be the same for multiple changes, just use the one of 2542 /* The timestamp can be the same for multiple changes, just use the one of
2460 * the undone/redone change. */ 2543 * the undone/redone change. */
2461 curbuf->b_u_seq_time = curhead->uh_time; 2544 curbuf->b_u_time_cur = curhead->uh_time;
2462 #ifdef U_DEBUG 2545 #ifdef U_DEBUG
2463 u_check(FALSE); 2546 u_check(FALSE);
2464 #endif 2547 #endif
2465 } 2548 }
2466 2549
2593 break; 2676 break;
2594 vim_snprintf((char *)IObuff, IOSIZE, "%6ld %7ld ", 2677 vim_snprintf((char *)IObuff, IOSIZE, "%6ld %7ld ",
2595 uhp->uh_seq, changes); 2678 uhp->uh_seq, changes);
2596 u_add_time(IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff), 2679 u_add_time(IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff),
2597 uhp->uh_time); 2680 uhp->uh_time);
2681 if (uhp->uh_save_nr > 0)
2682 {
2683 while (STRLEN(IObuff) < 32)
2684 STRCAT(IObuff, " ");
2685 vim_snprintf_add((char *)IObuff, IOSIZE,
2686 " %3ld", uhp->uh_save_nr);
2687 }
2598 ((char_u **)(ga.ga_data))[ga.ga_len++] = vim_strsave(IObuff); 2688 ((char_u **)(ga.ga_data))[ga.ga_len++] = vim_strsave(IObuff);
2599 } 2689 }
2600 2690
2601 uhp->uh_walk = mark; 2691 uhp->uh_walk = mark;
2602 2692
2643 else 2733 else
2644 { 2734 {
2645 sort_strings((char_u **)ga.ga_data, ga.ga_len); 2735 sort_strings((char_u **)ga.ga_data, ga.ga_len);
2646 2736
2647 msg_start(); 2737 msg_start();
2648 msg_puts_attr((char_u *)_("number changes time"), hl_attr(HLF_T)); 2738 msg_puts_attr((char_u *)_("number changes time saved"),
2739 hl_attr(HLF_T));
2649 for (i = 0; i < ga.ga_len && !got_int; ++i) 2740 for (i = 0; i < ga.ga_len && !got_int; ++i)
2650 { 2741 {
2651 msg_putchar('\n'); 2742 msg_putchar('\n');
2652 if (got_int) 2743 if (got_int)
2653 break; 2744 break;
3046 #ifdef FEAT_QUICKFIX 3137 #ifdef FEAT_QUICKFIX
3047 !bt_dontwrite(curbuf) && 3138 !bt_dontwrite(curbuf) &&
3048 #endif 3139 #endif
3049 (curbuf->b_changed || file_ff_differs(curbuf)); 3140 (curbuf->b_changed || file_ff_differs(curbuf));
3050 } 3141 }
3142
3143 #if defined(FEAT_EVAL) || defined(PROTO)
3144 /*
3145 * For undotree(): Append the list of undo blocks at "first_uhp" to "list".
3146 * Recursive.
3147 */
3148 void
3149 u_eval_tree(first_uhp, list)
3150 u_header_T *first_uhp;
3151 list_T *list;
3152 {
3153 u_header_T *uhp = first_uhp;
3154 dict_T *dict;
3155
3156 while (uhp != NULL)
3157 {
3158 dict = dict_alloc();
3159 if (dict == NULL)
3160 return;
3161 dict_add_nr_str(dict, "seq", uhp->uh_seq, NULL);
3162 dict_add_nr_str(dict, "time", uhp->uh_time, NULL);
3163 if (uhp == curbuf->b_u_newhead)
3164 dict_add_nr_str(dict, "newhead", 1, NULL);
3165 if (uhp == curbuf->b_u_curhead)
3166 dict_add_nr_str(dict, "curhead", 1, NULL);
3167 if (uhp->uh_save_nr > 0)
3168 dict_add_nr_str(dict, "save", uhp->uh_save_nr, NULL);
3169
3170 if (uhp->uh_alt_next.ptr != NULL)
3171 {
3172 list_T *alt_list = list_alloc();
3173
3174 if (alt_list != NULL)
3175 {
3176 /* Recursive call to add alternate undo tree. */
3177 u_eval_tree(uhp->uh_alt_next.ptr, alt_list);
3178 dict_add_list(dict, "alt", alt_list);
3179 }
3180 }
3181
3182 list_append_dict(list, dict);
3183 uhp = uhp->uh_prev.ptr;
3184 }
3185 }
3186 #endif