diff options
| author | Gregory Heytings | 2022-11-27 22:19:41 +0100 |
|---|---|---|
| committer | Gregory Heytings | 2022-11-27 22:19:41 +0100 |
| commit | 849223fba1ef899f90a6edff05bce24b90fbb043 (patch) | |
| tree | b5ab707f2da7d13ba2cb10c8af441547152c83ef /src | |
| parent | 89a10ffcc49c5832619649b7876cc339fa9d0dcf (diff) | |
| parent | 18fa159fa91b515f2281b83648961fdc5e21aca7 (diff) | |
| download | emacs-849223fba1ef899f90a6edff05bce24b90fbb043.tar.gz emacs-849223fba1ef899f90a6edff05bce24b90fbb043.zip | |
Merge branch 'feature/improved-locked-narrowing'
Diffstat (limited to 'src')
| -rw-r--r-- | src/buffer.c | 37 | ||||
| -rw-r--r-- | src/dispextern.h | 10 | ||||
| -rw-r--r-- | src/editfns.c | 373 | ||||
| -rw-r--r-- | src/keyboard.c | 16 | ||||
| -rw-r--r-- | src/lisp.h | 3 | ||||
| -rw-r--r-- | src/xdisp.c | 59 |
6 files changed, 407 insertions, 91 deletions
diff --git a/src/buffer.c b/src/buffer.c index ac7f4f8e9d4..71be7ed9e13 100644 --- a/src/buffer.c +++ b/src/buffer.c | |||
| @@ -5898,7 +5898,42 @@ this threshold. | |||
| 5898 | If nil, these display shortcuts will always remain disabled. | 5898 | If nil, these display shortcuts will always remain disabled. |
| 5899 | 5899 | ||
| 5900 | There is no reason to change that value except for debugging purposes. */); | 5900 | There is no reason to change that value except for debugging purposes. */); |
| 5901 | XSETFASTINT (Vlong_line_threshold, 10000); | 5901 | XSETFASTINT (Vlong_line_threshold, 50000); |
| 5902 | |||
| 5903 | DEFVAR_INT ("long-line-locked-narrowing-region-size", | ||
| 5904 | long_line_locked_narrowing_region_size, | ||
| 5905 | doc: /* Region size for locked narrowing in buffers with long lines. | ||
| 5906 | |||
| 5907 | This variable has effect only in buffers which contain one or more | ||
| 5908 | lines whose length is above `long-line-threshold', which see. For | ||
| 5909 | performance reasons, in such buffers, low-level hooks such as | ||
| 5910 | `fontification-functions' or `post-command-hook' are executed on a | ||
| 5911 | narrowed buffer, with a narrowing locked with `narrowing-lock'. This | ||
| 5912 | variable specifies the size of the narrowed region around point. | ||
| 5913 | |||
| 5914 | To disable that narrowing, set this variable to 0. | ||
| 5915 | |||
| 5916 | See also `long-line-locked-narrowing-bol-search-limit'. | ||
| 5917 | |||
| 5918 | There is no reason to change that value except for debugging purposes. */); | ||
| 5919 | long_line_locked_narrowing_region_size = 500000; | ||
| 5920 | |||
| 5921 | DEFVAR_INT ("long-line-locked-narrowing-bol-search-limit", | ||
| 5922 | long_line_locked_narrowing_bol_search_limit, | ||
| 5923 | doc: /* Limit for beginning of line search in buffers with long lines. | ||
| 5924 | |||
| 5925 | This variable has effect only in buffers which contain one or more | ||
| 5926 | lines whose length is above `long-line-threshold', which see. For | ||
| 5927 | performance reasons, in such buffers, low-level hooks such as | ||
| 5928 | `fontification-functions' or `post-command-hook' are executed on a | ||
| 5929 | narrowed buffer, with a narrowing locked with `narrowing-lock'. The | ||
| 5930 | variable `long-line-locked-narrowing-region-size' specifies the size | ||
| 5931 | of the narrowed region around point. This variable, which should be a | ||
| 5932 | small integer, specifies the number of characters by which that region | ||
| 5933 | can be extended backwards to make it start at the beginning of a line. | ||
| 5934 | |||
| 5935 | There is no reason to change that value except for debugging purposes. */); | ||
| 5936 | long_line_locked_narrowing_bol_search_limit = 128; | ||
| 5902 | 5937 | ||
| 5903 | DEFVAR_INT ("large-hscroll-threshold", large_hscroll_threshold, | 5938 | DEFVAR_INT ("large-hscroll-threshold", large_hscroll_threshold, |
| 5904 | doc: /* Horizontal scroll of truncated lines above which to use redisplay shortcuts. | 5939 | doc: /* Horizontal scroll of truncated lines above which to use redisplay shortcuts. |
diff --git a/src/dispextern.h b/src/dispextern.h index 2afbdeabaab..df6134e68f0 100644 --- a/src/dispextern.h +++ b/src/dispextern.h | |||
| @@ -2342,6 +2342,14 @@ struct it | |||
| 2342 | optimize display. */ | 2342 | optimize display. */ |
| 2343 | ptrdiff_t narrowed_zv; | 2343 | ptrdiff_t narrowed_zv; |
| 2344 | 2344 | ||
| 2345 | /* Begin position of the buffer for the locked narrowing around | ||
| 2346 | low-level hooks. */ | ||
| 2347 | ptrdiff_t locked_narrowing_begv; | ||
| 2348 | |||
| 2349 | /* End position of the buffer for the locked narrowing around | ||
| 2350 | low-level hooks. */ | ||
| 2351 | ptrdiff_t locked_narrowing_zv; | ||
| 2352 | |||
| 2345 | /* C string to iterate over. Non-null means get characters from | 2353 | /* C string to iterate over. Non-null means get characters from |
| 2346 | this string, otherwise characters are read from current_buffer | 2354 | this string, otherwise characters are read from current_buffer |
| 2347 | or it->string. */ | 2355 | or it->string. */ |
| @@ -3405,6 +3413,8 @@ void init_iterator (struct it *, struct window *, ptrdiff_t, | |||
| 3405 | ptrdiff_t get_narrowed_begv (struct window *, ptrdiff_t); | 3413 | ptrdiff_t get_narrowed_begv (struct window *, ptrdiff_t); |
| 3406 | ptrdiff_t get_narrowed_zv (struct window *, ptrdiff_t); | 3414 | ptrdiff_t get_narrowed_zv (struct window *, ptrdiff_t); |
| 3407 | ptrdiff_t get_closer_narrowed_begv (struct window *, ptrdiff_t); | 3415 | ptrdiff_t get_closer_narrowed_begv (struct window *, ptrdiff_t); |
| 3416 | ptrdiff_t get_locked_narrowing_begv (ptrdiff_t); | ||
| 3417 | ptrdiff_t get_locked_narrowing_zv (ptrdiff_t); | ||
| 3408 | void init_iterator_to_row_start (struct it *, struct window *, | 3418 | void init_iterator_to_row_start (struct it *, struct window *, |
| 3409 | struct glyph_row *); | 3419 | struct glyph_row *); |
| 3410 | void start_display (struct it *, struct window *, struct text_pos); | 3420 | void start_display (struct it *, struct window *, struct text_pos); |
diff --git a/src/editfns.c b/src/editfns.c index 17dca4708ed..b364f441b53 100644 --- a/src/editfns.c +++ b/src/editfns.c | |||
| @@ -2653,88 +2653,216 @@ DEFUN ("delete-and-extract-region", Fdelete_and_extract_region, | |||
| 2653 | return del_range_1 (XFIXNUM (start), XFIXNUM (end), 1, 1); | 2653 | return del_range_1 (XFIXNUM (start), XFIXNUM (end), 1, 1); |
| 2654 | } | 2654 | } |
| 2655 | 2655 | ||
| 2656 | DEFUN ("widen", Fwiden, Swiden, 0, 0, "", | 2656 | /* Alist of buffers in which locked narrowing is used. The car of |
| 2657 | doc: /* Remove restrictions (narrowing) from current buffer. | 2657 | each list element is a buffer, the cdr is a list of triplets (tag |
| 2658 | This allows the buffer's full text to be seen and edited. | 2658 | begv-marker zv-marker). The last element of that list always uses |
| 2659 | the (uninterned) Qoutermost_narrowing tag and records the narrowing | ||
| 2660 | bounds that were set by the user and that are visible on display. | ||
| 2661 | This alist is used internally by narrow-to-region, widen, | ||
| 2662 | narrowing-lock, narrowing-unlock and save-restriction. */ | ||
| 2663 | static Lisp_Object narrowing_locks; | ||
| 2664 | |||
| 2665 | /* Add BUF with its LOCKS in the narrowing_locks alist. */ | ||
| 2666 | static void | ||
| 2667 | narrowing_locks_add (Lisp_Object buf, Lisp_Object locks) | ||
| 2668 | { | ||
| 2669 | narrowing_locks = nconc2 (list1 (list2 (buf, locks)), narrowing_locks); | ||
| 2670 | } | ||
| 2659 | 2671 | ||
| 2660 | Note that, when the current buffer contains one or more lines whose | 2672 | /* Remove BUF and its locks from the narrowing_locks alist. Do |
| 2661 | length is above `long-line-threshold', Emacs may decide to leave, for | 2673 | nothing if BUF is not present in narrowing_locks. */ |
| 2662 | performance reasons, the accessible portion of the buffer unchanged | 2674 | static void |
| 2663 | after this function is called from low-level hooks, such as | 2675 | narrowing_locks_remove (Lisp_Object buf) |
| 2664 | `jit-lock-functions' or `post-command-hook'. */) | 2676 | { |
| 2665 | (void) | 2677 | narrowing_locks = Fdelq (Fassoc (buf, narrowing_locks, Qnil), |
| 2678 | narrowing_locks); | ||
| 2679 | } | ||
| 2680 | |||
| 2681 | /* Retrieve one of the BEGV/ZV bounds of a narrowing in BUF from the | ||
| 2682 | narrowing_locks alist, as a pointer to a struct Lisp_Marker, or | ||
| 2683 | NULL if BUF is not in narrowing_locks or is a killed buffer. When | ||
| 2684 | OUTERMOST is true, the bounds that were set by the user and that | ||
| 2685 | are visible on display are returned. Otherwise the innermost | ||
| 2686 | locked narrowing bounds are returned. */ | ||
| 2687 | static struct Lisp_Marker * | ||
| 2688 | narrowing_lock_get_bound (Lisp_Object buf, bool begv, bool outermost) | ||
| 2689 | { | ||
| 2690 | if (NILP (Fbuffer_live_p (buf))) | ||
| 2691 | return NULL; | ||
| 2692 | Lisp_Object buffer_locks = assq_no_quit (buf, narrowing_locks); | ||
| 2693 | if (NILP (buffer_locks)) | ||
| 2694 | return NULL; | ||
| 2695 | buffer_locks = XCAR (XCDR (buffer_locks)); | ||
| 2696 | Lisp_Object bounds | ||
| 2697 | = outermost | ||
| 2698 | ? XCDR (assq_no_quit (Qoutermost_narrowing, buffer_locks)) | ||
| 2699 | : XCDR (XCAR (buffer_locks)); | ||
| 2700 | eassert (! NILP (bounds)); | ||
| 2701 | Lisp_Object marker = begv ? XCAR (bounds) : XCAR (XCDR (bounds)); | ||
| 2702 | eassert (EQ (Fmarker_buffer (marker), buf)); | ||
| 2703 | return XMARKER (marker); | ||
| 2704 | } | ||
| 2705 | |||
| 2706 | /* Retrieve the tag of the innermost narrowing in BUF. Return nil if | ||
| 2707 | BUF is not in narrowing_locks or is a killed buffer. */ | ||
| 2708 | static Lisp_Object | ||
| 2709 | narrowing_lock_peek_tag (Lisp_Object buf) | ||
| 2666 | { | 2710 | { |
| 2667 | if (! NILP (Vrestrictions_locked)) | 2711 | if (NILP (Fbuffer_live_p (buf))) |
| 2668 | return Qnil; | 2712 | return Qnil; |
| 2669 | if (BEG != BEGV || Z != ZV) | 2713 | Lisp_Object buffer_locks = assq_no_quit (buf, narrowing_locks); |
| 2670 | current_buffer->clip_changed = 1; | 2714 | if (NILP (buffer_locks)) |
| 2671 | BEGV = BEG; | 2715 | return Qnil; |
| 2672 | BEGV_BYTE = BEG_BYTE; | 2716 | Lisp_Object tag = XCAR (XCAR (XCAR (XCDR (buffer_locks)))); |
| 2673 | SET_BUF_ZV_BOTH (current_buffer, Z, Z_BYTE); | 2717 | eassert (! NILP (tag)); |
| 2674 | /* Changing the buffer bounds invalidates any recorded current column. */ | 2718 | return tag; |
| 2675 | invalidate_current_column (); | ||
| 2676 | return Qnil; | ||
| 2677 | } | 2719 | } |
| 2678 | 2720 | ||
| 2721 | /* Add a LOCK for BUF in the narrowing_locks alist. */ | ||
| 2679 | static void | 2722 | static void |
| 2680 | unwind_locked_begv (Lisp_Object point_min) | 2723 | narrowing_lock_push (Lisp_Object buf, Lisp_Object lock) |
| 2681 | { | 2724 | { |
| 2682 | SET_BUF_BEGV (current_buffer, XFIXNUM (point_min)); | 2725 | Lisp_Object buffer_locks = assq_no_quit (buf, narrowing_locks); |
| 2726 | if (NILP (buffer_locks)) | ||
| 2727 | narrowing_locks_add (buf, list1 (lock)); | ||
| 2728 | else | ||
| 2729 | XSETCDR (buffer_locks, list1 (nconc2 (list1 (lock), | ||
| 2730 | XCAR (XCDR (buffer_locks))))); | ||
| 2683 | } | 2731 | } |
| 2684 | 2732 | ||
| 2733 | /* Remove the innermost lock in BUF from the narrowing_locks alist. | ||
| 2734 | Do nothing if BUF is not present in narrowing_locks. */ | ||
| 2685 | static void | 2735 | static void |
| 2686 | unwind_locked_zv (Lisp_Object point_max) | 2736 | narrowing_lock_pop (Lisp_Object buf) |
| 2687 | { | 2737 | { |
| 2688 | SET_BUF_ZV (current_buffer, XFIXNUM (point_max)); | 2738 | Lisp_Object buffer_locks = assq_no_quit (buf, narrowing_locks); |
| 2739 | if (NILP (buffer_locks)) | ||
| 2740 | return; | ||
| 2741 | if (EQ (narrowing_lock_peek_tag (buf), Qoutermost_narrowing)) | ||
| 2742 | narrowing_locks_remove (buf); | ||
| 2743 | else | ||
| 2744 | XSETCDR (buffer_locks, list1 (XCDR (XCAR (XCDR (buffer_locks))))); | ||
| 2689 | } | 2745 | } |
| 2690 | 2746 | ||
| 2691 | /* Internal function for Fnarrow_to_region, meant to be used with a | 2747 | static void |
| 2692 | third argument 'true', in which case it should be followed by "specbind | 2748 | unwind_reset_outermost_narrowing (Lisp_Object buf) |
| 2693 | (Qrestrictions_locked, Qt)". */ | ||
| 2694 | Lisp_Object | ||
| 2695 | narrow_to_region_internal (Lisp_Object start, Lisp_Object end, bool lock) | ||
| 2696 | { | 2749 | { |
| 2697 | EMACS_INT s = fix_position (start), e = fix_position (end); | 2750 | struct Lisp_Marker *begv = narrowing_lock_get_bound (buf, true, false); |
| 2698 | 2751 | struct Lisp_Marker *zv = narrowing_lock_get_bound (buf, false, false); | |
| 2699 | if (e < s) | 2752 | if (begv != NULL && zv != NULL) |
| 2700 | { | 2753 | { |
| 2701 | EMACS_INT tem = s; s = e; e = tem; | 2754 | SET_BUF_BEGV_BOTH (XBUFFER (buf), begv->charpos, begv->bytepos); |
| 2755 | SET_BUF_ZV_BOTH (XBUFFER (buf), zv->charpos, zv->bytepos); | ||
| 2702 | } | 2756 | } |
| 2757 | else | ||
| 2758 | narrowing_locks_remove (buf); | ||
| 2759 | } | ||
| 2703 | 2760 | ||
| 2704 | if (lock) | 2761 | /* Restore the narrowing bounds that were set by the user, and restore |
| 2762 | the bounds of the locked narrowing upon return. | ||
| 2763 | In particular, this function is called when redisplay starts, so | ||
| 2764 | that if a Lisp function executed during redisplay calls (redisplay) | ||
| 2765 | while a locked narrowing is in effect, the locked narrowing will | ||
| 2766 | not be visible on display. */ | ||
| 2767 | void | ||
| 2768 | reset_outermost_narrowings (void) | ||
| 2769 | { | ||
| 2770 | Lisp_Object val, buf; | ||
| 2771 | for (val = narrowing_locks; CONSP (val); val = XCDR (val)) | ||
| 2705 | { | 2772 | { |
| 2706 | if (!(BEGV <= s && s <= e && e <= ZV)) | 2773 | buf = XCAR (XCAR (val)); |
| 2707 | args_out_of_range (start, end); | 2774 | eassert (BUFFERP (buf)); |
| 2775 | struct Lisp_Marker *begv = narrowing_lock_get_bound (buf, true, true); | ||
| 2776 | struct Lisp_Marker *zv = narrowing_lock_get_bound (buf, false, true); | ||
| 2777 | if (begv != NULL && zv != NULL) | ||
| 2778 | { | ||
| 2779 | SET_BUF_BEGV_BOTH (XBUFFER (buf), begv->charpos, begv->bytepos); | ||
| 2780 | SET_BUF_ZV_BOTH (XBUFFER (buf), zv->charpos, zv->bytepos); | ||
| 2781 | record_unwind_protect (unwind_reset_outermost_narrowing, buf); | ||
| 2782 | } | ||
| 2783 | else | ||
| 2784 | narrowing_locks_remove (buf); | ||
| 2785 | } | ||
| 2786 | } | ||
| 2708 | 2787 | ||
| 2709 | if (BEGV != s || ZV != e) | 2788 | /* Helper functions to save and restore the narrowing locks of the |
| 2710 | current_buffer->clip_changed = 1; | 2789 | current buffer in Fsave_restriction. */ |
| 2790 | static Lisp_Object | ||
| 2791 | narrowing_locks_save (void) | ||
| 2792 | { | ||
| 2793 | Lisp_Object buf = Fcurrent_buffer (); | ||
| 2794 | Lisp_Object locks = assq_no_quit (buf, narrowing_locks); | ||
| 2795 | if (NILP (locks)) | ||
| 2796 | return Qnil; | ||
| 2797 | locks = XCAR (XCDR (locks)); | ||
| 2798 | return Fcons (buf, Fcopy_sequence (locks)); | ||
| 2799 | } | ||
| 2711 | 2800 | ||
| 2712 | record_unwind_protect (restore_point_unwind, Fpoint_marker ()); | 2801 | static void |
| 2713 | record_unwind_protect (unwind_locked_begv, Fpoint_min ()); | 2802 | narrowing_locks_restore (Lisp_Object buf_and_saved_locks) |
| 2714 | record_unwind_protect (unwind_locked_zv, Fpoint_max ()); | 2803 | { |
| 2804 | if (NILP (buf_and_saved_locks)) | ||
| 2805 | return; | ||
| 2806 | Lisp_Object buf = XCAR (buf_and_saved_locks); | ||
| 2807 | Lisp_Object saved_locks = XCDR (buf_and_saved_locks); | ||
| 2808 | narrowing_locks_remove (buf); | ||
| 2809 | narrowing_locks_add (buf, saved_locks); | ||
| 2810 | } | ||
| 2715 | 2811 | ||
| 2716 | SET_BUF_BEGV (current_buffer, s); | 2812 | static void |
| 2717 | SET_BUF_ZV (current_buffer, e); | 2813 | unwind_narrow_to_region_locked (Lisp_Object tag) |
| 2814 | { | ||
| 2815 | Fnarrowing_unlock (tag); | ||
| 2816 | Fwiden (); | ||
| 2817 | } | ||
| 2818 | |||
| 2819 | /* Narrow current_buffer to BEGV-ZV with a narrowing locked with TAG. */ | ||
| 2820 | void | ||
| 2821 | narrow_to_region_locked (Lisp_Object begv, Lisp_Object zv, Lisp_Object tag) | ||
| 2822 | { | ||
| 2823 | Fnarrow_to_region (begv, zv); | ||
| 2824 | Fnarrowing_lock (tag); | ||
| 2825 | record_unwind_protect (restore_point_unwind, Fpoint_marker ()); | ||
| 2826 | record_unwind_protect (unwind_narrow_to_region_locked, tag); | ||
| 2827 | } | ||
| 2828 | |||
| 2829 | DEFUN ("widen", Fwiden, Swiden, 0, 0, "", | ||
| 2830 | doc: /* Remove restrictions (narrowing) from current buffer. | ||
| 2831 | |||
| 2832 | This allows the buffer's full text to be seen and edited, unless | ||
| 2833 | restrictions have been locked with `narrowing-lock', which see, in | ||
| 2834 | which case the narrowing that was current when `narrowing-lock' was | ||
| 2835 | called is restored. */) | ||
| 2836 | (void) | ||
| 2837 | { | ||
| 2838 | Fset (Qoutermost_narrowing, Qnil); | ||
| 2839 | Lisp_Object buf = Fcurrent_buffer (); | ||
| 2840 | Lisp_Object tag = narrowing_lock_peek_tag (buf); | ||
| 2841 | |||
| 2842 | if (NILP (tag)) | ||
| 2843 | { | ||
| 2844 | if (BEG != BEGV || Z != ZV) | ||
| 2845 | current_buffer->clip_changed = 1; | ||
| 2846 | BEGV = BEG; | ||
| 2847 | BEGV_BYTE = BEG_BYTE; | ||
| 2848 | SET_BUF_ZV_BOTH (current_buffer, Z, Z_BYTE); | ||
| 2718 | } | 2849 | } |
| 2719 | else | 2850 | else |
| 2720 | { | 2851 | { |
| 2721 | if (! NILP (Vrestrictions_locked)) | 2852 | struct Lisp_Marker *begv = narrowing_lock_get_bound (buf, true, false); |
| 2722 | return Qnil; | 2853 | struct Lisp_Marker *zv = narrowing_lock_get_bound (buf, false, false); |
| 2723 | 2854 | eassert (begv != NULL && zv != NULL); | |
| 2724 | if (!(BEG <= s && s <= e && e <= Z)) | 2855 | if (begv->charpos != BEGV || zv->charpos != ZV) |
| 2725 | args_out_of_range (start, end); | ||
| 2726 | |||
| 2727 | if (BEGV != s || ZV != e) | ||
| 2728 | current_buffer->clip_changed = 1; | 2856 | current_buffer->clip_changed = 1; |
| 2729 | 2857 | SET_BUF_BEGV_BOTH (current_buffer, begv->charpos, begv->bytepos); | |
| 2730 | SET_BUF_BEGV (current_buffer, s); | 2858 | SET_BUF_ZV_BOTH (current_buffer, zv->charpos, zv->bytepos); |
| 2731 | SET_BUF_ZV (current_buffer, e); | 2859 | /* If the only remaining bounds in narrowing_locks for |
| 2860 | current_buffer are the bounds that were set by the user, no | ||
| 2861 | locked narrowing is in effect in current_buffer anymore: | ||
| 2862 | remove it from the narrowing_locks alist. */ | ||
| 2863 | if (EQ (tag, Qoutermost_narrowing)) | ||
| 2864 | narrowing_lock_pop (buf); | ||
| 2732 | } | 2865 | } |
| 2733 | |||
| 2734 | if (PT < s) | ||
| 2735 | SET_PT (s); | ||
| 2736 | if (e < PT) | ||
| 2737 | SET_PT (e); | ||
| 2738 | /* Changing the buffer bounds invalidates any recorded current column. */ | 2866 | /* Changing the buffer bounds invalidates any recorded current column. */ |
| 2739 | invalidate_current_column (); | 2867 | invalidate_current_column (); |
| 2740 | return Qnil; | 2868 | return Qnil; |
| @@ -2751,14 +2879,110 @@ When calling from Lisp, pass two arguments START and END: | |||
| 2751 | positions (integers or markers) bounding the text that should | 2879 | positions (integers or markers) bounding the text that should |
| 2752 | remain visible. | 2880 | remain visible. |
| 2753 | 2881 | ||
| 2754 | Note that, when the current buffer contains one or more lines whose | 2882 | When restrictions have been locked with `narrowing-lock', which see, |
| 2755 | length is above `long-line-threshold', Emacs may decide to leave, for | 2883 | `narrow-to-region' can be used only within the limits of the |
| 2756 | performance reasons, the accessible portion of the buffer unchanged | 2884 | restrictions that were current when `narrowing-lock' was called. If |
| 2757 | after this function is called from low-level hooks, such as | 2885 | the START or END arguments are outside these limits, the corresponding |
| 2758 | `jit-lock-functions' or `post-command-hook'. */) | 2886 | limit of the locked restriction is used instead of the argument. */) |
| 2759 | (Lisp_Object start, Lisp_Object end) | 2887 | (Lisp_Object start, Lisp_Object end) |
| 2760 | { | 2888 | { |
| 2761 | return narrow_to_region_internal (start, end, false); | 2889 | EMACS_INT s = fix_position (start), e = fix_position (end); |
| 2890 | |||
| 2891 | if (e < s) | ||
| 2892 | { | ||
| 2893 | EMACS_INT tem = s; s = e; e = tem; | ||
| 2894 | } | ||
| 2895 | |||
| 2896 | if (!(BEG <= s && s <= e && e <= Z)) | ||
| 2897 | args_out_of_range (start, end); | ||
| 2898 | |||
| 2899 | Lisp_Object buf = Fcurrent_buffer (); | ||
| 2900 | if (! NILP (narrowing_lock_peek_tag (buf))) | ||
| 2901 | { | ||
| 2902 | struct Lisp_Marker *begv = narrowing_lock_get_bound (buf, true, false); | ||
| 2903 | struct Lisp_Marker *zv = narrowing_lock_get_bound (buf, false, false); | ||
| 2904 | eassert (begv != NULL && zv != NULL); | ||
| 2905 | /* Limit the start and end positions to those of the locked | ||
| 2906 | narrowing. */ | ||
| 2907 | if (s < begv->charpos) s = begv->charpos; | ||
| 2908 | if (s > zv->charpos) s = zv->charpos; | ||
| 2909 | if (e < begv->charpos) e = begv->charpos; | ||
| 2910 | if (e > zv->charpos) e = zv->charpos; | ||
| 2911 | } | ||
| 2912 | |||
| 2913 | /* Record the accessible range of the buffer when narrow-to-region | ||
| 2914 | is called, that is, before applying the narrowing. It is used | ||
| 2915 | only by narrowing-lock. */ | ||
| 2916 | Fset (Qoutermost_narrowing, list3 (Qoutermost_narrowing, | ||
| 2917 | Fpoint_min_marker (), | ||
| 2918 | Fpoint_max_marker ())); | ||
| 2919 | |||
| 2920 | if (BEGV != s || ZV != e) | ||
| 2921 | current_buffer->clip_changed = 1; | ||
| 2922 | |||
| 2923 | SET_BUF_BEGV (current_buffer, s); | ||
| 2924 | SET_BUF_ZV (current_buffer, e); | ||
| 2925 | |||
| 2926 | if (PT < s) | ||
| 2927 | SET_PT (s); | ||
| 2928 | if (e < PT) | ||
| 2929 | SET_PT (e); | ||
| 2930 | /* Changing the buffer bounds invalidates any recorded current column. */ | ||
| 2931 | invalidate_current_column (); | ||
| 2932 | return Qnil; | ||
| 2933 | } | ||
| 2934 | |||
| 2935 | DEFUN ("narrowing-lock", Fnarrowing_lock, Snarrowing_lock, 1, 1, 0, | ||
| 2936 | doc: /* Lock the current narrowing with TAG. | ||
| 2937 | |||
| 2938 | When restrictions are locked, `narrow-to-region' and `widen' can be | ||
| 2939 | used only within the limits of the restrictions that were current when | ||
| 2940 | `narrowing-lock' was called, unless the lock is removed by calling | ||
| 2941 | `narrowing-unlock' with TAG. | ||
| 2942 | |||
| 2943 | Locking restrictions should be used sparingly, after carefully | ||
| 2944 | considering the potential adverse effects on the code that will be | ||
| 2945 | executed within locked restrictions. It is typically meant to be used | ||
| 2946 | around portions of code that would become too slow, and make Emacs | ||
| 2947 | unresponsive, if they were executed in a large buffer. For example, | ||
| 2948 | restrictions are locked by Emacs around low-level hooks such as | ||
| 2949 | `fontification-functions' or `post-command-hook'. | ||
| 2950 | |||
| 2951 | Locked restrictions are never visible on display, and can therefore | ||
| 2952 | not be used as a stronger variant of normal restrictions. */) | ||
| 2953 | (Lisp_Object tag) | ||
| 2954 | { | ||
| 2955 | Lisp_Object buf = Fcurrent_buffer (); | ||
| 2956 | Lisp_Object outermost_narrowing | ||
| 2957 | = buffer_local_value (Qoutermost_narrowing, buf); | ||
| 2958 | /* If narrowing-lock is called without being preceded by | ||
| 2959 | narrow-to-region, do nothing. */ | ||
| 2960 | if (NILP (outermost_narrowing)) | ||
| 2961 | return Qnil; | ||
| 2962 | if (NILP (narrowing_lock_peek_tag (buf))) | ||
| 2963 | narrowing_lock_push (buf, outermost_narrowing); | ||
| 2964 | narrowing_lock_push (buf, list3 (tag, | ||
| 2965 | Fpoint_min_marker (), | ||
| 2966 | Fpoint_max_marker ())); | ||
| 2967 | return Qnil; | ||
| 2968 | } | ||
| 2969 | |||
| 2970 | DEFUN ("narrowing-unlock", Fnarrowing_unlock, Snarrowing_unlock, 1, 1, 0, | ||
| 2971 | doc: /* Unlock a narrowing locked with (narrowing-lock TAG). | ||
| 2972 | |||
| 2973 | Unlocking restrictions locked with `narrowing-lock' should be used | ||
| 2974 | sparingly, after carefully considering the reasons why restrictions | ||
| 2975 | were locked. Restrictions are typically locked around portions of | ||
| 2976 | code that would become too slow, and make Emacs unresponsive, if they | ||
| 2977 | were executed in a large buffer. For example, restrictions are locked | ||
| 2978 | by Emacs around low-level hooks such as `fontification-functions' or | ||
| 2979 | `post-command-hook'. */) | ||
| 2980 | (Lisp_Object tag) | ||
| 2981 | { | ||
| 2982 | Lisp_Object buf = Fcurrent_buffer (); | ||
| 2983 | if (EQ (narrowing_lock_peek_tag (buf), tag)) | ||
| 2984 | narrowing_lock_pop (buf); | ||
| 2985 | return Qnil; | ||
| 2762 | } | 2986 | } |
| 2763 | 2987 | ||
| 2764 | Lisp_Object | 2988 | Lisp_Object |
| @@ -2858,11 +3082,12 @@ DEFUN ("save-restriction", Fsave_restriction, Ssave_restriction, 0, UNEVALLED, 0 | |||
| 2858 | doc: /* Execute BODY, saving and restoring current buffer's restrictions. | 3082 | doc: /* Execute BODY, saving and restoring current buffer's restrictions. |
| 2859 | The buffer's restrictions make parts of the beginning and end invisible. | 3083 | The buffer's restrictions make parts of the beginning and end invisible. |
| 2860 | \(They are set up with `narrow-to-region' and eliminated with `widen'.) | 3084 | \(They are set up with `narrow-to-region' and eliminated with `widen'.) |
| 2861 | This special form, `save-restriction', saves the current buffer's restrictions | 3085 | This special form, `save-restriction', saves the current buffer's |
| 2862 | when it is entered, and restores them when it is exited. | 3086 | restrictions, as well as their locks if they have been locked with |
| 3087 | `narrowing-lock', when it is entered, and restores them when it is exited. | ||
| 2863 | So any `narrow-to-region' within BODY lasts only until the end of the form. | 3088 | So any `narrow-to-region' within BODY lasts only until the end of the form. |
| 2864 | The old restrictions settings are restored | 3089 | The old restrictions settings are restored even in case of abnormal exit |
| 2865 | even in case of abnormal exit (throw or error). | 3090 | \(throw or error). |
| 2866 | 3091 | ||
| 2867 | The value returned is the value of the last form in BODY. | 3092 | The value returned is the value of the last form in BODY. |
| 2868 | 3093 | ||
| @@ -2877,6 +3102,7 @@ usage: (save-restriction &rest BODY) */) | |||
| 2877 | specpdl_ref count = SPECPDL_INDEX (); | 3102 | specpdl_ref count = SPECPDL_INDEX (); |
| 2878 | 3103 | ||
| 2879 | record_unwind_protect (save_restriction_restore, save_restriction_save ()); | 3104 | record_unwind_protect (save_restriction_restore, save_restriction_save ()); |
| 3105 | record_unwind_protect (narrowing_locks_restore, narrowing_locks_save ()); | ||
| 2880 | val = Fprogn (body); | 3106 | val = Fprogn (body); |
| 2881 | return unbind_to (count, val); | 3107 | return unbind_to (count, val); |
| 2882 | } | 3108 | } |
| @@ -4518,6 +4744,8 @@ syms_of_editfns (void) | |||
| 4518 | DEFSYM (Qwall, "wall"); | 4744 | DEFSYM (Qwall, "wall"); |
| 4519 | DEFSYM (Qpropertize, "propertize"); | 4745 | DEFSYM (Qpropertize, "propertize"); |
| 4520 | 4746 | ||
| 4747 | staticpro (&narrowing_locks); | ||
| 4748 | |||
| 4521 | DEFVAR_LISP ("inhibit-field-text-motion", Vinhibit_field_text_motion, | 4749 | DEFVAR_LISP ("inhibit-field-text-motion", Vinhibit_field_text_motion, |
| 4522 | doc: /* Non-nil means text motion commands don't notice fields. */); | 4750 | doc: /* Non-nil means text motion commands don't notice fields. */); |
| 4523 | Vinhibit_field_text_motion = Qnil; | 4751 | Vinhibit_field_text_motion = Qnil; |
| @@ -4577,11 +4805,12 @@ This variable is experimental; email 32252@debbugs.gnu.org if you need | |||
| 4577 | it to be non-nil. */); | 4805 | it to be non-nil. */); |
| 4578 | binary_as_unsigned = false; | 4806 | binary_as_unsigned = false; |
| 4579 | 4807 | ||
| 4580 | DEFSYM (Qrestrictions_locked, "restrictions-locked"); | 4808 | DEFVAR_LISP ("outermost-narrowing", Voutermost_narrowing, |
| 4581 | DEFVAR_LISP ("restrictions-locked", Vrestrictions_locked, | 4809 | doc: /* Outermost narrowing bounds, if any. Internal use only. */); |
| 4582 | doc: /* If non-nil, restrictions are currently locked. */); | 4810 | Voutermost_narrowing = Qnil; |
| 4583 | Vrestrictions_locked = Qnil; | 4811 | Fmake_variable_buffer_local (Qoutermost_narrowing); |
| 4584 | Funintern (Qrestrictions_locked, Qnil); | 4812 | DEFSYM (Qoutermost_narrowing, "outermost-narrowing"); |
| 4813 | Funintern (Qoutermost_narrowing, Qnil); | ||
| 4585 | 4814 | ||
| 4586 | defsubr (&Spropertize); | 4815 | defsubr (&Spropertize); |
| 4587 | defsubr (&Schar_equal); | 4816 | defsubr (&Schar_equal); |
| @@ -4674,6 +4903,8 @@ it to be non-nil. */); | |||
| 4674 | defsubr (&Sdelete_and_extract_region); | 4903 | defsubr (&Sdelete_and_extract_region); |
| 4675 | defsubr (&Swiden); | 4904 | defsubr (&Swiden); |
| 4676 | defsubr (&Snarrow_to_region); | 4905 | defsubr (&Snarrow_to_region); |
| 4906 | defsubr (&Snarrowing_lock); | ||
| 4907 | defsubr (&Snarrowing_unlock); | ||
| 4677 | defsubr (&Ssave_restriction); | 4908 | defsubr (&Ssave_restriction); |
| 4678 | defsubr (&Stranspose_regions); | 4909 | defsubr (&Stranspose_regions); |
| 4679 | } | 4910 | } |
diff --git a/src/keyboard.c b/src/keyboard.c index 811998823cc..b82a5e1a3ef 100644 --- a/src/keyboard.c +++ b/src/keyboard.c | |||
| @@ -1911,9 +1911,9 @@ safe_run_hooks_maybe_narrowed (Lisp_Object hook, struct window *w) | |||
| 1911 | specbind (Qinhibit_quit, Qt); | 1911 | specbind (Qinhibit_quit, Qt); |
| 1912 | 1912 | ||
| 1913 | if (current_buffer->long_line_optimizations_p) | 1913 | if (current_buffer->long_line_optimizations_p) |
| 1914 | narrow_to_region_internal (make_fixnum (get_narrowed_begv (w, PT)), | 1914 | narrow_to_region_locked (make_fixnum (get_locked_narrowing_begv (PT)), |
| 1915 | make_fixnum (get_narrowed_zv (w, PT)), | 1915 | make_fixnum (get_locked_narrowing_zv (PT)), |
| 1916 | true); | 1916 | hook); |
| 1917 | 1917 | ||
| 1918 | run_hook_with_args (2, ((Lisp_Object []) {hook, hook}), | 1918 | run_hook_with_args (2, ((Lisp_Object []) {hook, hook}), |
| 1919 | safe_run_hook_funcall); | 1919 | safe_run_hook_funcall); |
| @@ -12727,8 +12727,9 @@ the error might happen repeatedly and make Emacs nonfunctional. | |||
| 12727 | 12727 | ||
| 12728 | Note that, when the current buffer contains one or more lines whose | 12728 | Note that, when the current buffer contains one or more lines whose |
| 12729 | length is above `long-line-threshold', these hook functions are called | 12729 | length is above `long-line-threshold', these hook functions are called |
| 12730 | with the buffer narrowed to a small portion around point, and the | 12730 | with the buffer narrowed to a small portion around point (whose size |
| 12731 | narrowing is locked (see `narrow-to-region'), so that these hook | 12731 | is specified by `long-line-locked-narrowing-region-size'), and the |
| 12732 | narrowing is locked (see `narrowing-lock'), so that these hook | ||
| 12732 | functions cannot use `widen' to gain access to other portions of | 12733 | functions cannot use `widen' to gain access to other portions of |
| 12733 | buffer text. | 12734 | buffer text. |
| 12734 | 12735 | ||
| @@ -12748,8 +12749,9 @@ avoid making Emacs unresponsive while the user types. | |||
| 12748 | 12749 | ||
| 12749 | Note that, when the current buffer contains one or more lines whose | 12750 | Note that, when the current buffer contains one or more lines whose |
| 12750 | length is above `long-line-threshold', these hook functions are called | 12751 | length is above `long-line-threshold', these hook functions are called |
| 12751 | with the buffer narrowed to a small portion around point, and the | 12752 | with the buffer narrowed to a small portion around point (whose size |
| 12752 | narrowing is locked (see `narrow-to-region'), so that these hook | 12753 | is specified by `long-line-locked-narrowing-region-size'), and the |
| 12754 | narrowing is locked (see `narrowing-lock'), so that these hook | ||
| 12753 | functions cannot use `widen' to gain access to other portions of | 12755 | functions cannot use `widen' to gain access to other portions of |
| 12754 | buffer text. | 12756 | buffer text. |
| 12755 | 12757 | ||
diff --git a/src/lisp.h b/src/lisp.h index 6a24a538172..0f70f60d75c 100644 --- a/src/lisp.h +++ b/src/lisp.h | |||
| @@ -4687,7 +4687,8 @@ extern void save_restriction_restore (Lisp_Object); | |||
| 4687 | extern Lisp_Object make_buffer_string (ptrdiff_t, ptrdiff_t, bool); | 4687 | extern Lisp_Object make_buffer_string (ptrdiff_t, ptrdiff_t, bool); |
| 4688 | extern Lisp_Object make_buffer_string_both (ptrdiff_t, ptrdiff_t, ptrdiff_t, | 4688 | extern Lisp_Object make_buffer_string_both (ptrdiff_t, ptrdiff_t, ptrdiff_t, |
| 4689 | ptrdiff_t, bool); | 4689 | ptrdiff_t, bool); |
| 4690 | extern Lisp_Object narrow_to_region_internal (Lisp_Object, Lisp_Object, bool); | 4690 | extern void narrow_to_region_locked (Lisp_Object, Lisp_Object, Lisp_Object); |
| 4691 | extern void reset_outermost_narrowings (void); | ||
| 4691 | extern void init_editfns (void); | 4692 | extern void init_editfns (void); |
| 4692 | extern void syms_of_editfns (void); | 4693 | extern void syms_of_editfns (void); |
| 4693 | 4694 | ||
diff --git a/src/xdisp.c b/src/xdisp.c index 5dcf21dc4ce..0002c3d611c 100644 --- a/src/xdisp.c +++ b/src/xdisp.c | |||
| @@ -3533,6 +3533,33 @@ get_closer_narrowed_begv (struct window *w, ptrdiff_t pos) | |||
| 3533 | return max ((pos / len - 1) * len, BEGV); | 3533 | return max ((pos / len - 1) * len, BEGV); |
| 3534 | } | 3534 | } |
| 3535 | 3535 | ||
| 3536 | ptrdiff_t | ||
| 3537 | get_locked_narrowing_begv (ptrdiff_t pos) | ||
| 3538 | { | ||
| 3539 | if (long_line_locked_narrowing_region_size == 0) | ||
| 3540 | return BEGV; | ||
| 3541 | int len = long_line_locked_narrowing_region_size / 2; | ||
| 3542 | int begv = max (pos - len, BEGV); | ||
| 3543 | int limit = long_line_locked_narrowing_bol_search_limit; | ||
| 3544 | while (limit) | ||
| 3545 | { | ||
| 3546 | if (begv == BEGV || FETCH_BYTE (CHAR_TO_BYTE (begv) - 1) == '\n') | ||
| 3547 | return begv; | ||
| 3548 | begv--; | ||
| 3549 | limit--; | ||
| 3550 | } | ||
| 3551 | return begv; | ||
| 3552 | } | ||
| 3553 | |||
| 3554 | ptrdiff_t | ||
| 3555 | get_locked_narrowing_zv (ptrdiff_t pos) | ||
| 3556 | { | ||
| 3557 | if (long_line_locked_narrowing_region_size == 0) | ||
| 3558 | return ZV; | ||
| 3559 | int len = long_line_locked_narrowing_region_size / 2; | ||
| 3560 | return min (pos + len, ZV); | ||
| 3561 | } | ||
| 3562 | |||
| 3536 | static void | 3563 | static void |
| 3537 | unwind_narrowed_begv (Lisp_Object point_min) | 3564 | unwind_narrowed_begv (Lisp_Object point_min) |
| 3538 | { | 3565 | { |
| @@ -4368,16 +4395,16 @@ handle_fontified_prop (struct it *it) | |||
| 4368 | 4395 | ||
| 4369 | if (current_buffer->long_line_optimizations_p) | 4396 | if (current_buffer->long_line_optimizations_p) |
| 4370 | { | 4397 | { |
| 4371 | ptrdiff_t begv = it->narrowed_begv; | 4398 | ptrdiff_t begv = it->locked_narrowing_begv; |
| 4372 | ptrdiff_t zv = it->narrowed_zv; | 4399 | ptrdiff_t zv = it->locked_narrowing_zv; |
| 4373 | ptrdiff_t charpos = IT_CHARPOS (*it); | 4400 | ptrdiff_t charpos = IT_CHARPOS (*it); |
| 4374 | if (charpos < begv || charpos > zv) | 4401 | if (charpos < begv || charpos > zv) |
| 4375 | { | 4402 | { |
| 4376 | begv = get_narrowed_begv (it->w, charpos); | 4403 | begv = get_locked_narrowing_begv (charpos); |
| 4377 | zv = get_narrowed_zv (it->w, charpos); | 4404 | zv = get_locked_narrowing_zv (charpos); |
| 4378 | } | 4405 | } |
| 4379 | narrow_to_region_internal (make_fixnum (begv), make_fixnum (zv), true); | 4406 | narrow_to_region_locked (make_fixnum (begv), make_fixnum (zv), |
| 4380 | specbind (Qrestrictions_locked, Qt); | 4407 | Qfontification_functions); |
| 4381 | } | 4408 | } |
| 4382 | 4409 | ||
| 4383 | /* Don't allow Lisp that runs from 'fontification-functions' | 4410 | /* Don't allow Lisp that runs from 'fontification-functions' |
| @@ -7435,12 +7462,20 @@ reseat (struct it *it, struct text_pos pos, bool force_p) | |||
| 7435 | { | 7462 | { |
| 7436 | it->narrowed_begv = get_narrowed_begv (it->w, window_point (it->w)); | 7463 | it->narrowed_begv = get_narrowed_begv (it->w, window_point (it->w)); |
| 7437 | it->narrowed_zv = get_narrowed_zv (it->w, window_point (it->w)); | 7464 | it->narrowed_zv = get_narrowed_zv (it->w, window_point (it->w)); |
| 7465 | it->locked_narrowing_begv | ||
| 7466 | = get_locked_narrowing_begv (window_point (it->w)); | ||
| 7467 | it->locked_narrowing_zv | ||
| 7468 | = get_locked_narrowing_zv (window_point (it->w)); | ||
| 7438 | } | 7469 | } |
| 7439 | else if ((pos.charpos < it->narrowed_begv || pos.charpos > it->narrowed_zv) | 7470 | else if ((pos.charpos < it->narrowed_begv || pos.charpos > it->narrowed_zv) |
| 7440 | && (!redisplaying_p || it->line_wrap == TRUNCATE)) | 7471 | && (!redisplaying_p || it->line_wrap == TRUNCATE)) |
| 7441 | { | 7472 | { |
| 7442 | it->narrowed_begv = get_narrowed_begv (it->w, pos.charpos); | 7473 | it->narrowed_begv = get_narrowed_begv (it->w, pos.charpos); |
| 7443 | it->narrowed_zv = get_narrowed_zv (it->w, pos.charpos); | 7474 | it->narrowed_zv = get_narrowed_zv (it->w, pos.charpos); |
| 7475 | it->locked_narrowing_begv | ||
| 7476 | = get_locked_narrowing_begv (window_point (it->w)); | ||
| 7477 | it->locked_narrowing_zv | ||
| 7478 | = get_locked_narrowing_zv (window_point (it->w)); | ||
| 7444 | } | 7479 | } |
| 7445 | } | 7480 | } |
| 7446 | 7481 | ||
| @@ -16266,7 +16301,6 @@ do { if (! polling_stopped_here) stop_polling (); \ | |||
| 16266 | do { if (polling_stopped_here) start_polling (); \ | 16301 | do { if (polling_stopped_here) start_polling (); \ |
| 16267 | polling_stopped_here = false; } while (false) | 16302 | polling_stopped_here = false; } while (false) |
| 16268 | 16303 | ||
| 16269 | |||
| 16270 | /* Perhaps in the future avoid recentering windows if it | 16304 | /* Perhaps in the future avoid recentering windows if it |
| 16271 | is not necessary; currently that causes some problems. */ | 16305 | is not necessary; currently that causes some problems. */ |
| 16272 | 16306 | ||
| @@ -16352,6 +16386,8 @@ redisplay_internal (void) | |||
| 16352 | FOR_EACH_FRAME (tail, frame) | 16386 | FOR_EACH_FRAME (tail, frame) |
| 16353 | XFRAME (frame)->already_hscrolled_p = false; | 16387 | XFRAME (frame)->already_hscrolled_p = false; |
| 16354 | 16388 | ||
| 16389 | reset_outermost_narrowings (); | ||
| 16390 | |||
| 16355 | retry: | 16391 | retry: |
| 16356 | /* Remember the currently selected window. */ | 16392 | /* Remember the currently selected window. */ |
| 16357 | sw = w; | 16393 | sw = w; |
| @@ -36711,10 +36747,11 @@ fontify a region starting at POS in the current buffer, and give | |||
| 36711 | fontified regions the property `fontified' with a non-nil value. | 36747 | fontified regions the property `fontified' with a non-nil value. |
| 36712 | 36748 | ||
| 36713 | Note that, when the buffer contains one or more lines whose length is | 36749 | Note that, when the buffer contains one or more lines whose length is |
| 36714 | above `long-line-threshold', these functions are called with the buffer | 36750 | above `long-line-threshold', these functions are called with the |
| 36715 | narrowed to a small portion around POS, and the narrowing is locked (see | 36751 | buffer narrowed to a small portion around POS (whose size is specified |
| 36716 | `narrow-to-region'), so that these functions cannot use `widen' to gain | 36752 | by `long-line-locked-narrowing-region-size'), and the narrowing is |
| 36717 | access to other portions of buffer text. */); | 36753 | locked (see `narrowing-lock'), so that these functions cannot use |
| 36754 | `widen' to gain access to other portions of buffer text. */); | ||
| 36718 | Vfontification_functions = Qnil; | 36755 | Vfontification_functions = Qnil; |
| 36719 | Fmake_variable_buffer_local (Qfontification_functions); | 36756 | Fmake_variable_buffer_local (Qfontification_functions); |
| 36720 | 36757 | ||