diff options
| author | Richard M. Stallman | 1994-04-29 23:22:51 +0000 |
|---|---|---|
| committer | Richard M. Stallman | 1994-04-29 23:22:51 +0000 |
| commit | b229b8d187a65116800e56f7ebd35edbecbcf026 (patch) | |
| tree | d13e876b6b75a4ecd1db6f03616c5388bf613285 /src/editfns.c | |
| parent | c15c5d408d696928862ca2848a359231e373556c (diff) | |
| download | emacs-b229b8d187a65116800e56f7ebd35edbecbcf026.tar.gz emacs-b229b8d187a65116800e56f7ebd35edbecbcf026.zip | |
(Ftranspose_regions): New function.
(transpose_markers): Helper func for above.
(syms_of_frame): Call defsubr for Stranspose_regions.
Diffstat (limited to 'src/editfns.c')
| -rw-r--r-- | src/editfns.c | 301 |
1 files changed, 301 insertions, 0 deletions
diff --git a/src/editfns.c b/src/editfns.c index fee9d9e687f..5464c3ac717 100644 --- a/src/editfns.c +++ b/src/editfns.c | |||
| @@ -1609,6 +1609,306 @@ Case is ignored if `case-fold-search' is non-nil in the current buffer.") | |||
| 1609 | return Qt; | 1609 | return Qt; |
| 1610 | return Qnil; | 1610 | return Qnil; |
| 1611 | } | 1611 | } |
| 1612 | |||
| 1613 | /* Transpose the markers in two regions of the current buffer, and | ||
| 1614 | adjust the ones between them if necessary (i.e.: if the regions | ||
| 1615 | differ in size). | ||
| 1616 | |||
| 1617 | Traverses the entire marker list of the buffer to do so, adding an | ||
| 1618 | appropriate amount to some, subtracting from some, and leaving the | ||
| 1619 | rest untouched. Most of this is copied from adjust_markers in insdel.c. | ||
| 1620 | |||
| 1621 | It's caller's job to see that (start1 <= end1 <= start2 <= end2), | ||
| 1622 | and that the buffer gap will not conflict with the markers. This | ||
| 1623 | last requirement is odd and maybe should be taken out, but it works | ||
| 1624 | for now because Ftranspose_regions does in fact guarantee that, in | ||
| 1625 | addition to providing universal health-care coverage. */ | ||
| 1626 | |||
| 1627 | void | ||
| 1628 | transpose_markers (start1, end1, start2, end2) | ||
| 1629 | register int start1, end1, start2, end2; | ||
| 1630 | { | ||
| 1631 | register int amt1, amt2, diff, mpos; | ||
| 1632 | register Lisp_Object marker; | ||
| 1633 | register struct Lisp_Marker *m; | ||
| 1634 | |||
| 1635 | /* Internally, marker positions take the gap into account, so if the | ||
| 1636 | * gap is before one or both of the regions, the region's limits | ||
| 1637 | * must be adjusted to compensate. The caller guaranteed that the | ||
| 1638 | * gap is not inside any of the regions, however, so this is fairly | ||
| 1639 | * simple. | ||
| 1640 | */ | ||
| 1641 | if (GPT < start1) | ||
| 1642 | { | ||
| 1643 | register int gs = GAP_SIZE; | ||
| 1644 | start1 += gs; end1 += gs; | ||
| 1645 | start2 += gs; end2 += gs; | ||
| 1646 | } | ||
| 1647 | else if (GPT < start2) | ||
| 1648 | { | ||
| 1649 | /* If the regions are of equal size, the gap could, in theory, | ||
| 1650 | * be somewhere between them. */ | ||
| 1651 | register int gs = GAP_SIZE; | ||
| 1652 | start2 += gs; end2 += gs; | ||
| 1653 | } | ||
| 1654 | |||
| 1655 | /* The difference between the region's lengths */ | ||
| 1656 | diff = (end2 - start2) - (end1 - start1); | ||
| 1657 | |||
| 1658 | /* For shifting each marker in a region by the length of the other | ||
| 1659 | * region plus the distance between the regions. | ||
| 1660 | */ | ||
| 1661 | amt1 = (end2 - start2) + (start2 - end1); | ||
| 1662 | amt2 = (end1 - start1) + (start2 - end1); | ||
| 1663 | |||
| 1664 | marker = current_buffer->markers; | ||
| 1665 | |||
| 1666 | while (!NILP (marker)) | ||
| 1667 | { | ||
| 1668 | m = XMARKER (marker); | ||
| 1669 | mpos = m->bufpos; | ||
| 1670 | if (mpos >= start1 && mpos < end1) /* in region 1 */ | ||
| 1671 | { | ||
| 1672 | m->bufpos += amt1; | ||
| 1673 | } | ||
| 1674 | else if (mpos >= start2 && mpos < end2) /* in region 2 */ | ||
| 1675 | { | ||
| 1676 | m->bufpos -= amt2; | ||
| 1677 | } | ||
| 1678 | else if (mpos >= end1 && mpos < start2) /* between the regions */ | ||
| 1679 | { | ||
| 1680 | m->bufpos += diff; | ||
| 1681 | } | ||
| 1682 | marker = m->chain; | ||
| 1683 | } | ||
| 1684 | } | ||
| 1685 | |||
| 1686 | DEFUN ("transpose-regions", Ftranspose_regions, Stranspose_regions, 4, 5, 0, | ||
| 1687 | "Transpose region START1 to END1 with START2 to END2.\n\ | ||
| 1688 | The regions may not be overlapping, because the size of the buffer is\n\ | ||
| 1689 | never changed in a transposition.\n\ | ||
| 1690 | \n\ | ||
| 1691 | Optional fifth arg LEAVE_MARKERS, if non-nil, means don't transpose\n\ | ||
| 1692 | any markers that happen to be located in the regions.\n\ | ||
| 1693 | \n\ | ||
| 1694 | Transposing beyond buffer boundaries is an error.") | ||
| 1695 | (startr1, endr1, startr2, endr2, leave_markers) | ||
| 1696 | Lisp_Object startr1, endr1, startr2, endr2, leave_markers; | ||
| 1697 | { | ||
| 1698 | register int start1, end1, start2, end2, | ||
| 1699 | gap, len1, len_mid, len2; | ||
| 1700 | char *start1_addr, *start2_addr, *temp; | ||
| 1701 | |||
| 1702 | #ifdef USE_TEXT_PROPERTIES | ||
| 1703 | INTERVAL cur_intv, tmp_interval1, tmp_interval_mid, tmp_interval2; | ||
| 1704 | cur_intv = current_buffer->intervals; | ||
| 1705 | #endif /* USE_TEXT_PROPERTIES */ | ||
| 1706 | |||
| 1707 | validate_region (&startr1, &endr1); | ||
| 1708 | validate_region (&startr2, &endr2); | ||
| 1709 | |||
| 1710 | start1 = XFASTINT (startr1); | ||
| 1711 | end1 = XFASTINT (endr1); | ||
| 1712 | start2 = XFASTINT (startr2); | ||
| 1713 | end2 = XFASTINT (endr2); | ||
| 1714 | gap = GPT; | ||
| 1715 | |||
| 1716 | /* Swap the regions if they're reversed. */ | ||
| 1717 | if (start2 < end1) | ||
| 1718 | { | ||
| 1719 | register int glumph = start1; | ||
| 1720 | start1 = start2; | ||
| 1721 | start2 = glumph; | ||
| 1722 | glumph = end1; | ||
| 1723 | end1 = end2; | ||
| 1724 | end2 = glumph; | ||
| 1725 | } | ||
| 1726 | |||
| 1727 | start1_addr = BUF_CHAR_ADDRESS (current_buffer, start1); | ||
| 1728 | start2_addr = BUF_CHAR_ADDRESS (current_buffer, start2); | ||
| 1729 | len1 = end1 - start1; | ||
| 1730 | len2 = end2 - start2; | ||
| 1731 | |||
| 1732 | if (start2 < end1) | ||
| 1733 | error ("transposed regions not properly ordered"); | ||
| 1734 | else if (start1 == end1 || start2 == end2) | ||
| 1735 | error ("transposed region may not be of length 0"); | ||
| 1736 | |||
| 1737 | /* The possibilities are: | ||
| 1738 | 1. Adjacent (contiguous) regions, or separate but equal regions | ||
| 1739 | (no, really equal, in this case!), or | ||
| 1740 | 2. Separate regions of unequal size. | ||
| 1741 | |||
| 1742 | The worst case is usually No. 2. It means that (aside from | ||
| 1743 | potential need for getting the gap out of the way), there also | ||
| 1744 | needs to be a shifting of the text between the two regions. So | ||
| 1745 | if they are spread far apart, we are that much slower... sigh. */ | ||
| 1746 | |||
| 1747 | /* It must be pointed out that the really studly thing to do would | ||
| 1748 | be not to move the gap at all, but to leave it in place and work | ||
| 1749 | around it if necessary. This would be extremely efficient, | ||
| 1750 | especially considering that people are likely to do | ||
| 1751 | transpositions near where they are working interactively, which | ||
| 1752 | is exactly where the gap would be found. However, such code | ||
| 1753 | would be much harder to write and to read. So, if you are | ||
| 1754 | reading this comment and are feeling squirrely, by all means have | ||
| 1755 | a go! I just didn't feel like doing it, so I will simply move | ||
| 1756 | the gap the minimum distance to get it out of the way, and then | ||
| 1757 | deal with an unbroken array. */ | ||
| 1758 | |||
| 1759 | /* Hmmm... how about checking to see if the gap is large | ||
| 1760 | enough to use as the temporary storage? That would avoid an | ||
| 1761 | allocation... interesting. Later, don't fool with it now. */ | ||
| 1762 | |||
| 1763 | /* Working without memmove, for portability (sigh), so must be | ||
| 1764 | careful of overlapping subsections of the array... */ | ||
| 1765 | |||
| 1766 | if (end1 == start2) /* adjacent regions */ | ||
| 1767 | { | ||
| 1768 | if ((start1 < gap) && (gap <= end2)) | ||
| 1769 | { | ||
| 1770 | if ((gap - start1) < (end2 - gap) || (end2 == Z)) | ||
| 1771 | move_gap (start1); | ||
| 1772 | else | ||
| 1773 | move_gap (end2 + 1); | ||
| 1774 | } | ||
| 1775 | modify_region (current_buffer, start1, end2); | ||
| 1776 | record_change (start1, len1 + len2); | ||
| 1777 | |||
| 1778 | #ifdef USE_TEXT_PROPERTIES | ||
| 1779 | tmp_interval1 = copy_intervals (cur_intv, start1, len1); | ||
| 1780 | tmp_interval2 = copy_intervals (cur_intv, start2, len2); | ||
| 1781 | Fset_text_properties (start1, end2, Qnil, Qnil); | ||
| 1782 | #endif /* USE_TEXT_PROPERTIES */ | ||
| 1783 | |||
| 1784 | /* First region smaller than second. */ | ||
| 1785 | if (len1 < len2) | ||
| 1786 | { | ||
| 1787 | temp = alloca (len2); | ||
| 1788 | bcopy (start2_addr, temp, len2); | ||
| 1789 | bcopy (start1_addr, start1_addr + len2, len1); | ||
| 1790 | bcopy (temp, start1_addr, len2); | ||
| 1791 | } | ||
| 1792 | else | ||
| 1793 | /* First region not smaller than second. */ | ||
| 1794 | { | ||
| 1795 | temp = alloca (len1); | ||
| 1796 | bcopy (start1_addr, temp, len1); | ||
| 1797 | bcopy (start2_addr, start1_addr, len2); | ||
| 1798 | bcopy (temp, start1_addr + len2, len1); | ||
| 1799 | } | ||
| 1800 | #ifdef USE_TEXT_PROPERTIES | ||
| 1801 | graft_intervals_into_buffer (tmp_interval1, start1 + len2, | ||
| 1802 | len1, current_buffer, 0); | ||
| 1803 | graft_intervals_into_buffer (tmp_interval2, start1, | ||
| 1804 | len2, current_buffer, 0); | ||
| 1805 | #endif /* USE_TEXT_PROPERTIES */ | ||
| 1806 | } | ||
| 1807 | /* Non-adjacent regions, because end1 != start2, bleagh... */ | ||
| 1808 | else | ||
| 1809 | { | ||
| 1810 | if (((start1 < gap) && (gap <= end1)) || (end2 == Z)) | ||
| 1811 | move_gap (start1); | ||
| 1812 | else if ((start2 < gap) && (gap <= end2)) | ||
| 1813 | move_gap (end2 + 1); | ||
| 1814 | |||
| 1815 | if (len1 == len2) | ||
| 1816 | /* Regions are same size, though, how nice. */ | ||
| 1817 | { | ||
| 1818 | modify_region (current_buffer, start1, end1); | ||
| 1819 | modify_region (current_buffer, start2, end2); | ||
| 1820 | record_change (start1, len1); | ||
| 1821 | record_change (start2, len2); | ||
| 1822 | #ifdef USE_TEXT_PROPERTIES | ||
| 1823 | tmp_interval1 = copy_intervals (cur_intv, start1, len1); | ||
| 1824 | tmp_interval2 = copy_intervals (cur_intv, start2, len2); | ||
| 1825 | Fset_text_properties (start1, end1, Qnil, Qnil); | ||
| 1826 | Fset_text_properties (start2, end2, Qnil, Qnil); | ||
| 1827 | #endif /* USE_TEXT_PROPERTIES */ | ||
| 1828 | |||
| 1829 | temp = alloca (len1); | ||
| 1830 | bcopy (start1_addr, temp, len1); | ||
| 1831 | bcopy (start2_addr, start1_addr, len2); | ||
| 1832 | bcopy (temp, start2_addr, len1); | ||
| 1833 | #ifdef USE_TEXT_PROPERTIES | ||
| 1834 | graft_intervals_into_buffer (tmp_interval1, start2, | ||
| 1835 | len1, current_buffer, 0); | ||
| 1836 | graft_intervals_into_buffer (tmp_interval2, start1, | ||
| 1837 | len2, current_buffer, 0); | ||
| 1838 | #endif /* USE_TEXT_PROPERTIES */ | ||
| 1839 | } | ||
| 1840 | |||
| 1841 | else if (len1 < len2) /* Second region larger than first */ | ||
| 1842 | /* Non-adjacent & unequal size, area between must also be shifted. */ | ||
| 1843 | { | ||
| 1844 | len_mid = start2 - end1; | ||
| 1845 | modify_region (current_buffer, start1, end2); | ||
| 1846 | record_change (start1, (end2 - start1)); | ||
| 1847 | #ifdef USE_TEXT_PROPERTIES | ||
| 1848 | tmp_interval1 = copy_intervals (cur_intv, start1, len1); | ||
| 1849 | tmp_interval_mid = copy_intervals (cur_intv, end1, len_mid); | ||
| 1850 | tmp_interval2 = copy_intervals (cur_intv, start2, len2); | ||
| 1851 | Fset_text_properties (start1, end2, Qnil, Qnil); | ||
| 1852 | #endif /* USE_TEXT_PROPERTIES */ | ||
| 1853 | |||
| 1854 | temp = alloca (len_mid + len2); /* holds mid area & region 2 */ | ||
| 1855 | bcopy (start1_addr + len1, temp, len_mid); | ||
| 1856 | bcopy (start2_addr, temp + len_mid, len2); | ||
| 1857 | bcopy (start1_addr, start1_addr + len_mid + len2, len1); | ||
| 1858 | bcopy (temp + len_mid, start1_addr, len2); | ||
| 1859 | bcopy (temp, start1_addr + len2, len_mid); | ||
| 1860 | #ifdef USE_TEXT_PROPERTIES | ||
| 1861 | graft_intervals_into_buffer (tmp_interval1, end2 - len1, | ||
| 1862 | len1, current_buffer, 0); | ||
| 1863 | graft_intervals_into_buffer (tmp_interval_mid, start1 + len2, | ||
| 1864 | len_mid, current_buffer, 0); | ||
| 1865 | graft_intervals_into_buffer (tmp_interval2, start1, | ||
| 1866 | len2, current_buffer, 0); | ||
| 1867 | #endif /* USE_TEXT_PROPERTIES */ | ||
| 1868 | } | ||
| 1869 | else | ||
| 1870 | /* Second region smaller than first. */ | ||
| 1871 | { | ||
| 1872 | len_mid = start2 - end1; | ||
| 1873 | record_change (start1, (end2 - start1)); | ||
| 1874 | modify_region (current_buffer, start1, end2); | ||
| 1875 | |||
| 1876 | #ifdef USE_TEXT_PROPERTIES | ||
| 1877 | tmp_interval1 = copy_intervals (cur_intv, start1, len1); | ||
| 1878 | tmp_interval_mid = copy_intervals (cur_intv, end1, len_mid); | ||
| 1879 | tmp_interval2 = copy_intervals (cur_intv, start2, len2); | ||
| 1880 | Fset_text_properties (start1, end2, Qnil, Qnil); | ||
| 1881 | #endif /* USE_TEXT_PROPERTIES */ | ||
| 1882 | |||
| 1883 | temp = alloca (len_mid + len1); /* holds mid area & region 1 */ | ||
| 1884 | bcopy (start1_addr + len1, temp, len_mid); | ||
| 1885 | bcopy (start1_addr, temp + len_mid, len1); | ||
| 1886 | bcopy (start2_addr, start1_addr, len2); | ||
| 1887 | bcopy (temp + len_mid, start1_addr + len2 + len_mid, len1); | ||
| 1888 | bcopy (temp, start1_addr + len2, len_mid); | ||
| 1889 | #ifdef USE_TEXT_PROPERTIES | ||
| 1890 | graft_intervals_into_buffer (tmp_interval1, end2 - len1, | ||
| 1891 | len1, current_buffer, 0); | ||
| 1892 | graft_intervals_into_buffer (tmp_interval_mid, start1 + len2, | ||
| 1893 | len_mid, current_buffer, 0); | ||
| 1894 | graft_intervals_into_buffer (tmp_interval2, start1, | ||
| 1895 | len2, current_buffer, 0); | ||
| 1896 | #endif /* USE_TEXT_PROPERTIES */ | ||
| 1897 | } | ||
| 1898 | } | ||
| 1899 | |||
| 1900 | /* todo: this will be slow, because for every transposition, we | ||
| 1901 | traverse the whole friggin marker list. Possible solutions: | ||
| 1902 | somehow get a list of *all* the markers across multiple | ||
| 1903 | transpositions and do it all in one swell phoop. Or maybe modify | ||
| 1904 | Emacs' marker code to keep an ordered list or tree. This might | ||
| 1905 | be nicer, and more beneficial in the long run, but would be a | ||
| 1906 | bunch of work. Plus the way they're arranged now is nice. */ | ||
| 1907 | if (NILP (leave_markers)) | ||
| 1908 | transpose_markers (start1, end1, start2, end2); | ||
| 1909 | |||
| 1910 | return Qnil; | ||
| 1911 | } | ||
| 1612 | 1912 | ||
| 1613 | 1913 | ||
| 1614 | void | 1914 | void |
| @@ -1675,4 +1975,5 @@ syms_of_editfns () | |||
| 1675 | defsubr (&Swiden); | 1975 | defsubr (&Swiden); |
| 1676 | defsubr (&Snarrow_to_region); | 1976 | defsubr (&Snarrow_to_region); |
| 1677 | defsubr (&Ssave_restriction); | 1977 | defsubr (&Ssave_restriction); |
| 1978 | defsubr (&Stranspose_regions); | ||
| 1678 | } | 1979 | } |