Mercurial > vim
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 |