commit ce527464440ca070e752ca18a68ebf2d369a14df (HEAD, refs/remotes/origin/master) Author: Stefan Kangas Date: Sat Oct 29 07:44:07 2022 +0200 Prefer defvar-keymap in help.el * lisp/help.el (help-key): New function. (help-map): Prefer defvar-keymap. diff --git a/lisp/help.el b/lisp/help.el index d48b866938..b25a8ce299 100644 --- a/lisp/help.el +++ b/lisp/help.el @@ -55,66 +55,68 @@ This variable is bound to t during the preparation of a *Help* buffer.") -(defvar help-map - (let ((map (make-sparse-keymap))) - (define-key map (char-to-string help-char) 'help-for-help) - (define-key map [help] 'help-for-help) - (define-key map [f1] 'help-for-help) - (define-key map "." 'display-local-help) - (define-key map "?" 'help-for-help) - - (define-key map "\C-a" 'about-emacs) - (define-key map "\C-c" 'describe-copying) - (define-key map "\C-d" 'view-emacs-debugging) - (define-key map "\C-e" 'view-external-packages) - (define-key map "\C-f" 'view-emacs-FAQ) - (define-key map "\C-m" 'view-order-manuals) - (define-key map "\C-n" 'view-emacs-news) - (define-key map "\C-o" 'describe-distribution) - (define-key map "\C-p" 'view-emacs-problems) - (define-key map "\C-s" 'search-forward-help-for-help) - (define-key map "\C-t" 'view-emacs-todo) - (define-key map "\C-w" 'describe-no-warranty) - - ;; This does not fit the pattern, but it is natural given the C-\ command. - (define-key map "\C-\\" 'describe-input-method) - - (define-key map "C" 'describe-coding-system) - (define-key map "F" 'Info-goto-emacs-command-node) - (define-key map "I" 'describe-input-method) - (define-key map "K" 'Info-goto-emacs-key-command-node) - (define-key map "L" 'describe-language-environment) - (define-key map "S" 'info-lookup-symbol) - - (define-key map "a" 'apropos-command) - (define-key map "b" 'describe-bindings) - (define-key map "c" 'describe-key-briefly) - (define-key map "d" 'apropos-documentation) - (define-key map "e" 'view-echo-area-messages) - (define-key map "f" 'describe-function) - (define-key map "g" 'describe-gnu-project) - (define-key map "h" 'view-hello-file) - - (define-key map "i" 'info) - (define-key map "4i" 'info-other-window) - - (define-key map "k" 'describe-key) - (define-key map "l" 'view-lossage) - (define-key map "m" 'describe-mode) - (define-key map "o" 'describe-symbol) - (define-key map "n" 'view-emacs-news) - (define-key map "p" 'finder-by-keyword) - (define-key map "P" 'describe-package) - (define-key map "r" 'info-emacs-manual) - (define-key map "R" 'info-display-manual) - (define-key map "s" 'describe-syntax) - (define-key map "t" 'help-with-tutorial) - (define-key map "v" 'describe-variable) - (define-key map "w" 'where-is) - (define-key map "x" 'describe-command) - (define-key map "q" 'help-quit-or-quick) - map) - "Keymap for characters following the Help key.") +(defun help-key () + "Return `help-char' in a format suitable for the `keymap-set' KEY argument." + (key-description (char-to-string help-char))) + +(defvar-keymap help-map + :doc "Keymap for characters following the Help key." + (help-key) #'help-for-help + "" #'help-for-help + "" #'help-for-help + "." #'display-local-help + "?" #'help-for-help + + "C-a" #'about-emacs + "C-c" #'describe-copying + "C-d" #'view-emacs-debugging + "C-e" #'view-external-packages + "C-f" #'view-emacs-FAQ + "RET" #'view-order-manuals + "C-n" #'view-emacs-news + "C-o" #'describe-distribution + "C-p" #'view-emacs-problems + "C-s" #'search-forward-help-for-help + "C-t" #'view-emacs-todo + "C-w" #'describe-no-warranty + + ;; This does not fit the pattern, but it is natural given the C-\ command. + "C-\\" #'describe-input-method + + "C" #'describe-coding-system + "F" #'Info-goto-emacs-command-node + "I" #'describe-input-method + "K" #'Info-goto-emacs-key-command-node + "L" #'describe-language-environment + "S" #'info-lookup-symbol + + "a" #'apropos-command + "b" #'describe-bindings + "c" #'describe-key-briefly + "d" #'apropos-documentation + "e" #'view-echo-area-messages + "f" #'describe-function + "g" #'describe-gnu-project + "h" #'view-hello-file + + "i" #'info + "4 i" #'info-other-window + + "k" #'describe-key + "l" #'view-lossage + "m" #'describe-mode + "o" #'describe-symbol + "n" #'view-emacs-news + "p" #'finder-by-keyword + "P" #'describe-package + "r" #'info-emacs-manual + "R" #'info-display-manual + "s" #'describe-syntax + "t" #'help-with-tutorial + "v" #'describe-variable + "w" #'where-is + "x" #'describe-command + "q" #'help-quit-or-quick) (define-key global-map (char-to-string help-char) 'help-command) (define-key global-map [help] 'help-command) commit 3b0296c8b03436c7848009ca0238b1126dc3bac7 Author: Stefan Kangas Date: Sat Oct 29 03:57:55 2022 +0200 Prefer defvar-keymap in page-ext.el * lisp/textmodes/page-ext.el (pages--ctl-x-ctl-p-map) (pages-directory-mode-map): Prefer defvar-keymap. diff --git a/lisp/textmodes/page-ext.el b/lisp/textmodes/page-ext.el index 05c02308e5..b133b1e9e3 100644 --- a/lisp/textmodes/page-ext.el +++ b/lisp/textmodes/page-ext.el @@ -276,19 +276,17 @@ Used by `pages-directory-for-addresses' function." ;;; Key bindings for page handling functions -(defvar pages--ctl-x-ctl-p-map - (let ((map (make-sparse-keymap))) - (define-key map "\C-n" #'pages-next-page) - (define-key map "\C-p" #'pages-previous-page) - (define-key map "\C-a" #'pages-add-new-page) - (define-key map "\C-m" #'mark-page) - (define-key map "\C-s" #'pages-search) - (define-key map "s" #'pages-sort-buffer) - (define-key map "\C-l" #'pages-set-delimiter) - (define-key map "\C-d" #'pages-directory) - (define-key map "d" #'pages-directory-for-addresses) - map) - "Keymap for subcommands of C-x C-p, which are for page handling.") +(defvar-keymap pages--ctl-x-ctl-p-map + :doc "Keymap for subcommands of \\`C-x C-p', which are for page handling." + "C-n" #'pages-next-page + "C-p" #'pages-previous-page + "C-a" #'pages-add-new-page + "C-m" #'mark-page + "C-s" #'pages-search + "s" #'pages-sort-buffer + "C-l" #'pages-set-delimiter + "C-d" #'pages-directory + "d" #'pages-directory-for-addresses) ;; FIXME: Merely loading a package shouldn't have this kind of side-effects! (global-unset-key "\C-x\C-p") @@ -476,14 +474,12 @@ contain matches to the regexp.)") (define-obsolete-variable-alias 'pages-directory-map 'pages-directory-mode-map "26.1") -(defvar pages-directory-mode-map - (let ((map (make-sparse-keymap))) - (define-key map "\C-c\C-c" #'pages-directory-goto) - (define-key map "\C-m" #'pages-directory-goto) - (define-key map "\C-c\C-p\C-a" #'pages-add-new-page) - (define-key map [mouse-2] #'pages-directory-goto) - map) - "Keymap for the pages-directory-buffer.") +(defvar-keymap pages-directory-mode-map + :doc "Keymap for the pages-directory-buffer." + "C-c C-c" #'pages-directory-goto + "RET" #'pages-directory-goto + "C-c C-p C-a" #'pages-add-new-page + "" #'pages-directory-goto) (defvar pages-original-delimiter "^\f" "Default page delimiter.") commit aa23bf987a320f36478b09ae82a12a419bc00753 Author: Stefan Kangas Date: Sat Oct 29 03:42:46 2022 +0200 ; Minor cleanups in make-mode.el * lisp/progmodes/make-mode.el (makefile-query-one-target-method-function) (makefile-insert-macro, makefile-insert-target) (makefile-insert-target-ref, makefile-backslash-region) (makefile-browser-next-line, makefile-browser-previous-line) (makefile-remember-target): Minor cleanups. diff --git a/lisp/progmodes/make-mode.el b/lisp/progmodes/make-mode.el index 9b9bb0e9d4..4cee361df3 100644 --- a/lisp/progmodes/make-mode.el +++ b/lisp/progmodes/make-mode.el @@ -536,7 +536,7 @@ This should identify a `make' command that can handle the `-q' option." 'makefile-query-one-target-method-function "29.1") (defcustom makefile-query-one-target-method-function - 'makefile-query-by-make-minus-q + #'makefile-query-by-make-minus-q "Function to call to determine whether a make target is up to date. The function must satisfy this calling convention: @@ -995,12 +995,11 @@ Anywhere else just self-inserts." Interactively, prompt for the name of the macro." (interactive "sMacro Name: ") (makefile-pickup-macros) - (if (not (zerop (length macro-name))) - (progn - (beginning-of-line) - (insert macro-name makefile-macro-assign) - (setq makefile-need-macro-pickup t) - (makefile-remember-macro macro-name)))) + (unless (zerop (length macro-name)) + (beginning-of-line) + (insert macro-name makefile-macro-assign) + (setq makefile-need-macro-pickup t) + (makefile-remember-macro macro-name))) (defun makefile-insert-macro-ref (macro-name) "Complete on a list of known macros, then insert complete ref at point." @@ -1014,24 +1013,23 @@ Interactively, prompt for the name of the macro." (defun makefile-insert-target (target-name) "Prepare definition of a new target (dependency line)." (interactive "sTarget: ") - (if (not (zerop (length target-name))) - (progn - (beginning-of-line) - (insert target-name makefile-target-colon) - (makefile-forward-after-target-colon) - (end-of-line) - (setq makefile-need-target-pickup t) - (makefile-remember-target target-name)))) + (unless (zerop (length target-name)) + (beginning-of-line) + (insert target-name makefile-target-colon) + (makefile-forward-after-target-colon) + (end-of-line) + (setq makefile-need-target-pickup t) + (makefile-remember-target target-name))) (defun makefile-insert-target-ref (target-name) "Complete on a list of known targets, then insert TARGET-NAME at point." (interactive (list (progn - (makefile-pickup-targets) - (completing-read "Refer to target: " makefile-target-table nil nil nil)))) - (if (not (zerop (length target-name))) - (insert target-name " "))) + (makefile-pickup-targets) + (completing-read "Refer to target: " makefile-target-table nil nil nil)))) + (unless (zerop (length target-name)) + (insert target-name " "))) (defun makefile-electric-colon (arg) "Prompt for name of new target. @@ -1180,9 +1178,9 @@ definition and conveniently use this command." (when (and makefile-backslash-align (not delete-flag)) (while (< (point) to) (end-of-line) - (if (= (preceding-char) ?\\) - (progn (forward-char -1) - (skip-chars-backward " \t"))) + (when (= (preceding-char) ?\\) + (forward-char -1) + (skip-chars-backward " \t")) (setq column (max column (1+ (current-column)))) (forward-line 1)) ;; Adjust upward to a tab column, if that doesn't push @@ -1349,18 +1347,16 @@ Fill comments, backslashed lines, and variable definitions specially." (defun makefile-browser-next-line () "Move the browser selection cursor to the next line." (interactive) - (if (not (makefile-last-line-p)) - (progn - (forward-line 1) - (forward-char makefile-browser-cursor-column)))) + (unless (makefile-last-line-p) + (forward-line 1) + (forward-char makefile-browser-cursor-column))) (defun makefile-browser-previous-line () "Move the browser selection cursor to the previous line." (interactive) - (if (not (makefile-first-line-p)) - (progn - (forward-line -1) - (forward-char makefile-browser-cursor-column)))) + (unless (makefile-first-line-p) + (forward-line -1) + (forward-char makefile-browser-cursor-column))) ;;; ;;; Quitting the browser (returns to client buffer) @@ -1706,14 +1702,13 @@ matched in a rule action." (defun makefile-remember-target (target-name &optional has-prereqs) "Remember a given target if it is not already remembered for this buffer." - (if (not (zerop (length target-name))) - (progn - (if (not (assoc target-name makefile-target-table)) - (setq makefile-target-table - (cons (list target-name) makefile-target-table))) - (if has-prereqs - (setq makefile-has-prereqs - (cons target-name makefile-has-prereqs)))))) + (unless (zerop (length target-name)) + (if (not (assoc target-name makefile-target-table)) + (setq makefile-target-table + (cons (list target-name) makefile-target-table))) + (if has-prereqs + (setq makefile-has-prereqs + (cons target-name makefile-has-prereqs))))) (defun makefile-remember-macro (macro-name) "Remember a given macro if it is not already remembered for this buffer." commit 6fca31fdb8b49e60a750e6a9bb2237c0002c098f Author: Po Lu Date: Sat Oct 29 08:55:40 2022 +0800 Fix last change * src/itree.c (struct itree_iterator): (itree_iterator_next): Fix some fields and const confusion. diff --git a/src/itree.c b/src/itree.c index 18eb390e35..3b10802ff0 100644 --- a/src/itree.c +++ b/src/itree.c @@ -249,7 +249,7 @@ struct itree_iterator uintmax_t otick; enum itree_order order; bool running; - const char file; + const char *file; int line; }; @@ -1354,7 +1354,7 @@ itree_iterator_next (struct itree_iterator *g) { eassert (g->running); - const struct itree_node *null = NULL; + struct itree_node *const null = NULL; struct itree_node *node; /* The `visited` flag stored in each node is used here (and only here): @@ -1376,8 +1376,8 @@ itree_iterator_next (struct itree_iterator *g) visited = nav_flag (nav), node && !visited)) { - const struct itree_node *left = node->left; - const struct itree_node *right = node->right; + struct itree_node *const left = node->left; + struct itree_node *const right = node->right; interval_tree_inherit_offset (g->otick, node); eassert (itree_limit_is_stable (node)); commit fbf2bfba0770b7f575d8ef06ca3a08cf1a38cc2d Author: Po Lu Date: Sat Oct 29 08:48:58 2022 +0800 Fix function declarations in itree headers * src/itree.h: Make all declarations `extern'. diff --git a/src/itree.h b/src/itree.h index 3df07ac1b7..c6b68d3667 100644 --- a/src/itree.h +++ b/src/itree.h @@ -79,8 +79,7 @@ struct itree_node NOTE: The interval iterators ensure nodes are clean before yielding them, so BEGIN and END may be safely used as buffer - positions then. - */ + positions then. */ ptrdiff_t begin; /* The beginning of this interval. */ ptrdiff_t end; /* The end of the interval. */ @@ -100,39 +99,41 @@ struct itree_tree intmax_t size; /* Number of nodes in the tree. */ }; -enum itree_order { - ITREE_ASCENDING, - ITREE_DESCENDING, - ITREE_PRE_ORDER, -}; - -void itree_node_init (struct itree_node *, bool, bool, Lisp_Object); -ptrdiff_t itree_node_begin (struct itree_tree *, struct itree_node *); -ptrdiff_t itree_node_end (struct itree_tree *, struct itree_node *); -void itree_node_set_region (struct itree_tree *, struct itree_node *, - ptrdiff_t, ptrdiff_t); -struct itree_tree *itree_create (void); -void itree_destroy (struct itree_tree *); -intmax_t itree_size (struct itree_tree *); -void itree_clear (struct itree_tree *); -void itree_insert (struct itree_tree *tree, struct itree_node *node, - ptrdiff_t begin, ptrdiff_t end); -struct itree_node *itree_remove (struct itree_tree *, - struct itree_node *); -void itree_insert_gap (struct itree_tree *, ptrdiff_t, ptrdiff_t); -void itree_delete_gap (struct itree_tree *, ptrdiff_t, ptrdiff_t); +enum itree_order + { + ITREE_ASCENDING, + ITREE_DESCENDING, + ITREE_PRE_ORDER, + }; + +extern void itree_node_init (struct itree_node *, bool, bool, Lisp_Object); +extern ptrdiff_t itree_node_begin (struct itree_tree *, struct itree_node *); +extern ptrdiff_t itree_node_end (struct itree_tree *, struct itree_node *); +extern void itree_node_set_region (struct itree_tree *, struct itree_node *, + ptrdiff_t, ptrdiff_t); +extern struct itree_tree *itree_create (void); +extern void itree_destroy (struct itree_tree *); +extern intmax_t itree_size (struct itree_tree *); +extern void itree_clear (struct itree_tree *); +extern void itree_insert (struct itree_tree *, struct itree_node *, + ptrdiff_t, ptrdiff_t); +extern struct itree_node *itree_remove (struct itree_tree *, + struct itree_node *); +extern void itree_insert_gap (struct itree_tree *, ptrdiff_t, ptrdiff_t); +extern void itree_delete_gap (struct itree_tree *, ptrdiff_t, ptrdiff_t); /* Iteration functions. Almost all code should use ITREE_FOREACH instead. */ -bool itree_iterator_busy_p (void); -struct itree_iterator * -itree_iterator_start (struct itree_tree *tree, ptrdiff_t begin, - ptrdiff_t end, enum itree_order order, - const char *file, int line); -void itree_iterator_narrow (struct itree_iterator *, ptrdiff_t, - ptrdiff_t); -void itree_iterator_finish (struct itree_iterator *); -struct itree_node *itree_iterator_next (struct itree_iterator *); +extern bool itree_iterator_busy_p (void); +extern struct itree_iterator *itree_iterator_start (struct itree_tree *, + ptrdiff_t, + ptrdiff_t, + enum itree_order, + const char *, int); +extern void itree_iterator_narrow (struct itree_iterator *, ptrdiff_t, + ptrdiff_t); +extern void itree_iterator_finish (struct itree_iterator *); +extern struct itree_node *itree_iterator_next (struct itree_iterator *); /* Iterate over the intervals between BEG and END in the tree T. N will hold successive nodes. ORDER can be one of : `ASCENDING`, commit 7ca456da7f72feeeec571ece3c373e2b3a3d327b Author: Po Lu Date: Sat Oct 29 08:47:16 2022 +0800 Fix coding style of latest feature branch merge * src/itree.c (interval_stack_ensure_space) (interval_stack_push_flagged, struct itree_iterator) (struct check_subtree_result, check_subtree, check_tree) (itree_newlimit, interval_tree_inherit_offset) (interval_tree_propagate_limit, itree_node_init, itree_node_begin) (itree_node_end, itree_create, interval_tree_rotate_left) (interval_tree_rotate_right, interval_tree_insert_fix) (interval_tree_insert, itree_insert, itree_node_set_region) (interval_tree_contains, interval_tree_subtree_min) (interval_tree_remove_fix, interval_tree_replace_child) (interval_tree_transplant, itree_remove, itree_iterator_start) (itree_insert_gap, itree_delete_gap, interval_node_intersects) (itree_iterator_next, itree_iterator_narrow): Tabify. Fix comment and code coding style. diff --git a/src/itree.c b/src/itree.c index e824f2c891..18eb390e35 100644 --- a/src/itree.c +++ b/src/itree.c @@ -125,8 +125,7 @@ along with GNU Emacs. If not, see . */ remember the fact, that a node's path to the root has no offsets applied (i.e. its values are up to date). This is the case if some node's value differs from the tree's one, the later of which is - incremented whenever some node's offset has changed. -*/ + incremented whenever some node's offset has changed. */ /* +=======================================================================+ * | Stack @@ -198,7 +197,7 @@ interval_stack_ensure_space (struct interval_stack *stack, intmax_t nelements) { stack->size = (nelements + 1) * 2; stack->nodes = xrealloc (stack->nodes, - stack->size * sizeof (*stack->nodes)); + stack->size * sizeof (*stack->nodes)); } } @@ -206,7 +205,7 @@ interval_stack_ensure_space (struct interval_stack *stack, intmax_t nelements) static inline void interval_stack_push_flagged (struct interval_stack *stack, - struct itree_node *node, bool flag) + struct itree_node *node, bool flag) { eassert (node && node != NULL); @@ -245,10 +244,12 @@ struct itree_iterator struct interval_stack *stack; ptrdiff_t begin; ptrdiff_t end; - uintmax_t otick; /* A copy of the tree's `otick`. */ + + /* A copy of the tree's `otick`. */ + uintmax_t otick; enum itree_order order; bool running; - const char* file; + const char file; int line; }; @@ -294,20 +295,25 @@ itree_init (void) struct check_subtree_result { - int size; /* Node count of the tree. */ - ptrdiff_t limit; /* Limit of the tree (max END). */ - int black_height; /* Black height of the tree. */ + /* Node count of the tree. */ + int size; + + /* Limit of the tree (max END). */ + ptrdiff_t limit; + + /* Black height of the tree. */ + int black_height; }; static struct check_subtree_result check_subtree (struct itree_node *node, - bool check_red_black_invariants, uintmax_t tree_otick, - ptrdiff_t offset, ptrdiff_t min_begin, - ptrdiff_t max_begin) + bool check_red_black_invariants, uintmax_t tree_otick, + ptrdiff_t offset, ptrdiff_t min_begin, + ptrdiff_t max_begin) { struct check_subtree_result result = { .size = 0, - .limit = PTRDIFF_MIN, - .black_height = 0 }; + .limit = PTRDIFF_MIN, + .black_height = 0 }; if (node == NULL) return result; @@ -338,10 +344,10 @@ check_subtree (struct itree_node *node, struct check_subtree_result left_result = check_subtree (node->left, check_red_black_invariants, - tree_otick, offset, min_begin, begin); + tree_otick, offset, min_begin, begin); struct check_subtree_result right_result = check_subtree (node->right, check_red_black_invariants, - tree_otick, offset, begin, max_begin); + tree_otick, offset, begin, max_begin); eassert (left_result.limit <= limit); eassert (right_result.limit <= limit); @@ -370,7 +376,7 @@ check_subtree (struct itree_node *node, */ static bool check_tree (struct itree_tree *tree, - bool check_red_black_invariants) + bool check_red_black_invariants) { eassert (tree != NULL); eassert (tree->size >= 0); @@ -383,8 +389,8 @@ check_tree (struct itree_tree *tree, struct itree_node *node = tree->root; struct check_subtree_result result = check_subtree (node, check_red_black_invariants, tree->otick, - node->offset, PTRDIFF_MIN, - PTRDIFF_MAX); + node->offset, PTRDIFF_MIN, + PTRDIFF_MAX); eassert (result.size == tree->size); /* The only way this function fails is eassert(). */ @@ -412,12 +418,12 @@ itree_newlimit (struct itree_node *node) { eassert (node != NULL); return max (node->end, - max (node->left == NULL - ? PTRDIFF_MIN - : node->left->limit + node->left->offset, - node->right == NULL - ? PTRDIFF_MIN - : node->right->limit + node->right->offset)); + max (node->left == NULL + ? PTRDIFF_MIN + : node->left->limit + node->left->offset, + node->right == NULL + ? PTRDIFF_MIN + : node->right->limit + node->right->offset)); } /* Update NODE's limit attribute according to its children. */ @@ -459,9 +465,9 @@ interval_tree_inherit_offset (uintmax_t otick, struct itree_node *node) node->end += node->offset; node->limit += node->offset; if (node->left != NULL) - node->left->offset += node->offset; + node->left->offset += node->offset; if (node->right != NULL) - node->right->offset += node->offset; + node->right->offset += node->offset; node->offset = 0; } /* The only thing that matters about `otick` is whether it's equal to @@ -477,18 +483,22 @@ interval_tree_inherit_offset (uintmax_t otick, struct itree_node *node) static void interval_tree_propagate_limit (struct itree_node *node) { + ptrdiff_t newlimit; + if (node == NULL) return; - while (1) { - ptrdiff_t newlimit = itree_newlimit (node); - if (newlimit == node->limit) - break; - node->limit = newlimit; - if (node->parent == NULL) - break; - node = node->parent; - } + while (1) + { + newlimit = itree_newlimit (node); + + if (newlimit == node->limit) + break; + node->limit = newlimit; + if (node->parent == NULL) + break; + node = node->parent; + } } static struct itree_node* @@ -512,8 +522,8 @@ interval_tree_validate (struct itree_tree *tree, struct itree_node *node) void itree_node_init (struct itree_node *node, - bool front_advance, bool rear_advance, - Lisp_Object data) + bool front_advance, bool rear_advance, + Lisp_Object data) { node->parent = NULL; node->left = NULL; @@ -529,7 +539,7 @@ itree_node_init (struct itree_node *node, ptrdiff_t itree_node_begin (struct itree_tree *tree, - struct itree_node *node) + struct itree_node *node) { interval_tree_validate (tree, node); return node->begin; @@ -539,7 +549,7 @@ itree_node_begin (struct itree_tree *tree, ptrdiff_t itree_node_end (struct itree_tree *tree, - struct itree_node *node) + struct itree_node *node) { interval_tree_validate (tree, node); return node->end; @@ -547,7 +557,7 @@ itree_node_end (struct itree_tree *tree, /* Allocate an interval_tree. Free with interval_tree_destroy. */ -struct itree_tree* +struct itree_tree * itree_create (void) { /* FIXME? Maybe avoid the initialization of itree_null in the same @@ -603,7 +613,7 @@ itree_size (struct itree_tree *tree) static void interval_tree_rotate_left (struct itree_tree *tree, - struct itree_node *node) + struct itree_node *node) { eassert (node->right != NULL); @@ -646,7 +656,7 @@ interval_tree_rotate_left (struct itree_tree *tree, static void interval_tree_rotate_right (struct itree_tree *tree, - struct itree_node *node) + struct itree_node *node) { eassert (tree && node && node->left != NULL); @@ -685,7 +695,7 @@ interval_tree_rotate_right (struct itree_tree *tree, static void interval_tree_insert_fix (struct itree_tree *tree, - struct itree_node *node) + struct itree_node *node) { eassert (tree->root->red == false); @@ -701,7 +711,7 @@ interval_tree_insert_fix (struct itree_tree *tree, our "uncle". */ struct itree_node *uncle = node->parent->parent->right; - if (null_safe_is_red (uncle)) /* case 1.a */ + if (null_safe_is_red (uncle)) /* case 1.a */ { /* Uncle and parent are red but should be black because NODE is red. Change the colors accordingly and @@ -710,7 +720,7 @@ interval_tree_insert_fix (struct itree_tree *tree, uncle->red = false; node->parent->parent->red = true; node = node->parent->parent; - } + } else { /* Parent and uncle have different colors; parent is @@ -719,25 +729,25 @@ interval_tree_insert_fix (struct itree_tree *tree, { node = node->parent; interval_tree_rotate_left (tree, node); - } - /* case 3.a */ + } + /* case 3.a */ node->parent->red = false; node->parent->parent->red = true; interval_tree_rotate_right (tree, node->parent->parent); - } - } + } + } else { /* This is the symmetrical case of above. */ struct itree_node *uncle = node->parent->parent->left; - if (null_safe_is_red (uncle)) /* case 1.b */ + if (null_safe_is_red (uncle)) /* case 1.b */ { node->parent->red = false; uncle->red = false; node->parent->parent->red = true; node = node->parent->parent; - } + } else { if (node == node->parent->left) /* case 2.b */ @@ -745,12 +755,12 @@ interval_tree_insert_fix (struct itree_tree *tree, node = node->parent; interval_tree_rotate_right (tree, node); } - /* case 3.b */ + /* case 3.b */ node->parent->red = false; node->parent->parent->red = true; interval_tree_rotate_left (tree, node->parent->parent); - } - } + } + } } /* The root may have been changed to red due to the algorithm. @@ -769,7 +779,7 @@ interval_tree_insert (struct itree_tree *tree, struct itree_node *node) /* FIXME: The assertion below fails because `delete_all_overlays` doesn't set left/right/parent to NULL. */ /* eassert (node->left == NULL && node->right == NULL - && node->parent == NULL) */; + && node->parent == NULL) */; eassert (check_tree (tree, true)); /* FIXME: Too expensive. */ struct itree_node *parent = NULL; @@ -788,7 +798,7 @@ interval_tree_insert (struct itree_tree *tree, struct itree_node *node) eassert (child->offset == 0); child->limit = max (child->limit, node->end); /* This suggests that nodes in the right subtree are strictly - greater. But this is not true due to later rotations. */ + greater. But this is not true due to later rotations. */ child = node->begin <= child->begin ? child->left : child->right; } @@ -822,7 +832,7 @@ interval_tree_insert (struct itree_tree *tree, struct itree_node *node) void itree_insert (struct itree_tree *tree, struct itree_node *node, - ptrdiff_t begin, ptrdiff_t end) + ptrdiff_t begin, ptrdiff_t end) { node->begin = begin; node->end = end; @@ -834,8 +844,8 @@ itree_insert (struct itree_tree *tree, struct itree_node *node, void itree_node_set_region (struct itree_tree *tree, - struct itree_node *node, - ptrdiff_t begin, ptrdiff_t end) + struct itree_node *node, + ptrdiff_t begin, ptrdiff_t end) { interval_tree_validate (tree, node); if (begin != node->begin) @@ -863,8 +873,8 @@ interval_tree_contains (struct itree_tree *tree, struct itree_node *node) ITREE_FOREACH (other, tree, node->begin, PTRDIFF_MAX, ASCENDING) if (other == node) { - ITREE_FOREACH_ABORT (); - return true; + ITREE_FOREACH_ABORT (); + return true; } return false; @@ -885,7 +895,7 @@ interval_tree_subtree_min (uintmax_t otick, struct itree_node *node) if (node == NULL) return node; while ((interval_tree_inherit_offset (otick, node), - node->left != NULL)) + node->left != NULL)) node = node->left; return node; } @@ -896,8 +906,8 @@ interval_tree_subtree_min (uintmax_t otick, struct itree_node *node) static void interval_tree_remove_fix (struct itree_tree *tree, - struct itree_node *node, - struct itree_node *parent) + struct itree_node *node, + struct itree_node *parent) { if (parent == NULL) eassert (node == tree->root); @@ -912,70 +922,70 @@ interval_tree_remove_fix (struct itree_tree *tree, { struct itree_node *other = parent->right; - if (null_safe_is_red (other)) /* case 1.a */ + if (null_safe_is_red (other)) /* case 1.a */ { other->red = false; parent->red = true; interval_tree_rotate_left (tree, parent); other = parent->right; - } - eassume (other != NULL); + } + eassume (other != NULL); - if (null_safe_is_black (other->left) /* 2.a */ - && null_safe_is_black (other->right)) + if (null_safe_is_black (other->left) /* 2.a */ + && null_safe_is_black (other->right)) { other->red = true; node = parent; eassert (node != NULL); parent = node->parent; - } + } else { - if (null_safe_is_black (other->right)) /* 3.a */ + if (null_safe_is_black (other->right)) /* 3.a */ { other->left->red = false; other->red = true; interval_tree_rotate_right (tree, other); other = parent->right; - } + } other->red = parent->red; /* 4.a */ parent->red = false; other->right->red = false; interval_tree_rotate_left (tree, parent); node = tree->root; parent = NULL; - } - } + } + } else { struct itree_node *other = parent->left; - if (null_safe_is_red (other)) /* 1.b */ + if (null_safe_is_red (other)) /* 1.b */ { other->red = false; parent->red = true; interval_tree_rotate_right (tree, parent); other = parent->left; - } - eassume (other != NULL); + } + eassume (other != NULL); - if (null_safe_is_black (other->right) /* 2.b */ - && null_safe_is_black (other->left)) + if (null_safe_is_black (other->right) /* 2.b */ + && null_safe_is_black (other->left)) { other->red = true; node = parent; eassert (node != NULL); parent = node->parent; - } + } else { - if (null_safe_is_black (other->left)) /* 3.b */ + if (null_safe_is_black (other->left)) /* 3.b */ { other->right->red = false; other->red = true; interval_tree_rotate_left (tree, other); other = parent->left; - } + } other->red = parent->red; /* 4.b */ parent->red = false; @@ -983,12 +993,12 @@ interval_tree_remove_fix (struct itree_tree *tree, interval_tree_rotate_right (tree, parent); node = tree->root; parent = NULL; - } - } + } + } } if (node != NULL) - node->red = false; + node->red = false; } /* Return accumulated offsets of NODE's parents. */ @@ -1014,12 +1024,12 @@ itree_total_offset (struct itree_node *node) Requires both nodes to be using the same effective `offset`. */ static void interval_tree_replace_child (struct itree_tree *tree, - struct itree_node *source, - struct itree_node *dest) + struct itree_node *source, + struct itree_node *dest) { eassert (tree && dest != NULL); eassert (source == NULL - || itree_total_offset (source) == itree_total_offset (dest)); + || itree_total_offset (source) == itree_total_offset (dest)); if (dest == tree->root) tree->root = source; @@ -1040,8 +1050,8 @@ interval_tree_replace_child (struct itree_tree *tree, effective `offset`. */ static void interval_tree_transplant (struct itree_tree *tree, - struct itree_node *source, - struct itree_node *dest) + struct itree_node *source, + struct itree_node *dest) { interval_tree_replace_child (tree, source, dest); source->left = dest->left; @@ -1067,8 +1077,8 @@ itree_remove (struct itree_tree *tree, struct itree_node *node) interval_tree_inherit_offset (tree->otick, node); struct itree_node *splice = (node->left == NULL || node->right == NULL) - ? node - : interval_tree_subtree_min (tree->otick, node->right); + ? node + : interval_tree_subtree_min (tree->otick, node->right); /* Find `subtree`, the only child of `splice` (may be NULL). Note: `subtree` will not be modified other than changing its parent to @@ -1101,7 +1111,7 @@ itree_remove (struct itree_tree *tree, struct itree_node *node) interval_tree_transplant (tree, splice, node); interval_tree_propagate_limit (subtree_parent); if (splice != subtree_parent) - interval_tree_update_limit (splice); + interval_tree_update_limit (splice); } interval_tree_propagate_limit (splice->parent); @@ -1138,15 +1148,15 @@ itree_iterator_busy_p (void) struct itree_iterator * itree_iterator_start (struct itree_tree *tree, ptrdiff_t begin, - ptrdiff_t end, enum itree_order order, - const char *file, int line) + ptrdiff_t end, enum itree_order order, + const char *file, int line) { /* struct itree_iterator *iter = tree->iter; */ if (iter->running) { fprintf (stderr, - "Detected nested iteration!\nOuter: %s:%d\nInner: %s:%d\n", - iter->file, iter->line, file, line); + "Detected nested iteration!\nOuter: %s:%d\nInner: %s:%d\n", + iter->file, iter->line, file, line); emacs_abort (); } iter->begin = begin; @@ -1160,7 +1170,7 @@ itree_iterator_start (struct itree_tree *tree, ptrdiff_t begin, iter->line = line; iter->running = true; /* interval_stack_ensure_space (iter->stack, - 2 * interval_tree_max_height (tree)); */ + 2 * interval_tree_max_height (tree)); */ return iter; } @@ -1184,7 +1194,7 @@ itree_iterator_finish (struct itree_iterator *iter) void itree_insert_gap (struct itree_tree *tree, - ptrdiff_t pos, ptrdiff_t length) + ptrdiff_t pos, ptrdiff_t length) { if (length <= 0 || tree->root == NULL) return; @@ -1199,8 +1209,8 @@ itree_insert_gap (struct itree_tree *tree, ITREE_FOREACH (node, tree, pos, pos + 1, PRE_ORDER) { if (node->begin == pos && node->front_advance - && (node->begin != node->end || node->rear_advance)) - interval_stack_push (saved, node); + && (node->begin != node->end || node->rear_advance)) + interval_stack_push (saved, node); } for (int i = 0; i < saved->length; ++i) itree_remove (tree, nav_nodeptr (saved->nodes[i])); @@ -1214,35 +1224,35 @@ itree_insert_gap (struct itree_tree *tree, interval_stack_push (stack, tree->root); nodeptr_and_flag nav; while ((nav = interval_stack_pop (stack), - node = nav_nodeptr (nav))) - { - /* Process in pre-order. */ - interval_tree_inherit_offset (tree->otick, node); - if (node->right != NULL) - { - if (node->begin > pos) - { - /* All nodes in this subtree are shifted by length. */ - node->right->offset += length; - ++tree->otick; - } - else - interval_stack_push (stack, node->right); - } - if (node->left != NULL - && pos <= node->left->limit + node->left->offset) - interval_stack_push (stack, node->left); - - /* node->begin == pos implies no front-advance. */ - if (node->begin > pos) - node->begin += length; - if (node->end > pos || (node->end == pos && node->rear_advance)) - { - node->end += length; - eassert (node != NULL); - interval_tree_propagate_limit (node); - } - } + node = nav_nodeptr (nav))) + { + /* Process in pre-order. */ + interval_tree_inherit_offset (tree->otick, node); + if (node->right != NULL) + { + if (node->begin > pos) + { + /* All nodes in this subtree are shifted by length. */ + node->right->offset += length; + ++tree->otick; + } + else + interval_stack_push (stack, node->right); + } + if (node->left != NULL + && pos <= node->left->limit + node->left->offset) + interval_stack_push (stack, node->left); + + /* node->begin == pos implies no front-advance. */ + if (node->begin > pos) + node->begin += length; + if (node->end > pos || (node->end == pos && node->rear_advance)) + { + node->end += length; + eassert (node != NULL); + interval_tree_propagate_limit (node); + } + } interval_stack_destroy (stack); } @@ -1250,12 +1260,12 @@ itree_insert_gap (struct itree_tree *tree, uintmax_t notick = tree->otick; nodeptr_and_flag nav; while ((nav = interval_stack_pop (saved), - node = nav_nodeptr (nav))) + node = nav_nodeptr (nav))) { eassert (node->otick == ootick); node->begin += length; if (node->end != pos || node->rear_advance) - node->end += length; + node->end += length; node->otick = notick; interval_tree_insert (tree, node); } @@ -1268,7 +1278,7 @@ itree_insert_gap (struct itree_tree *tree, void itree_delete_gap (struct itree_tree *tree, - ptrdiff_t pos, ptrdiff_t length) + ptrdiff_t pos, ptrdiff_t length) { if (length <= 0 || tree->root == NULL) return; @@ -1288,28 +1298,28 @@ itree_delete_gap (struct itree_tree *tree, node = nav_nodeptr (nav); interval_tree_inherit_offset (tree->otick, node); if (node->right != NULL) - { - if (node->begin > pos + length) - { - /* Shift right subtree to the left. */ - node->right->offset -= length; - ++tree->otick; - } - else - interval_stack_push (stack, node->right); - } + { + if (node->begin > pos + length) + { + /* Shift right subtree to the left. */ + node->right->offset -= length; + ++tree->otick; + } + else + interval_stack_push (stack, node->right); + } if (node->left != NULL - && pos <= node->left->limit + node->left->offset) - interval_stack_push (stack, node->left); + && pos <= node->left->limit + node->left->offset) + interval_stack_push (stack, node->left); if (pos < node->begin) - node->begin = max (pos, node->begin - length); + node->begin = max (pos, node->begin - length); if (node->end > pos) - { - node->end = max (pos , node->end - length); - eassert (node != NULL); - interval_tree_propagate_limit (node); - } + { + node->end = max (pos , node->end - length); + eassert (node != NULL); + interval_tree_propagate_limit (node); + } } interval_stack_destroy (stack); } @@ -1330,7 +1340,7 @@ itree_delete_gap (struct itree_tree *tree, static inline bool interval_node_intersects (const struct itree_node *node, - ptrdiff_t begin, ptrdiff_t end) + ptrdiff_t begin, ptrdiff_t end) { return (begin < node->end && node->begin < end) || (node->begin == node->end && begin == node->begin); @@ -1344,7 +1354,7 @@ itree_iterator_next (struct itree_iterator *g) { eassert (g->running); - struct itree_node * const null = NULL; + const struct itree_node *null = NULL; struct itree_node *node; /* The `visited` flag stored in each node is used here (and only here): @@ -1357,51 +1367,52 @@ itree_iterator_next (struct itree_iterator *g) needs to be considered but we haven't yet decided whether it's included in the iterator's output. */ - do { - nodeptr_and_flag nav; - bool visited; - while ((nav = interval_stack_pop (g->stack), - node = nav_nodeptr (nav), - visited = nav_flag (nav), - node && !visited)) - { - struct itree_node * const left = node->left; - struct itree_node * const right = node->right; - - interval_tree_inherit_offset (g->otick, node); - eassert (itree_limit_is_stable (node)); - switch (g->order) - { - case ITREE_ASCENDING: - if (right != null && node->begin <= g->end) - interval_stack_push_flagged (g->stack, right, false); - if (interval_node_intersects (node, g->begin, g->end)) - interval_stack_push_flagged (g->stack, node, true); - /* Node's children may still be off-set and we need to add it. */ - if (left != null && g->begin <= left->limit + left->offset) - interval_stack_push_flagged (g->stack, left, false); - break; - case ITREE_DESCENDING: - if (left != null && g->begin <= left->limit + left->offset) - interval_stack_push_flagged (g->stack, left, false); - if (interval_node_intersects (node, g->begin, g->end)) - interval_stack_push_flagged (g->stack, node, true); - if (right != null && node->begin <= g->end) - interval_stack_push_flagged (g->stack, right, false); - break; - case ITREE_PRE_ORDER: - if (right != null && node->begin <= g->end) - interval_stack_push_flagged (g->stack, right, false); - if (left != null && g->begin <= left->limit + left->offset) - interval_stack_push_flagged (g->stack, left, false); - if (interval_node_intersects (node, g->begin, g->end)) - interval_stack_push_flagged (g->stack, node, true); - break; - } - } - /* Node may have been invalidated by itree_iterator_narrow - after it was pushed: Check if it still intersects. */ - } while (node && ! interval_node_intersects (node, g->begin, g->end)); + do + { + nodeptr_and_flag nav; + bool visited; + while ((nav = interval_stack_pop (g->stack), + node = nav_nodeptr (nav), + visited = nav_flag (nav), + node && !visited)) + { + const struct itree_node *left = node->left; + const struct itree_node *right = node->right; + + interval_tree_inherit_offset (g->otick, node); + eassert (itree_limit_is_stable (node)); + switch (g->order) + { + case ITREE_ASCENDING: + if (right != null && node->begin <= g->end) + interval_stack_push_flagged (g->stack, right, false); + if (interval_node_intersects (node, g->begin, g->end)) + interval_stack_push_flagged (g->stack, node, true); + /* Node's children may still be off-set and we need to add it. */ + if (left != null && g->begin <= left->limit + left->offset) + interval_stack_push_flagged (g->stack, left, false); + break; + case ITREE_DESCENDING: + if (left != null && g->begin <= left->limit + left->offset) + interval_stack_push_flagged (g->stack, left, false); + if (interval_node_intersects (node, g->begin, g->end)) + interval_stack_push_flagged (g->stack, node, true); + if (right != null && node->begin <= g->end) + interval_stack_push_flagged (g->stack, right, false); + break; + case ITREE_PRE_ORDER: + if (right != null && node->begin <= g->end) + interval_stack_push_flagged (g->stack, right, false); + if (left != null && g->begin <= left->limit + left->offset) + interval_stack_push_flagged (g->stack, left, false); + if (interval_node_intersects (node, g->begin, g->end)) + interval_stack_push_flagged (g->stack, node, true); + break; + } + } + /* Node may have been invalidated by itree_iterator_narrow + after it was pushed: Check if it still intersects. */ + } while (node && ! interval_node_intersects (node, g->begin, g->end)); return node; } @@ -1411,7 +1422,7 @@ itree_iterator_next (struct itree_iterator *g) void itree_iterator_narrow (struct itree_iterator *g, - ptrdiff_t begin, ptrdiff_t end) + ptrdiff_t begin, ptrdiff_t end) { eassert (g->running); eassert (begin >= g->begin); commit 8562a23fc6a382367351a1471d8585279d12605a Author: Po Lu Date: Sat Oct 29 08:38:09 2022 +0800 Include Cairo FreeType header when available * src/xsettings.c: Include cairo-ft.h if CAIRO_HAS_FT_FONT. (bug#58830) diff --git a/src/xsettings.c b/src/xsettings.c index c4c70ad846..15e7ff5499 100644 --- a/src/xsettings.c +++ b/src/xsettings.c @@ -54,13 +54,14 @@ typedef unsigned int CARD32; #include #endif -#if defined USE_CAIRO || defined HAVE_XFT #ifdef USE_CAIRO #include -#include -#else /* HAVE_XFT */ +#elif defined HAVE_XFT #include #endif + +#if defined USE_CAIRO && defined CAIRO_HAS_FT_FONT +#include #endif static char *current_mono_font; commit 71589b101ccbec67fa2741856ee0add5752dea72 Merge: 69121c33e4 9d7ba2b199 Author: Stefan Monnier Date: Fri Oct 28 17:44:44 2022 -0400 Merge remote-tracking branch 'origin/feature/noverlay' commit 69121c33e4a11805bf6438131c8aec72411a0e5d Author: Stefan Kangas Date: Fri Oct 28 23:08:36 2022 +0200 Fix warning in xsettings.c * src/xsettings.c [USE_CAIRO]: Include cairo-ft.h. Thanks to Vincent Bernat . diff --git a/src/xsettings.c b/src/xsettings.c index deacddcfde..c4c70ad846 100644 --- a/src/xsettings.c +++ b/src/xsettings.c @@ -57,6 +57,7 @@ typedef unsigned int CARD32; #if defined USE_CAIRO || defined HAVE_XFT #ifdef USE_CAIRO #include +#include #else /* HAVE_XFT */ #include #endif commit 61226605f6bfe5d0a4d9d63bda7f0eae634d9ec8 Author: Stefan Kangas Date: Fri Oct 28 22:34:24 2022 +0200 ; Fix sit-for calling convention in zone.el * lisp/play/zone.el (zone-pgm-2nd-putz-with-case) (zone-pgm-putz-with-case): Don't use obsolete calling convention for 'sit-for'. diff --git a/lisp/play/zone.el b/lisp/play/zone.el index 1e8e0cbab5..a470f23dfe 100644 --- a/lisp/play/zone.el +++ b/lisp/play/zone.el @@ -347,7 +347,7 @@ run a specific program. The program must be a member of (upcase i))) (setq i (+ i (1+ (random 5))))) (translate-region (point-min) (point-max) tbl) - (sit-for 0 2)))) + (sit-for 0.002)))) (defun zone-pgm-putz-with-case () (goto-char (point-min)) @@ -359,7 +359,7 @@ run a specific program. The program must be a member of 'downcase-region) (1- np) np) (setq np (+ np (1+ (random 5)))))) (goto-char (point-min)) - (sit-for 0 2))) + (sit-for 0.002))) ;;;; rotating commit cabb496d6e189c94fb204378c59b3ab1821db643 Author: Stefan Kangas Date: Fri Oct 28 22:30:31 2022 +0200 Remove redundant requires from make-mode.el * lisp/progmodes/make-mode.el (imenu, dabbrev, add-log): Remove redundant requires. diff --git a/lisp/progmodes/make-mode.el b/lisp/progmodes/make-mode.el index 5e9a68805d..9b9bb0e9d4 100644 --- a/lisp/progmodes/make-mode.el +++ b/lisp/progmodes/make-mode.el @@ -81,12 +81,6 @@ ;; * Update texinfo manual. ;; * Update files.el. -;; Sadly we need this for a macro. -(eval-when-compile - (require 'imenu) - (require 'dabbrev) - (require 'add-log)) - (require 'subr-x) ; `string-limit' ;;; ------------------------------------------------------------ commit 44d2d23ffe60c1d2dd9df8885537c65776d3744f Author: Stefan Kangas Date: Fri Oct 28 22:20:56 2022 +0200 Use new sit-for calling convention in zone.el * lisp/play/zone.el (zone-exploding-remove): Don't use obsolete calling convention for 'sit-for'. diff --git a/lisp/play/zone.el b/lisp/play/zone.el index e3a9507f1c..1e8e0cbab5 100644 --- a/lisp/play/zone.el +++ b/lisp/play/zone.el @@ -312,7 +312,7 @@ run a specific program. The program must be a member of (insert " "))) (forward-char 1))) (setq i (1+ i)) - (sit-for 0 2))) + (sit-for 0.002))) (zone-pgm-jitter)) (defun zone-pgm-explode () commit 4fc5670a72e96f57ec27aac90df66489b84c7dc8 Author: Stefan Kangas Date: Fri Oct 28 02:36:04 2022 +0200 Minor doc fixes in make-mode.el * lisp/progmodes/make-mode.el: (makefile-insert-macro, makefile-pickup-everything) (makefile-backslash-region): Minor doc fixes. (makefile-browse): Use substitute-command-keys. diff --git a/lisp/progmodes/make-mode.el b/lisp/progmodes/make-mode.el index 1ddb001dd8..5e9a68805d 100644 --- a/lisp/progmodes/make-mode.el +++ b/lisp/progmodes/make-mode.el @@ -1,6 +1,6 @@ ;;; make-mode.el --- makefile editing commands for Emacs -*- lexical-binding:t -*- -;; Copyright (C) 1992, 1994, 1999-2022 Free Software Foundation, Inc. +;; Copyright (C) 1992-2022 Free Software Foundation, Inc. ;; Author: Thomas Neumann ;; Eric S. Raymond @@ -37,7 +37,7 @@ ;; ;; The command C-c C-f adds certain filenames in the current directory ;; as targets. You can filter out filenames by setting the variable -;; makefile-ignored-files-in-pickup-regex. +;; `makefile-ignored-files-in-pickup-regex'. ;; ;; The command C-c C-u grinds for a bit, then pops up a report buffer ;; showing which target names are up-to-date with respect to their @@ -49,11 +49,14 @@ ;; all marked items back in the Makefile with C-c TAB. ;; ;; The command C-c TAB in the makefile buffer inserts a GNU make builtin. -;; You will be prompted for the builtin's args. +;; You will be prompted for the builtin's arguments. ;; ;; There are numerous other customization variables. -;; + + +;;; Code: + ;; To Do: ;; ;; * Add missing doc strings, improve terse doc strings. @@ -65,7 +68,7 @@ ;; indentation and slashification done automatically. Hard. ;; * Consider removing browser mode. It seems useless. ;; * ":" should notice when a new target is made and add it to the -;; list (or at least set makefile-need-target-pickup). +;; list (or at least set `makefile-need-target-pickup'). ;; * Make browser into a major mode. ;; * Clean up macro insertion stuff. It is a mess. ;; * Browser entry and exit is weird. Normalize. @@ -78,10 +81,6 @@ ;; * Update texinfo manual. ;; * Update files.el. - - -;;; Code: - ;; Sadly we need this for a macro. (eval-when-compile (require 'imenu) @@ -998,7 +997,8 @@ Anywhere else just self-inserts." (self-insert-command arg))) (defun makefile-insert-macro (macro-name) - "Prepare definition of a new macro." + "Prepare definition of a new macro named MACRO-NAME. +Interactively, prompt for the name of the macro." (interactive "sMacro Name: ") (makefile-pickup-macros) (if (not (zerop (length macro-name))) @@ -1078,7 +1078,7 @@ Anywhere else just self-inserts." (skip-chars-forward " \t") (not (or (eolp) (eq (char-after) ?:))))) (forward-line))) - (message "Read targets OK."))) + (message "Read targets OK"))) (defun makefile-pickup-macros () "Notice names of all macro definitions in Makefile." @@ -1098,11 +1098,11 @@ Anywhere else just self-inserts." (message "Picked up macro \"%s\" from line %d" macro-name (line-number-at-pos)))) (forward-line))) - (message "Read macros OK."))) + (message "Read macros OK"))) (defun makefile-pickup-everything (arg) "Notice names of all macros and targets in Makefile. -Prefix arg means force pickups to be redone." +Prefix argument ARG means force pickups to be redone." (interactive "P") (if arg (setq makefile-need-target-pickup t @@ -1170,8 +1170,8 @@ and adds all qualifying names to the list of known targets." (defun makefile-backslash-region (from to delete-flag) "Insert, align, or delete end-of-line backslashes on the lines in the region. -With no argument, inserts backslashes and aligns existing backslashes. -With an argument, deletes the backslashes. +With no argument, insert backslashes and align existing backslashes. +With an argument, delete the backslashes. This function does not modify the last line of the region if the region ends right at the start of the following line; it does not modify blank lines @@ -1466,14 +1466,17 @@ Insertion takes place at point." (if (zerop (+ (length targets) (length macros))) (progn (beep) - (message "No macros or targets to browse! Consider running `makefile-pickup-everything'")) + (message + (substitute-command-keys + (concat "No macros or targets to browse! " + "Consider running \\[makefile-pickup-everything]")))) (let ((browser-buffer (get-buffer-create makefile-browser-buffer-name))) - (pop-to-buffer browser-buffer) - (makefile-browser-fill targets macros) - (shrink-window-if-larger-than-buffer) - (setq-local makefile-browser-selection-vector - (make-vector (+ (length targets) (length macros)) nil)) - (makefile-browser-start-interaction)))) + (pop-to-buffer browser-buffer) + (makefile-browser-fill targets macros) + (shrink-window-if-larger-than-buffer) + (setq-local makefile-browser-selection-vector + (make-vector (+ (length targets) (length macros)) nil)) + (makefile-browser-start-interaction)))) (defun makefile-switch-to-browser () (interactive) commit bf0e4d1b030d5b329df4a87bf505367a48a35a9b Author: Stefan Kangas Date: Fri Oct 28 02:33:33 2022 +0200 Prefer defvar-keymap in make-mode.el * lisp/progmodes/make-mode.el (makefile-mode-map) (makefile-browser-map): Prefer defvar-keymap. diff --git a/lisp/progmodes/make-mode.el b/lisp/progmodes/make-mode.el index 413e2a42d6..1ddb001dd8 100644 --- a/lisp/progmodes/make-mode.el +++ b/lisp/progmodes/make-mode.el @@ -568,34 +568,31 @@ The function must satisfy this calling convention: (define-abbrev-table 'makefile-mode-abbrev-table () "Abbrev table in use in Makefile buffers.") -(defvar makefile-mode-map - (let ((map (make-sparse-keymap))) - ;; set up the keymap - (define-key map "\C-c:" 'makefile-insert-target-ref) - (if makefile-electric-keys - (progn - (define-key map "$" 'makefile-insert-macro-ref) - (define-key map ":" 'makefile-electric-colon) - (define-key map "=" 'makefile-electric-equal) - (define-key map "." 'makefile-electric-dot))) - (define-key map "\C-c\C-f" 'makefile-pickup-filenames-as-targets) - (define-key map "\C-c\C-b" 'makefile-switch-to-browser) - (define-key map "\C-c\C-c" 'comment-region) - (define-key map "\C-c\C-p" 'makefile-pickup-everything) - (define-key map "\C-c\C-u" 'makefile-create-up-to-date-overview) - (define-key map "\C-c\C-i" 'makefile-insert-gmake-function) - (define-key map "\C-c\C-\\" 'makefile-backslash-region) - (define-key map "\C-c\C-m\C-a" 'makefile-automake-mode) - (define-key map "\C-c\C-m\C-b" 'makefile-bsdmake-mode) - (define-key map "\C-c\C-m\C-g" 'makefile-gmake-mode) - (define-key map "\C-c\C-m\C-i" 'makefile-imake-mode) - (define-key map "\C-c\C-m\C-m" 'makefile-mode) - (define-key map "\C-c\C-m\C-p" 'makefile-makepp-mode) - (define-key map "\M-p" 'makefile-previous-dependency) - (define-key map "\M-n" 'makefile-next-dependency) - (define-key map "\e\t" 'completion-at-point) - map) - "The keymap that is used in Makefile mode.") +(defvar-keymap makefile-mode-map + :doc "The keymap that is used in Makefile mode." + "C-c :" #'makefile-insert-target-ref + "C-c C-f" #'makefile-pickup-filenames-as-targets + "C-c C-b" #'makefile-switch-to-browser + "C-c C-c" #'comment-region + "C-c C-p" #'makefile-pickup-everything + "C-c C-u" #'makefile-create-up-to-date-overview + "C-c TAB" #'makefile-insert-gmake-function + "C-c C-\\" #'makefile-backslash-region + "C-c RET C-a" #'makefile-automake-mode + "C-c RET C-b" #'makefile-bsdmake-mode + "C-c RET C-g" #'makefile-gmake-mode + "C-c RET TAB" #'makefile-imake-mode + "C-c RET RET" #'makefile-mode + "C-c RET C-p" #'makefile-makepp-mode + "M-p" #'makefile-previous-dependency + "M-n" #'makefile-next-dependency + "C-M-i" #'completion-at-point) + +(when makefile-electric-keys + (define-key makefile-mode-map "$" #'makefile-insert-macro-ref) + (define-key makefile-mode-map ":" #'makefile-electric-colon) + (define-key makefile-mode-map "=" #'makefile-electric-equal) + (define-key makefile-mode-map "." #'makefile-electric-dot)) (easy-menu-define makefile-mode-menu makefile-mode-map "Menu for Makefile mode." @@ -651,22 +648,20 @@ The function must satisfy this calling convention: :selected (eq major-mode 'makefile-makepp-mode)]))) -(defvar makefile-browser-map - (let ((map (make-sparse-keymap))) - (define-key map "n" 'makefile-browser-next-line) - (define-key map "\C-n" 'makefile-browser-next-line) - (define-key map "p" 'makefile-browser-previous-line) - (define-key map "\C-p" 'makefile-browser-previous-line) - (define-key map " " 'makefile-browser-toggle) - (define-key map "i" 'makefile-browser-insert-selection) - (define-key map "I" 'makefile-browser-insert-selection-and-quit) - (define-key map "\C-c\C-m" 'makefile-browser-insert-continuation) - (define-key map "q" 'makefile-browser-quit) - ;; disable horizontal movement - (define-key map "\C-b" 'undefined) - (define-key map "\C-f" 'undefined) - map) - "The keymap that is used in the macro- and target browser.") +(defvar-keymap makefile-browser-map + :doc "The keymap that is used in the macro- and target browser." + "n" #'makefile-browser-next-line + "C-n" #'makefile-browser-next-line + "p" #'makefile-browser-previous-line + "C-p" #'makefile-browser-previous-line + "SPC" #'makefile-browser-toggle + "i" #'makefile-browser-insert-selection + "I" #'makefile-browser-insert-selection-and-quit + "C-c RET" #'makefile-browser-insert-continuation + "q" #'makefile-browser-quit + ;; disable horizontal movement + "C-b" #'undefined + "C-f" #'undefined) (defvar makefile-mode-syntax-table commit 6f42782a57c6a37e1f30535dde829053f1005389 Author: Stefan Kangas Date: Fri Oct 28 02:17:58 2022 +0200 Prefer make-temp-name in make-mode.el * lisp/progmodes/make-mode.el (makefile-generate-temporary-filename): Make obsolete in favor of 'make-temp-name'. Simplify. (makefile-save-temporary): Don't use above obsolete function. diff --git a/lisp/progmodes/make-mode.el b/lisp/progmodes/make-mode.el index 92d8a739a5..413e2a42d6 100644 --- a/lisp/progmodes/make-mode.el +++ b/lisp/progmodes/make-mode.el @@ -88,6 +88,8 @@ (require 'dabbrev) (require 'add-log)) +(require 'subr-x) ; `string-limit' + ;;; ------------------------------------------------------------ ;;; Configurable stuff ;;; ------------------------------------------------------------ @@ -1539,24 +1541,9 @@ dependency in the makefile." (defun makefile-save-temporary () "Create a temporary file from the current makefile buffer." - (let ((filename (makefile-generate-temporary-filename))) + (let ((filename (make-temp-name "mktmp."))) (write-region (point-min) (point-max) filename nil 0) - filename)) ; return the filename - -(defun makefile-generate-temporary-filename () - "Create a filename suitable for use in `makefile-save-temporary'. -Be careful to allow brain-dead file systems (DOS, SYSV ...) to cope -with the generated name!" - (let ((my-name (user-login-name)) - (my-uid (int-to-string (user-uid)))) - (concat "mktmp" - (if (> (length my-name) 3) - (substring my-name 0 3) - my-name) - "." - (if (> (length my-uid) 3) - (substring my-uid 0 3) - my-uid)))) + filename)) (defun makefile-query-targets (filename target-table prereq-list) "Fill the up-to-date overview buffer. @@ -1823,6 +1810,13 @@ If it isn't in one, return nil." (setq found (replace-regexp-in-string "\\`[ \t]+" "" found))) found))) +(defun makefile-generate-temporary-filename () + "Create a filename suitable for use in `makefile-save-temporary'." + (declare (obsolete make-temp-name "29.1")) + (format "mktmp%s.%s" + (string-limit (user-login-name) 3) + (string-limit (int-to-string (user-uid)) 3))) + (provide 'make-mode) ;;; make-mode.el ends here commit dee3d93e73b2bb9b6805084fb784a536960c42e6 Author: Stefan Kangas Date: Fri Oct 28 02:26:44 2022 +0200 Ensure temp file is deleted in makefile overview * lisp/progmodes/make-mode.el (makefile-create-up-to-date-overview): Ensure temp file is always deleted. diff --git a/lisp/progmodes/make-mode.el b/lisp/progmodes/make-mode.el index 5f26521299..92d8a739a5 100644 --- a/lisp/progmodes/make-mode.el +++ b/lisp/progmodes/make-mode.el @@ -1504,39 +1504,38 @@ dependency in the makefile." ;; writing the current contents of the makefile buffer. ;; (let ((saved-target-table makefile-target-table) - (this-buffer (current-buffer)) - (makefile-up-to-date-buffer - (get-buffer-create makefile-up-to-date-buffer-name)) - (filename (makefile-save-temporary)) - ;; - ;; Forget the target table because it may contain picked-up filenames - ;; that are not really targets in the current makefile. - ;; We don't want to query these, so get a new target-table with just the - ;; targets that can be found in the makefile buffer. - ;; The 'old' target table will be restored later. - ;; - (real-targets (progn - (makefile-pickup-targets) - makefile-target-table)) - (prereqs makefile-has-prereqs) - ) - - (set-buffer makefile-up-to-date-buffer) - (setq buffer-read-only nil) - (erase-buffer) - (makefile-query-targets filename real-targets prereqs) - (if (zerop (buffer-size)) ; if it did not get us anything - (progn - (kill-buffer (current-buffer)) - (message "No overview created!"))) - (set-buffer this-buffer) - (setq makefile-target-table saved-target-table) - (if (get-buffer makefile-up-to-date-buffer-name) - (progn - (pop-to-buffer (get-buffer makefile-up-to-date-buffer-name)) - (shrink-window-if-larger-than-buffer) - (sort-lines nil (point-min) (point-max)) - (setq buffer-read-only t)))))) + (this-buffer (current-buffer)) + (makefile-up-to-date-buffer + (get-buffer-create makefile-up-to-date-buffer-name)) + (filename (makefile-save-temporary)) + ;; + ;; Forget the target table because it may contain picked-up filenames + ;; that are not really targets in the current makefile. + ;; We don't want to query these, so get a new target-table with just the + ;; targets that can be found in the makefile buffer. + ;; The 'old' target table will be restored later. + ;; + (real-targets (progn + (makefile-pickup-targets) + makefile-target-table)) + (prereqs makefile-has-prereqs)) + (unwind-protect + (progn + (set-buffer makefile-up-to-date-buffer) + (setq buffer-read-only nil) + (erase-buffer) + (makefile-query-targets filename real-targets prereqs) + (when (zerop (buffer-size)) ; if it did not get us anything + (kill-buffer (current-buffer)) + (message "No overview created!")) + (set-buffer this-buffer) + (setq makefile-target-table saved-target-table) + (when (get-buffer makefile-up-to-date-buffer-name) + (pop-to-buffer (get-buffer makefile-up-to-date-buffer-name)) + (shrink-window-if-larger-than-buffer) + (sort-lines nil (point-min) (point-max)) + (setq buffer-read-only t))) + (ignore-errors (delete-file filename)))))) (defun makefile-save-temporary () "Create a temporary file from the current makefile buffer." commit 943f95e03c4b9d790267c71b1b9addf55b1c4802 Merge: 27b817ad32 510020d30d Author: Stefan Kangas Date: Fri Oct 28 21:51:44 2022 +0200 Merge from origin/emacs-28 510020d30d ; Improve doc strings in whitespace.el # Conflicts: # lisp/whitespace.el commit 27b817ad327d6c51286e19c27f001736eb9a4087 Author: Eli Zaretskii Date: Fri Oct 28 22:06:31 2022 +0300 ; * doc/lispref/frames.texi (Drag and Drop): Fix typos and wording. diff --git a/doc/lispref/frames.texi b/doc/lispref/frames.texi index b4cd611bff..b3f1a29ae8 100644 --- a/doc/lispref/frames.texi +++ b/doc/lispref/frames.texi @@ -4078,7 +4078,7 @@ performed for the drop, which is one of the symbols @code{copy}, If @var{action} is @var{private}, then it means the program that initiated the drop wants Emacs to perform an unspecified action with -the URL; a reasonable action to perform that case is to open the URL +the URL; a reasonable action to perform in that case is to open the URL or copy its contents into the current buffer. Otherwise, @var{action} has the same meaning as the @var{action} argument to @code{dnd-begin-file-drag}. @@ -4100,13 +4100,13 @@ the variable @code{x-dnd-test-function} is called by Emacs to determine what to reply to any such inquiry. The default value is @code{x-dnd-default-test-function}, which accepts drops if the type of the data to be dropped is present in @code{x-dnd-known-types}. -Changing the the variables @code{x-dnd-test-function} and +Changing the variables @code{x-dnd-test-function} and @code{x-dnd-known-types} can make Emacs accept or reject drops based on some other criteria. @vindex x-dnd-types-alist If you want to change the way Emacs receives drops of different data -types, or enable it to understand a new type, change the variable +types, or you want to enable it to understand a new type, change the variable @code{x-dnd-types-alist}. Doing so correctly requires detailed knowledge of what data types other applications use for drag and drop. @@ -4119,14 +4119,20 @@ example, the data type used for plain text may be either @code{"STRING"} or @code{"text/plain"}. @vindex x-dnd-direct-save-function +@c FIXME: This description is overly-complicated and confusing. In +@c particular, the two calls to the function basically sound +@c identical, so it is unclear how should the function distinguish +@c between the first and the second one. The description of who asks +@c whom to do what is also very hard to understand. Needs rewording, +@c and needs shorter sentences. Perhaps examples could help. However, @code{x-dnd-types-alist} does not handle a special kind of drop sent by a program that wants Emacs to tell it where to save a file in a specific location determined by the user. These drops are -instead handled by a function set as the value of the variable -@code{x-dnd-direct-save-function}, which should accept two arguments. +instead handled by a function that is the value of the variable +@code{x-dnd-direct-save-function}. This function should accept two arguments. If the first argument is non-@code{nil}, then the second argument is a -string describing the name (with no directory name) that the other -program recommends that the file should be saved under, and the +file name to save (with leading directories) that the other +program recommends, and the function should return the full file name under which it should be saved. After the function completes, Emacs will ask the other program to save the file under the name that was returned, and if the file was commit 622724e95d6bcf858c52922e7d85423c8337bc06 Author: Alan Mackenzie Date: Fri Oct 28 17:50:33 2022 +0000 CC Mode: Fontify cast types without adding them to c-found-types * lisp/progmodes/cc-engine.el (c-forward-type): Test for the special new value `just-one' of c-promote-possible-types, and if found, fontify the type, but don't add it to c-found-types. (c-forward-decl-or-cast-1): Add the new &optional parameter inside-macro. Whilst checking for a cast construct, analyze the text following the closing paren more rigorously. Check for, and allow, the closing paren of a macro arglist before the putative cast construct. * lisp/progmodes/cc-fonts.el (c-font-lock-declarations): In the lambda function, pass the parameter inside-macro to c-forward-decl-or-cast-1. * lisp/progmodes/cc-langs.el (c-primary-expr-regexp-details): New c-lang-defvar which calculates `c-primary-expr-regexp' and three match numbers for various sub-expressions in the regexp. (c-primary-expr-regexp): Now extracted from `c-primary-expr-regexp-details'. (c-per-++---match, c-per-&*+--match, c-per-\(-match): New c-lang-defconsts/vars extracted from `c-primary-expr-regexp-details'. diff --git a/lisp/progmodes/cc-engine.el b/lisp/progmodes/cc-engine.el index ed06807a87..d730fddeb0 100644 --- a/lisp/progmodes/cc-engine.el +++ b/lisp/progmodes/cc-engine.el @@ -8194,7 +8194,8 @@ multi-line strings (but not C++, for example)." ;; treat possible types (i.e. those that it normally returns 'maybe or ;; 'found for) as actual types (and always return 'found for them). ;; This means that it records them in `c-record-type-identifiers' if -;; that is set, and that it adds them to `c-found-types'. +;; that is set, and that if its value is t (not 'just-one), it adds +;; them to `c-found-types'. (defvar c-promote-possible-types nil) ;; Dynamically bound variable that instructs `c-forward-<>-arglist' to @@ -9191,10 +9192,11 @@ multi-line strings (but not C++, for example)." (goto-char id-end) (if (or res c-promote-possible-types) (progn - (c-add-type id-start (save-excursion - (goto-char id-end) - (c-backward-syntactic-ws) - (point))) + (when (not (eq c-promote-possible-types 'just-one)) + (c-add-type id-start (save-excursion + (goto-char id-end) + (c-backward-syntactic-ws) + (point)))) (when (and c-record-type-identifiers id-range) (c-record-type-id id-range)) (unless res @@ -10029,7 +10031,8 @@ This function might do hidden buffer changes." ;; This identifier is bound only in the inner let. '(setq start id-start)))) -(defun c-forward-decl-or-cast-1 (preceding-token-end context last-cast-end) +(defun c-forward-decl-or-cast-1 (preceding-token-end context last-cast-end + &optional inside-macro) ;; Move forward over a declaration or a cast if at the start of one. ;; The point is assumed to be at the start of some token. Nil is ;; returned if no declaration or cast is recognized, and the point @@ -10118,6 +10121,10 @@ This function might do hidden buffer changes." ;; matched. In that case it's used to discover chains of casts like ;; "(a) (b) c". ;; + ;; INSIDE-MACRO is t when we definitely know we're inside a macro, nil + ;; otherwise. We use it to disambiguate things like "(a) (b);", which is + ;; likely a function call in a macro, but a cast outside of one. + ;; ;; This function records identifier ranges on ;; `c-record-type-identifiers' and `c-record-ref-identifiers' if ;; `c-record-type-identifiers' is non-nil. @@ -11102,11 +11109,17 @@ This function might do hidden buffer changes." ;; Check if the expression begins with a prefix keyword. (match-beginning 2) (if (match-beginning 1) - ;; Expression begins with an ambiguous operator. Treat - ;; it as a cast if it's a type decl or if we've - ;; recognized the type somewhere else. - (or at-decl-or-cast - (memq at-type '(t known found))) + ;; Expression begins with an ambiguous operator. + (cond + ((match-beginning c-per-&*+--match) + (memq at-type '(t known found))) + ((match-beginning c-per-++---match) + t) + ((match-beginning c-per-\(-match) + (or + (memq at-type '(t known found)) + (not inside-macro))) + (t nil)) ;; Unless it's a keyword, it's the beginning of a primary ;; expression. (not (looking-at c-keywords-regexp))))) @@ -11132,18 +11145,33 @@ This function might do hidden buffer changes." ;; surrounding parens). (looking-at c-simple-stmt-key) (and - ;; Check that it isn't a close paren (block close is ok, - ;; though). - (not (memq (char-before) '(?\) ?\]))) + ;; Check that it isn't a close paren (block close , or a + ;; macro arglist is ok, though). + (or + (not (memq (char-before) '(?\) ?\]))) + ;; Have we moved back to a macro arglist? + (and c-opt-cpp-prefix + (eq (char-before) ?\)) + (save-excursion + (and + (c-go-list-backward) + (let (pos) + (c-backward-syntactic-ws) + (and (setq pos (c-on-identifier)) + (goto-char pos))) + (zerop (c-backward-token-2 2)) + (looking-at c-opt-cpp-macro-define-start))))) + ;; Check that it isn't a nonsymbol identifier. (not (c-on-identifier))))))))) ;; Handle the cast. (when (and c-record-type-identifiers at-type - (not (memq at-type '(t maybe)))) ; 'maybe isn't strong enough - ; evidence to promote the type. - (let ((c-promote-possible-types t)) + (not (eq at-type t))) + (let ((c-promote-possible-types (if (eq at-type 'maybe) + 'just-one + t))) (goto-char type-start) (c-forward-type))) diff --git a/lisp/progmodes/cc-fonts.el b/lisp/progmodes/cc-fonts.el index 5bb3e2e0b4..608919d0c9 100644 --- a/lisp/progmodes/cc-fonts.el +++ b/lisp/progmodes/cc-fonts.el @@ -1585,7 +1585,7 @@ casts and declarations are fontified. Used on level 2 and higher." nil) (setq decl-or-cast (c-forward-decl-or-cast-1 - match-pos context last-cast-end)) + match-pos context last-cast-end inside-macro)) ;; Ensure that c-<>-arg-sep c-type properties are in place on the ;; commas separating the arguments inside template/generic <..>s. diff --git a/lisp/progmodes/cc-langs.el b/lisp/progmodes/cc-langs.el index fc3977967b..291af038b7 100644 --- a/lisp/progmodes/cc-langs.el +++ b/lisp/progmodes/cc-langs.el @@ -3489,76 +3489,179 @@ Note that Java specific rules are currently applied to tell this from (c-lang-defvar c-regular-keywords-regexp (c-lang-const c-regular-keywords-regexp)) -(c-lang-defconst c-primary-expr-regexp - ;; Regexp matching the start of any primary expression, i.e. any - ;; literal, symbol, prefix operator, and '('. It doesn't need to - ;; exclude keywords; they are excluded afterwards unless the second - ;; submatch matches. If the first but not the second submatch - ;; matches then it is an ambiguous primary expression; it could also - ;; be a match of e.g. an infix operator. (The case with ambiguous - ;; keyword operators isn't handled.) - +(c-lang-defconst c-primary-expr-regexp-details + ;; A list of c-primary-expr-regexp and three numbers identifying particular + ;; matches in it. t (let* ((prefix-ops + ;All prefix ops (c-filter-ops (c-lang-const c-operators) '(prefix) (lambda (op) ;; Filter out the special case prefix ;; operators that are close parens. (not (string-match "\\s)" op))))) - - (nonkeyword-prefix-ops - (c-filter-ops prefix-ops - t - "\\`\\(\\s.\\|\\s(\\|\\s)\\)+\\'")) + (postfix-ops + ;; All postfix ops. + (c-filter-ops (c-lang-const c-operators) + '(postfix) + (lambda (op) (not (string-match "\\s)" op))))) (in-or-postfix-ops + ;; All ops which are postfix, etc. (c-filter-ops (c-lang-const c-operators) '(postfix postfix-if-paren left-assoc right-assoc right-assoc-sequence) - t))) + t)) - (concat - "\\(" - ;; Take out all symbol class operators from `prefix-ops' and make the - ;; first submatch from them together with `c-primary-expr-kwds'. - (c-make-keywords-re t - (append (c-lang-const c-primary-expr-kwds) - (c--set-difference prefix-ops nonkeyword-prefix-ops - :test 'string-equal))) - - "\\|" - ;; Match all ambiguous operators. - (c-make-keywords-re nil - (c--intersection nonkeyword-prefix-ops in-or-postfix-ops - :test 'string-equal)) - "\\)" - - "\\|" - ;; Now match all other symbols. - (c-lang-const c-symbol-start) - - "\\|" - ;; The chars that can start integer and floating point - ;; constants. - "\\.?[0-9]" - - "\\|" - ;; The unambiguous operators from `prefix-ops'. - (c-make-keywords-re nil - (c--set-difference nonkeyword-prefix-ops in-or-postfix-ops - :test 'string-equal)) - - "\\|" - ;; Match string and character literals. - "\\s\"" - (if (memq 'gen-string-delim c-emacs-features) - "\\|\\s|" - "")))) + (nonkeyword-prefix-ops + ;; All prefix ops apart from those which are keywords. + (c-filter-ops prefix-ops + t + "\\`\\(\\s.\\|\\s(\\|\\s)\\)+\\'")) + (nonkeyword-postfix-ops + ;; All postfix ops apart from those which are keywords. + (c-filter-ops postfix-ops + t + "\\`\\(\\s.\\|\\s(\\|\\s)\\)+\\'")) + + (cast-ops + ;; All prefix ops which have syntax open-paren. + (c-filter-ops prefix-ops + t + "\\`\\s(\\'")) + + (ambiguous-pre/postfix-ops + ;; All non-keyword ops which are both prefix and postfix, apart + ;; from (. + (c--set-difference (c--intersection nonkeyword-prefix-ops + nonkeyword-postfix-ops + :test 'string-equal) + cast-ops :test 'string-equal)) + (unambiguous-prefix-ops + ;; All non-keyword ops which are prefix ops and not any other type + ;; of op. + (c--set-difference nonkeyword-prefix-ops + in-or-postfix-ops + :test 'string-equal)) + (ambiguous-prefix-ops + ;; All non-keyword ops which are prefix ops and also some other + ;; type of op. + (c--intersection nonkeyword-prefix-ops + in-or-postfix-ops + :test 'string-equal)) ; This has everything we + ; need, plus (, ++, --. + + (ambiguous-prefix-non-postfix-ops + ;; All non-keyword prefix ops which are also other types of ops + ;; apart from postfix ops. + (c--set-difference (c--set-difference ambiguous-prefix-ops + ambiguous-pre/postfix-ops + :test 'string-equal) + cast-ops :test 'string-equal)) + + (primary-expression-keywords-string + ;; Take out all symbol class operators from `prefix-ops' and make + ;; the first submatch from them together with + ;; `c-primary-expr-kwds'. + (c-make-keywords-re t + (append (c-lang-const c-primary-expr-kwds) + (c--set-difference prefix-ops nonkeyword-prefix-ops + :test 'string-equal)))) + (primary-expression-keywords-string-depth + (regexp-opt-depth primary-expression-keywords-string)) + + (ambiguous-pre/postfix-string + (c-make-keywords-re nil ambiguous-pre/postfix-ops)) + (ambiguous-pre/postfix-string-depth + (regexp-opt-depth ambiguous-pre/postfix-string)) + + (ambiguous-prefix-non-postfix-string + (c-make-keywords-re nil ambiguous-prefix-non-postfix-ops)) + (ambiguous-prefix-non-postfix-string-depth + (regexp-opt-depth ambiguous-prefix-non-postfix-string)) + + (per-++---match (+ 2 primary-expression-keywords-string-depth)) + (per-&*+--match (+ 1 per-++---match + ambiguous-pre/postfix-string-depth)) + (per-\(-match (+ 1 per-&*+--match + ambiguous-prefix-non-postfix-string-depth))) + + (list + (concat + "\\(" ; 1 + primary-expression-keywords-string + "\\|" + ;; Match all ambiguous operators. + "\\(" ; 2 + primary-expression-keywords-string-depth + ambiguous-pre/postfix-string + "\\)\\|\\(" ; 3 + primary-expression-keywords-string-depth + ; + ambiguous-pre/postfix-string-depth + ambiguous-prefix-non-postfix-string + "\\)\\|" + "\\((\\)" ; 4 + primary-expression-keywords-string-depth + ; + ambiguous-pre/postfix-string-depth + ; + ambiguous-prefix-non-postfix-string-depth + "\\)" + + "\\|" + ;; Now match all other symbols. + (c-lang-const c-symbol-start) + + "\\|" + ;; The chars that can start integer and floating point + ;; constants. + "\\.?[0-9]" + + "\\|" + ;; The unambiguous operators from `prefix-ops'. + (c-make-keywords-re nil + ;; (c--set-difference nonkeyword-prefix-ops in-or-postfix-ops + ;; :test 'string-equal) + unambiguous-prefix-ops + ) + + "\\|" + ;; Match string and character literals. + "\\s\"" + (if (memq 'gen-string-delim c-emacs-features) + "\\|\\s|" + "")) + per-++---match + per-&*+--match + per-\(-match))) + +(c-lang-defconst c-primary-expr-regexp + ;; Regexp matching the start of any primary expression, i.e. any + ;; literal, symbol, prefix operator, and '('. It doesn't need to + ;; exclude keywords; they are excluded afterwards unless the second + ;; submatch matches. If the first but not the second submatch + ;; matches then it is an ambiguous primary expression; it could also + ;; be a match of e.g. an infix operator. (The case with ambiguous + ;; keyword operators isn't handled.) + t (car (c-lang-const c-primary-expr-regexp-details))) (c-lang-defvar c-primary-expr-regexp (c-lang-const c-primary-expr-regexp)) +(c-lang-defconst c-per-++---match + ;; Match number for group in `c-primary-expr-regexp' which matches (in C) + ;; the ++ and -- operators, and any similar ones in other languages. + t (cadr (c-lang-const c-primary-expr-regexp-details))) +(c-lang-defvar c-per-++---match (c-lang-const c-per-++---match)) + +(c-lang-defconst c-per-&*+--match + ;; Match number for group in `c-primary-expr-regexp' which matches (in C) + ;; the &, *, +, and - operators, and any similar ones in other languages. + t (car (cddr (c-lang-const c-primary-expr-regexp-details)))) +(c-lang-defvar c-per-&*+--match (c-lang-const c-per-&*+--match)) + +(c-lang-defconst c-per-\(-match + ;; Match number for group in `c-primary-expr-regexp' which matches (in C) + ;; the ( operator, and any similar ones in other languages. + t (cadr (cddr (c-lang-const c-primary-expr-regexp-details)))) +(c-lang-defvar c-per-\(-match (c-lang-const c-per-\(-match)) + ;;; Additional constants for parser-level constructs. commit d79cdcd4ff6687c2f0dcfde83ba36732408e52e8 Author: Stefan Monnier Date: Fri Oct 28 11:33:24 2022 -0400 cconv.el: Fix regression in cconv-tests-interactive-closure-bug51695 The new code to make interpreted closures safe-for-space introduced a regression in `cconv-tests-interactive-closure-bug51695`, only seen when using TEST_LOAD_EL. A few other issues were found and fixed along the way. * lisp/emacs-lisp/cconv.el (cconv-fv): Change calling convention and focus on finding the free variables. (cconv-make-interpreted-closure): New function. * lisp/loadup.el: Use `compiled-function-p` rather than `byte-code-function-p` so we also use safe-for-space interpreted closures when we build with native compilation. (internal-make-interpreted-closure-function): Use `cconv-make-interpreted-closure`. * src/eval.c (syms_of_eval): Rename `internal-filter-closure-env-function` to `internal-make-interpreted-closure-function`. (Ffunction): Let that new var build the actual closure. * test/lisp/emacs-lisp/cconv-tests.el (cconv-tests-interactive-closure-bug51695): Test specifically the interpreted case. diff --git a/lisp/emacs-lisp/cconv.el b/lisp/emacs-lisp/cconv.el index 289e2b0eee..f3431db415 100644 --- a/lisp/emacs-lisp/cconv.el +++ b/lisp/emacs-lisp/cconv.el @@ -828,49 +828,78 @@ This function does not return anything but instead fills the (setf (nth 1 dv) t)))))) (define-obsolete-function-alias 'cconv-analyse-form #'cconv-analyze-form "25.1") -(defun cconv-fv (form env &optional no-macroexpand) +(defun cconv-fv (form lexvars dynvars) "Return the list of free variables in FORM. -ENV is the lexical environment from which the variables can be taken. -It should be a list of pairs of the form (VAR . VAL). -The return value is a list of those (VAR . VAL) bindings, -in the same order as they appear in ENV. -If NO-MACROEXPAND is non-nil, we do not macro-expand FORM, -which means that the result may be incorrect if there are non-expanded -macro calls in FORM." - (let* ((fun `#'(lambda () ,form)) - ;; Make dummy bindings to avoid warnings about the var being - ;; left uninitialized. - (analysis-env - (delq nil (mapcar (lambda (b) (if (consp b) - (list (car b) nil nil nil nil))) - env))) - (cconv--dynbound-variables - (delq nil (mapcar (lambda (b) (if (symbolp b) b)) env))) +LEXVARS is the list of statically scoped vars in the context +and DYNVARS is the list of dynamically scoped vars in the context. +Returns a pair (LEXV . DYNV) of those vars actually used by FORM." + (let* ((fun + ;; Wrap FORM into a function because the analysis code we + ;; have only computes freevars for functions. + ;; In practice FORM is always already of the form + ;; #'(lambda ...), so optimize for this case. + (if (and (eq 'function (car-safe form)) + (eq 'lambda (car-safe (cadr form))) + ;; To get correct results, FUN needs to be a "simple lambda" + ;; without nested forms that aren't part of the body. :-( + (not (assq 'interactive (cadr form))) + (not (assq ':documentation (cadr form)))) + form + `#'(lambda () ,form))) + (analysis-env (mapcar (lambda (v) (list v nil nil nil nil)) lexvars)) + (cconv--dynbound-variables dynvars) (byte-compile-lexical-variables nil) (cconv--dynbindings nil) (cconv-freevars-alist '()) (cconv-var-classification '())) - (if (null analysis-env) + (let* ((body (cddr (cadr fun)))) + ;; Analyze form - fill these variables with new information. + (cconv-analyze-form fun analysis-env) + (setq cconv-freevars-alist (nreverse cconv-freevars-alist)) + (unless (equal (if (eq :documentation (car-safe (car body))) + (cdr body) body) + (caar cconv-freevars-alist)) + (message "BOOH!\n%S\n%S" + body (caar cconv-freevars-alist))) + (cl-assert (equal (if (eq :documentation (car-safe (car body))) + (cdr body) body) + (caar cconv-freevars-alist))) + (let ((fvs (nreverse (cdar cconv-freevars-alist))) + (dyns (delq nil (mapcar (lambda (var) (car (memq var dynvars))) + (delete-dups cconv--dynbindings))))) + (cons fvs dyns))))) + +(defun cconv-make-interpreted-closure (fun env) + (cl-assert (eq (car-safe fun) 'lambda)) + (let ((lexvars (delq nil (mapcar #'car-safe env)))) + (if (null lexvars) ;; The lexical environment is empty, so there's no need to ;; look for free variables. - env - (let* ((fun (if no-macroexpand fun - (macroexpand-all fun macroexpand-all-environment))) - (body (cddr (cadr fun)))) - ;; Analyze form - fill these variables with new information. - (cconv-analyze-form fun analysis-env) - (setq cconv-freevars-alist (nreverse cconv-freevars-alist)) - (cl-assert (equal (if (eq :documentation (car-safe (car body))) - (cdr body) body) - (caar cconv-freevars-alist))) - (let ((fvs (nreverse (cdar cconv-freevars-alist))) - (dyns (mapcar (lambda (var) (car (memq var env))) - (delete-dups cconv--dynbindings)))) - (or (nconc (mapcar (lambda (fv) (assq fv env)) fvs) - (delq nil dyns)) - ;; Never return nil, since nil means to use the dynbind - ;; dialect of ELisp. - '(t))))))) + `(closure ,env . ,(cdr fun)) + ;; We could try and cache the result of the macroexpansion and + ;; `cconv-fv' analysis. Not sure it's worth the trouble. + (let* ((form `#',fun) + (expanded-form + (let ((lexical-binding t) ;; Tell macros which dialect is in use. + ;; Make the macro aware of any defvar declarations in scope. + (macroexp--dynvars + (if macroexp--dynvars + (append env macroexp--dynvars) env))) + (macroexpand-all form macroexpand-all-environment))) + ;; Since we macroexpanded the body, we may as well use that. + (expanded-fun-cdr + (pcase expanded-form + (`#'(lambda . ,cdr) cdr) + (_ (cdr fun)))) + + (dynvars (delq nil (mapcar (lambda (b) (if (symbolp b) b)) env))) + (fvs (cconv-fv expanded-form lexvars dynvars)) + (newenv (nconc (mapcar (lambda (fv) (assq fv env)) (car fvs)) + (cdr fvs)))) + ;; Never return a nil env, since nil means to use the dynbind + ;; dialect of ELisp. + `(closure ,(or newenv '(t)) . ,expanded-fun-cdr))))) + (provide 'cconv) ;;; cconv.el ends here diff --git a/lisp/loadup.el b/lisp/loadup.el index 63806ae456..2a9aff4c1f 100644 --- a/lisp/loadup.el +++ b/lisp/loadup.el @@ -367,9 +367,10 @@ (load "emacs-lisp/eldoc") (load "emacs-lisp/cconv") -(when (and (byte-code-function-p (symbol-function 'cconv-fv)) - (byte-code-function-p (symbol-function 'macroexpand-all))) - (setq internal-filter-closure-env-function #'cconv-fv)) +(when (and (compiled-function-p (symbol-function 'cconv-fv)) + (compiled-function-p (symbol-function 'macroexpand-all))) + (setq internal-make-interpreted-closure-function + #'cconv-make-interpreted-closure)) (load "cus-start") ;Late to reduce customize-rogue (needs loaddefs.el anyway) (if (not (eq system-type 'ms-dos)) (load "tooltip")) diff --git a/src/eval.c b/src/eval.c index d2cab006d1..2928a45ac1 100644 --- a/src/eval.c +++ b/src/eval.c @@ -550,15 +550,12 @@ usage: (function ARG) */) CHECK_STRING (docstring); cdr = Fcons (XCAR (cdr), Fcons (docstring, XCDR (XCDR (cdr)))); } - Lisp_Object env - = NILP (Vinternal_filter_closure_env_function) - ? Vinternal_interpreter_environment - /* FIXME: This macroexpands the body, so we should use the resulting - macroexpanded code! */ - : call2 (Vinternal_filter_closure_env_function, - Fcons (Qprogn, CONSP (cdr) ? XCDR (cdr) : cdr), - Vinternal_interpreter_environment); - return Fcons (Qclosure, Fcons (env, cdr)); + if (NILP (Vinternal_make_interpreted_closure_function)) + return Fcons (Qclosure, Fcons (Vinternal_interpreter_environment, cdr)); + else + return call2 (Vinternal_make_interpreted_closure_function, + Fcons (Qlambda, cdr), + Vinternal_interpreter_environment); } else /* Simply quote the argument. */ @@ -4361,10 +4358,10 @@ alist of active lexical bindings. */); (Just imagine if someone makes it buffer-local). */ Funintern (Qinternal_interpreter_environment, Qnil); - DEFVAR_LISP ("internal-filter-closure-env-function", - Vinternal_filter_closure_env_function, + DEFVAR_LISP ("internal-make-interpreted-closure-function", + Vinternal_make_interpreted_closure_function, doc: /* Function to filter the env when constructing a closure. */); - Vinternal_filter_closure_env_function = Qnil; + Vinternal_make_interpreted_closure_function = Qnil; Vrun_hooks = intern_c_string ("run-hooks"); staticpro (&Vrun_hooks); diff --git a/test/lisp/emacs-lisp/cconv-tests.el b/test/lisp/emacs-lisp/cconv-tests.el index 37470f863f..e666fe0a4c 100644 --- a/test/lisp/emacs-lisp/cconv-tests.el +++ b/test/lisp/emacs-lisp/cconv-tests.el @@ -351,11 +351,18 @@ (let ((f (let ((d 51695)) (lambda (data) (interactive (progn (setq d (1+ d)) (list d))) - (list (called-interactively-p 'any) data))))) - (should (equal (list (call-interactively f) - (funcall f 51695) - (call-interactively f)) - '((t 51696) (nil 51695) (t 51697)))))) + (list (called-interactively-p 'any) data)))) + (f-interp + (eval '(let ((d 51695)) + (lambda (data) + (interactive (progn (setq d (1+ d)) (list d))) + (list (called-interactively-p 'any) data))) + t))) + (dolist (f (list f f-interp)) + (should (equal (list (call-interactively f) + (funcall f 51695) + (call-interactively f)) + '((t 51696) (nil 51695) (t 51697))))))) (provide 'cconv-tests) ;;; cconv-tests.el ends here commit de5a3fa1e529810f30d461d6682762c9c5e564a4 Author: Jonas Bernoulli Date: Fri Oct 28 16:16:27 2022 +0200 * lisp/transient.el: Update to package version v0.3.7-173-g81b29ca diff --git a/lisp/transient.el b/lisp/transient.el index f7920e414f..0919c2c3ef 100644 --- a/lisp/transient.el +++ b/lisp/transient.el @@ -68,26 +68,6 @@ (defvar display-line-numbers) ; since Emacs 26.1 (defvar Man-notify-method) -(define-obsolete-function-alias 'define-transient-command - 'transient-define-prefix "Transient 0.3.0") -(define-obsolete-function-alias 'define-suffix-command - 'transient-define-suffix "Transient 0.3.0") -(define-obsolete-function-alias 'define-infix-command - 'transient-define-infix "Transient 0.3.0") -(define-obsolete-function-alias 'define-infix-argument - #'transient-define-argument "Transient 0.3.0") - -(define-obsolete-variable-alias 'transient--source-buffer - 'transient--original-buffer "Transient 0.2.0") -(define-obsolete-variable-alias 'current-transient-prefix - 'transient-current-prefix "Transient 0.3.0") -(define-obsolete-variable-alias 'current-transient-command - 'transient-current-command "Transient 0.3.0") -(define-obsolete-variable-alias 'current-transient-suffixes - 'transient-current-suffixes "Transient 0.3.0") -(define-obsolete-variable-alias 'post-transient-hook - 'transient-exit-hook "Transient 0.3.0") - (defmacro transient--with-emergency-exit (&rest body) (declare (indent defun)) `(condition-case err @@ -893,8 +873,20 @@ to the setup function: (put ',name 'transient--prefix (,(or class 'transient-prefix) :command ',name ,@slots)) (put ',name 'transient--layout - ',(cl-mapcan (lambda (s) (transient--parse-child name s)) - suffixes))))) + (list ,@(cl-mapcan (lambda (s) (transient--parse-child name s)) + suffixes)))))) + +(defmacro transient-define-groups (name &rest groups) + "Define one or more GROUPS and store them in symbol NAME. +GROUPS, defined using this macro, can be used inside the +definition of transient prefix commands, by using the symbol +NAME where a group vector is expected. GROUPS has the same +form as for `transient-define-prefix'." + (declare (debug (&define name [&rest vectorp])) + (indent defun)) + `(put ',name 'transient--layout + (list ,@(cl-mapcan (lambda (group) (transient--parse-child name group)) + groups)))) (defmacro transient-define-suffix (name arglist &rest args) "Define NAME as a transient suffix command. @@ -1000,9 +992,8 @@ example, sets a variable use `transient-define-infix' instead. (push k keys) (push v keys)))) (while (let ((arg (car args))) - (if (vectorp arg) - (setcar args (eval (cdr (backquote-process arg)))) - (and arg (symbolp arg)))) + (or (vectorp arg) + (and arg (symbolp arg)))) (push (pop args) suffixes)) (list (if (eq (car-safe class) 'quote) (cadr class) @@ -1035,17 +1026,24 @@ example, sets a variable use `transient-define-infix' instead. (when (stringp car) (setq args (plist-put args :description pop))) (while (keywordp car) - (let ((k pop)) - (if (eq k :class) - (setq class pop) - (setq args (plist-put args k pop))))) - (vector (or level transient--default-child-level) - (or class - (if (vectorp car) - 'transient-columns - 'transient-column)) - args - (cl-mapcan (lambda (s) (transient--parse-child prefix s)) spec))))) + (let ((key pop) + (val pop)) + (cond ((eq key :class) + (setq class (macroexp-quote val))) + ((or (symbolp val) + (and (listp val) (not (eq (car val) 'lambda)))) + (setq args (plist-put args key (macroexp-quote val)))) + ((setq args (plist-put args key val)))))) + (list 'vector + (or level transient--default-child-level) + (or class + (if (vectorp car) + (quote 'transient-columns) + (quote 'transient-column))) + (and args (cons 'list args)) + (cons 'list + (cl-mapcan (lambda (s) (transient--parse-child prefix s)) + spec)))))) (defun transient--parse-suffix (prefix spec) (let (level class args) @@ -1057,17 +1055,19 @@ example, sets a variable use `transient-define-infix' instead. (when (or (stringp car) (vectorp car)) (setq args (plist-put args :key pop))) - (when (or (stringp car) - (eq (car-safe car) 'lambda) - (and (symbolp car) - (not (commandp car)) - (commandp (cadr spec)))) + (cond + ((or (stringp car) + (eq (car-safe car) 'lambda)) (setq args (plist-put args :description pop))) + ((and (symbolp car) + (not (commandp car)) + (commandp (cadr spec))) + (setq args (plist-put args :description (macroexp-quote pop))))) (cond ((keywordp car) (error "Need command, got %S" car)) ((symbolp car) - (setq args (plist-put args :command pop))) + (setq args (plist-put args :command (macroexp-quote pop)))) ((and (commandp car) (not (stringp car))) (let ((cmd pop) @@ -1076,7 +1076,7 @@ example, sets a variable use `transient-define-infix' instead. (or (plist-get args :description) (plist-get args :key)))))) (defalias sym cmd) - (setq args (plist-put args :command sym)))) + (setq args (plist-put args :command (macroexp-quote sym))))) ((or (stringp car) (and car (listp car))) (let ((arg pop)) @@ -1090,11 +1090,11 @@ example, sets a variable use `transient-define-infix' instead. (setq args (plist-put args :shortarg shortarg))) (setq args (plist-put args :argument arg)))) (setq args (plist-put args :command - (intern (format "transient:%s:%s" - prefix arg)))) + (list 'quote (intern (format "transient:%s:%s" + prefix arg))))) (cond ((and car (not (keywordp car))) (setq class 'transient-option) - (setq args (plist-put args :reader pop))) + (setq args (plist-put args :reader (macroexp-quote pop)))) ((not (string-suffix-p "=" arg)) (setq class 'transient-switch)) (t @@ -1102,17 +1102,23 @@ example, sets a variable use `transient-define-infix' instead. (t (error "Needed command or argument, got %S" car))) (while (keywordp car) - (let ((k pop)) - (cl-case k - (:class (setq class pop)) - (:level (setq level pop)) - (t (setq args (plist-put args k pop))))))) + (let ((key pop) + (val pop)) + (cond ((eq key :class) (setq class val)) + ((eq key :level) (setq level val)) + ((eq (car-safe val) '\,) + (setq args (plist-put args key (cadr val)))) + ((or (symbolp val) + (and (listp val) (not (eq (car val) 'lambda)))) + (setq args (plist-put args key (macroexp-quote val)))) + ((setq args (plist-put args key val))))))) (unless (plist-get args :key) (when-let ((shortarg (plist-get args :shortarg))) (setq args (plist-put args :key shortarg)))) - (list (or level transient--default-child-level) - (or class 'transient-suffix) - args))) + (list 'list + (or level transient--default-child-level) + (macroexp-quote (or class 'transient-suffix)) + (cons 'list args)))) (defun transient--default-infix-command () (cons 'lambda @@ -1139,6 +1145,22 @@ example, sets a variable use `transient-define-infix' instead. (and (string-match "\\`\\(-[a-zA-Z]\\)\\(\\'\\|=\\)" arg) (match-string 1 arg)))) +(defun transient-parse-suffix (prefix suffix) + "Parse SUFFIX, to be added to PREFIX. +PREFIX is a prefix command, a symbol. +SUFFIX is a suffix command or a group specification (of + the same forms as expected by `transient-define-prefix'). +Intended for use in PREFIX's `:setup-children' function." + (eval (car (transient--parse-child prefix suffix)))) + +(defun transient-parse-suffixes (prefix suffixes) + "Parse SUFFIXES, to be added to PREFIX. +PREFIX is a prefix command, a symbol. +SUFFIXES is a list of suffix command or a group specification + (of the same forms as expected by `transient-define-prefix'). +Intended for use in PREFIX's `:setup-children' function." + (mapcar (apply-partially #'transient-parse-suffix prefix) suffixes)) + ;;; Edit (defun transient--insert-suffix (prefix loc suffix action &optional keep-other) @@ -1148,6 +1170,7 @@ example, sets a variable use `transient-define-infix' instead. (string suffix))) (mem (transient--layout-member loc prefix)) (elt (car mem))) + (setq suf (eval suf)) (cond ((not mem) (message "Cannot insert %S into %s; %s not found" @@ -1448,7 +1471,10 @@ probably use this instead: transient-current-prefix) (cl-find-if (lambda (obj) (eq (transient--suffix-command obj) - (or command this-command))) + ;; When `this-command' is `transient-set-level', + ;; its reader needs to know what command is being + ;; configured. + (or command this-original-command))) (or transient--suffixes transient-current-suffixes)) (when-let* ((obj (get (or command this-command) 'transient--suffix)) @@ -1555,32 +1581,39 @@ to `transient-predicate-map'. Also see `transient-base-map'.") (put 'transient-common-commands 'transient--layout - (cl-mapcan - (lambda (s) (transient--parse-child 'transient-common-commands s)) - `([:hide ,(lambda () - (and (not (memq (car (bound-and-true-p - transient--redisplay-key)) - transient--common-command-prefixes)) - (not transient-show-common-commands))) - ["Value commands" - ("C-x s " "Set" transient-set) - ("C-x C-s" "Save" transient-save) - ("C-x C-k" "Reset" transient-reset) - ("C-x p " "Previous value" transient-history-prev) - ("C-x n " "Next value" transient-history-next)] - ["Sticky commands" - ;; Like `transient-sticky-map' except that - ;; "C-g" has to be bound to a different command. - ("C-g" "Quit prefix or transient" transient-quit-one) - ("C-q" "Quit transient stack" transient-quit-all) - ("C-z" "Suspend transient stack" transient-suspend)] - ["Customize" - ("C-x t" transient-toggle-common - :description ,(lambda () - (if transient-show-common-commands - "Hide common commands" - "Show common permanently"))) - ("C-x l" "Show/hide suffixes" transient-set-level)]]))) + (list + (eval + (car (transient--parse-child + 'transient-common-commands + (vector + :hide + (lambda () + (and (not (memq + (car (bound-and-true-p transient--redisplay-key)) + transient--common-command-prefixes)) + (not transient-show-common-commands))) + (vector + "Value commands" + (list "C-x s " "Set" #'transient-set) + (list "C-x C-s" "Save" #'transient-save) + (list "C-x C-k" "Reset" #'transient-reset) + (list "C-x p " "Previous value" #'transient-history-prev) + (list "C-x n " "Next value" #'transient-history-next)) + (vector + "Sticky commands" + ;; Like `transient-sticky-map' except that + ;; "C-g" has to be bound to a different command. + (list "C-g" "Quit prefix or transient" #'transient-quit-one) + (list "C-q" "Quit transient stack" #'transient-quit-all) + (list "C-z" "Suspend transient stack" #'transient-suspend)) + (vector + "Customize" + (list "C-x t" 'transient-toggle-common :description + (lambda () + (if transient-show-common-commands + "Hide common commands" + "Show common permanently"))) + (list "C-x l" "Show/hide suffixes" #'transient-set-level)))))))) (defvar transient-popup-navigation-map (let ((map (make-sparse-keymap))) @@ -2176,7 +2209,8 @@ value. Otherwise return CHILDREN as is." ;; used to call another command ;; that also uses the minibuffer. (equal - (string-to-multibyte (this-command-keys)) + (ignore-errors + (string-to-multibyte (this-command-keys))) (format "\M-x%s\r" this-command)))))) (transient--debug 'post-command-hook "act: %s" act) (when act @@ -3669,7 +3703,14 @@ manpage, then try to jump to the correct location." (defun transient--describe-function (fn) (describe-function (if (symbolp fn) fn 'transient--anonymous-infix-argument)) - (select-window (get-buffer-window (help-buffer)))) + (unless (derived-mode-p 'help-mode) + (when-let* ((buf (get-buffer "*Help*")) + (win (or (and buf (get-buffer-window buf)) + (cl-find-if (lambda (win) + (with-current-buffer (window-buffer win) + (derived-mode-p 'help-mode))) + (window-list))))) + (select-window win)))) (defun transient--anonymous-infix-argument () "Cannot show any documentation for this anonymous infix command. commit e8b59a9bb9b9cc7352651aaf1df864876df983b7 Author: Po Lu Date: Fri Oct 28 22:22:59 2022 +0800 Improve Drag and Drop preamble in the Lisp reference manual * doc/lispref/frames.texi (Drag and Drop): Reword contents and clear up some ambiguities and redundancies. diff --git a/doc/lispref/frames.texi b/doc/lispref/frames.texi index 8db6ad0fd3..b4cd611bff 100644 --- a/doc/lispref/frames.texi +++ b/doc/lispref/frames.texi @@ -4058,70 +4058,83 @@ amount of different data types on the clipboard. When the user drops something from another application over Emacs, Emacs will try to insert any text and open any URL that was dropped. If text was dropped, then it will always be inserted at the location -of the mouse pointer when the drop happened, or saved in the kill ring -if insertion failed (which can happen if the buffer is read-only). If -it was an URL, then Emacs tries to call an appropriate handler -function by first matching the URL against regexps defined in -@code{dnd-protocol-alist}, and then against @code{browse-url-handlers} -and @code{browse-url-default-handlers}, and failing that, inserting -the URL as plain text. +of the mouse pointer where the drop happened, or saved in the kill +ring if insertion failed, which could happen if the buffer was +read-only. If a URL was dropped instead, then Emacs will first try to +call an appropriate handler function by matching the URL against +regexps defined in the variable @code{dnd-protocol-alist}, and then +against those defined in the variables @code{browse-url-handlers} and +@code{browse-url-default-handlers}. Should no suitable handler be +located, Emacs will fall back to inserting the URL as plain text. @defvar dnd-protocol-alist This variable is a list of cons cells of the form @w{@code{(@var{pattern} . @var{action})}}. @var{pattern} is a regexp that URLs are matched against after being dropped. @var{action} is a -function that is called with two arguments should a URL being dropped +function that is called with two arguments, should a URL being dropped match @var{pattern}: the URL being dropped, and the action being -performed for the drop (one of the symbols @code{copy}, @code{move}, -@code{link}, @code{private} or @code{ask}). +performed for the drop, which is one of the symbols @code{copy}, +@code{move}, @code{link}, @code{private} or @code{ask}. + +If @var{action} is @var{private}, then it means the program that +initiated the drop wants Emacs to perform an unspecified action with +the URL; a reasonable action to perform that case is to open the URL +or copy its contents into the current buffer. Otherwise, @var{action} +has the same meaning as the @var{action} argument to +@code{dnd-begin-file-drag}. @end defvar @cindex drag and drop, X @cindex drag and drop, other formats - Emacs implements drag-and-drop for text and URLs individually for -each window system, and does not by default support the dropping of -anything else. Code that wishes to support the dropping of content -types not supported by Emacs can utilize the X-specific interface -described below: + Emacs implements receiving text and URLs individually for each +window system, and does not by default support receiving other kinds +of data as drops. To support receiving other kinds of data, use the +X-specific interface described below: @vindex x-dnd-test-function @vindex x-dnd-known-types - When a user drags something from another application over Emacs on -the X Window System, that other application expects Emacs to tell it -if Emacs can handle the data that was dragged. The variable -@code{x-dnd-test-function} is used by Emacs to determine what to -reply. The default value is @code{x-dnd-default-test-function} which -accepts drops if the type of the data to be dropped is present in -@code{x-dnd-known-types}. You can customize -@code{x-dnd-test-function} and/or @code{x-dnd-known-types} if you want -Emacs to accept or reject drops based on some other criteria. + When a user drags something from another application over Emacs +under the X Window System, that other application expects Emacs to +tell it if Emacs understands the data being dragged. The function in +the variable @code{x-dnd-test-function} is called by Emacs to +determine what to reply to any such inquiry. The default value is +@code{x-dnd-default-test-function}, which accepts drops if the type of +the data to be dropped is present in @code{x-dnd-known-types}. +Changing the the variables @code{x-dnd-test-function} and +@code{x-dnd-known-types} can make Emacs accept or reject drops based +on some other criteria. @vindex x-dnd-types-alist - If you want to change the way Emacs handles drop of different types -or add a new type, customize @code{x-dnd-types-alist}. This requires -detailed knowledge of what types other applications use for drag and -drop. - - Those data types are typically implemented as special data types an -X selection provided by the other application can be converted to. -They can either be the same data types that are typically accepted by -@code{gui-set-selection}, or they can be MIME types, depending on the -specific drag-n-drop protocol being used. Plain text may be -@code{"STRING"} or @code{"text/plain"}, for example. + If you want to change the way Emacs receives drops of different data +types, or enable it to understand a new type, change the variable +@code{x-dnd-types-alist}. Doing so correctly requires detailed +knowledge of what data types other applications use for drag and drop. + + These data types are typically implemented as special data types +that can be obtained from an X selection provided by the other +application. In most cases, they are either the same data types that +are typically accepted by @code{gui-set-selection}, or MIME types, +depending on the specific drag-and-drop protocol being used. For +example, the data type used for plain text may be either +@code{"STRING"} or @code{"text/plain"}. @vindex x-dnd-direct-save-function However, @code{x-dnd-types-alist} does not handle a special kind of -drop sent by a program which wants Emacs to save a file in a location -Emacs must determine by itself. These drops are handled via the -variable @code{x-dnd-direct-save-function}, which should be a function -that accepts two arguments. If the first argument is non-@code{nil}, -then the second argument is a string describing the name (with no -leading directory) that the other program recommends the file be saved -under, and the function should return the complete file name under -which it will be saved. Otherwise, the file has already been saved, -and the second argument is the complete name of the file. The -function should then perform whatever action is appropriate (i.e., -open the file or refresh the directory listing.) +drop sent by a program that wants Emacs to tell it where to save a +file in a specific location determined by the user. These drops are +instead handled by a function set as the value of the variable +@code{x-dnd-direct-save-function}, which should accept two arguments. +If the first argument is non-@code{nil}, then the second argument is a +string describing the name (with no directory name) that the other +program recommends that the file should be saved under, and the +function should return the full file name under which it should be +saved. After the function completes, Emacs will ask the other program +to save the file under the name that was returned, and if the file was +successfully saved, call the function again with the first argument +set to a non-@code{nil} value and the second argument set to the file +name that was returned. The function should then perform whatever +action is appropriate (i.e., opening the file or refreshing a +directory listing.) @cindex initiating drag-and-drop On capable window systems, Emacs also supports dragging contents commit c0f7a276cfff36033798ab8bde651a5278c073cc Author: Stefan Monnier Date: Fri Oct 28 09:36:40 2022 -0400 * lisp/emacs-lisp/re-builder.el: Cosmetic changes (reb-valid-string): Declare it risky so the mode-line will obey its text-properties. (reb-lisp-syntax-p): No need to make it `defsubst`. (reb-target-value): New function to replace the `reb-target-binding` macro. Adjust all callers. (reb-auto-update): Give a more informative error message than just "invalid" and give it a warning face. (reb-update-regexp): Always update `reb-regexp-src` (it's harmless), rather than only when it's necessary (reduces the need for advice in pcre.el). diff --git a/lisp/emacs-lisp/re-builder.el b/lisp/emacs-lisp/re-builder.el index 897c35b5b1..0f9b60730f 100644 --- a/lisp/emacs-lisp/re-builder.el +++ b/lisp/emacs-lisp/re-builder.el @@ -211,6 +211,7 @@ Except for Lisp syntax this is the same as `reb-regexp'.") (defvar reb-valid-string "" "String in mode line showing validity of RE.") +(put 'reb-valid-string 'risky-local-variable t) (defconst reb-buffer "*RE-Builder*" "Buffer to use for the RE Builder.") @@ -308,13 +309,13 @@ Except for Lisp syntax this is the same as `reb-regexp'.") "Return t if display is capable of displaying colors." (eq 'color (frame-parameter nil 'display-type))) -(defsubst reb-lisp-syntax-p () +(defun reb-lisp-syntax-p () "Return non-nil if RE Builder uses `rx' syntax." (eq reb-re-syntax 'rx)) -(defmacro reb-target-binding (symbol) +(defun reb-target-value (symbol) "Return binding for SYMBOL in the RE Builder target buffer." - `(with-current-buffer reb-target-buffer ,symbol)) + (buffer-local-value symbol reb-target-buffer)) (defun reb-initialize-buffer () "Initialize the current buffer as a RE Builder buffer." @@ -440,7 +441,7 @@ provided in the Commentary section of this library." (interactive) (reb-update-regexp) (let ((re (with-output-to-string - (print (reb-target-binding reb-regexp))))) + (print (reb-target-value 'reb-regexp))))) (setq re (substring re 1 (1- (length re)))) (setq re (string-replace "\n" "\\n" re)) (kill-new re) @@ -518,12 +519,17 @@ An actual update is only done if the regexp has changed or if the optional fourth argument FORCE is non-nil." (let ((prev-valid reb-valid-string) (new-valid - (condition-case nil + (condition-case err (progn (when (or (reb-update-regexp) force) (reb-do-update)) "") - (error " *invalid*")))) + (error (propertize + (format " %s" + (if (and (consp (cdr err)) (stringp (cadr err))) + (format "%s: %s" (car err) (cadr err)) + (car err))) + 'face 'font-lock-warning-face))))) (setq reb-valid-string new-valid) (force-mode-line-update) @@ -554,7 +560,7 @@ optional fourth argument FORCE is non-nil." (if reb-subexp-mode (format " (subexp %s)" (or reb-subexp-displayed "-")) "") - (if (not (reb-target-binding case-fold-search)) + (if (not (reb-target-value 'case-fold-search)) " Case" ""))) (force-mode-line-update)) @@ -600,7 +606,7 @@ optional fourth argument FORCE is non-nil." (defun reb-insert-regexp () "Insert current RE." - (let ((re (or (reb-target-binding reb-regexp) + (let ((re (or (reb-target-value 'reb-regexp) (reb-empty-regexp)))) (cond ((eq reb-re-syntax 'read) (print re (current-buffer))) @@ -608,7 +614,7 @@ optional fourth argument FORCE is non-nil." (insert "\n\"" re "\"")) ;; For the Lisp syntax we need the "source" of the regexp ((reb-lisp-syntax-p) - (insert (or (reb-target-binding reb-regexp-src) + (insert (or (reb-target-value 'reb-regexp-src) (reb-empty-regexp))))))) (defun reb-cook-regexp (re) @@ -627,9 +633,8 @@ Return t if the (cooked) expression changed." (prog1 (not (string= oldre re)) (setq reb-regexp re) - ;; Only update the source re for the lisp formats - (when (reb-lisp-syntax-p) - (setq reb-regexp-src re-src))))))) + ;; Update the source re for the Lisp formats. + (setq reb-regexp-src re-src)))))) ;; And now the real core of the whole thing @@ -644,7 +649,7 @@ Return t if the (cooked) expression changed." (defun reb-update-overlays (&optional subexp) "Switch to `reb-target-buffer' and mark all matches of `reb-regexp'. If SUBEXP is non-nil mark only the corresponding sub-expressions." - (let* ((re (reb-target-binding reb-regexp)) + (let* ((re (reb-target-value 'reb-regexp)) (subexps (reb-count-subexps re)) (matches 0) (submatches 0) commit 1073e16960164b901849c32019a373cb1090e20a Author: Jonas Bernoulli Date: Fri Oct 21 22:55:53 2022 +0200 Include more information in error data for sqlite errors Introduce a new 'sqlite-error' and use it for all errors signaled in 'src/sqlite.c', except those that already used 'sqlite-locked-error'. Include the values of 'sqlite3_errcode', 'sqlite3_extended_errcode', 'sqlite3_errstr' and 'sqlite3_errmsg' in the error data. * src/sqlite.c (load_dll_functions): Load 'sqlite3_extended_errcode'. (sqlite-load-extension): Use 'xsignal1' as required by argument type. (syms_of_sqlite): Introduce a new error type 'sqlite-error'. (check_sqlite, sqlite-open, bind_values, sqlite-execute) (sqlite-select, sqlite-load-extension, sqlite-next): Use it. (sqlite_prepare_errdata): New function. (sqlite_prepare_errmsg): Remove function. (sqlite-execute, sqlite-select): Use new function. (sqlite-locked-error): Derive from 'sqlite-error'. diff --git a/src/sqlite.c b/src/sqlite.c index 1526e344e5..d6cb38a29a 100644 --- a/src/sqlite.c +++ b/src/sqlite.c @@ -50,6 +50,7 @@ DEF_DLL_FN (SQLITE_API int, sqlite3_bind_int64, DEF_DLL_FN (SQLITE_API int, sqlite3_bind_double, (sqlite3_stmt*, int, double)); DEF_DLL_FN (SQLITE_API int, sqlite3_bind_null, (sqlite3_stmt*, int)); DEF_DLL_FN (SQLITE_API int, sqlite3_bind_int, (sqlite3_stmt*, int, int)); +DEF_DLL_FN (SQLITE_API int, sqlite3_extended_errcode, (sqlite3*)); DEF_DLL_FN (SQLITE_API const char*, sqlite3_errmsg, (sqlite3*)); DEF_DLL_FN (SQLITE_API const char*, sqlite3_errstr, (int)); DEF_DLL_FN (SQLITE_API int, sqlite3_step, (sqlite3_stmt*)); @@ -88,6 +89,7 @@ DEF_DLL_FN (SQLITE_API int, sqlite3_load_extension, # undef sqlite3_bind_double # undef sqlite3_bind_null # undef sqlite3_bind_int +# undef sqlite3_extended_errcode # undef sqlite3_errmsg # undef sqlite3_errstr # undef sqlite3_step @@ -113,6 +115,7 @@ DEF_DLL_FN (SQLITE_API int, sqlite3_load_extension, # define sqlite3_bind_double fn_sqlite3_bind_double # define sqlite3_bind_null fn_sqlite3_bind_null # define sqlite3_bind_int fn_sqlite3_bind_int +# define sqlite3_extended_errcode fn_sqlite3_extended_errcode # define sqlite3_errmsg fn_sqlite3_errmsg # define sqlite3_errstr fn_sqlite3_errstr # define sqlite3_step fn_sqlite3_step @@ -141,6 +144,7 @@ load_dll_functions (HMODULE library) LOAD_DLL_FN (library, sqlite3_bind_double); LOAD_DLL_FN (library, sqlite3_bind_null); LOAD_DLL_FN (library, sqlite3_bind_int); + LOAD_DLL_FN (library, sqlite3_extended_errcode); LOAD_DLL_FN (library, sqlite3_errmsg); LOAD_DLL_FN (library, sqlite3_errstr); LOAD_DLL_FN (library, sqlite3_step); @@ -233,13 +237,13 @@ check_sqlite (Lisp_Object db, bool is_statement) init_sqlite_functions (); CHECK_SQLITE (db); if (is_statement && !XSQLITE (db)->is_statement) - xsignal1 (Qerror, build_string ("Invalid set object")); + xsignal1 (Qsqlite_error, build_string ("Invalid set object")); else if (!is_statement && XSQLITE (db)->is_statement) - xsignal1 (Qerror, build_string ("Invalid database object")); + xsignal1 (Qsqlite_error, build_string ("Invalid database object")); if (!is_statement && !XSQLITE (db)->db) - xsignal1 (Qerror, build_string ("Database closed")); + xsignal1 (Qsqlite_error, build_string ("Database closed")); else if (is_statement && !XSQLITE (db)->db) - xsignal1 (Qerror, build_string ("Statement closed")); + xsignal1 (Qsqlite_error, build_string ("Statement closed")); } static int db_count = 0; @@ -259,7 +263,7 @@ If FILE is nil, an in-memory database will be opened instead. */) #endif if (!init_sqlite_functions ()) - xsignal1 (Qerror, build_string ("sqlite support is not available")); + xsignal1 (Qsqlite_error, build_string ("sqlite support is not available")); if (!NILP (file)) name = ENCODE_FILE (Fexpand_file_name (file, Qnil)); @@ -272,7 +276,7 @@ If FILE is nil, an in-memory database will be opened instead. */) name = CALLN (Fformat, memory_fmt, make_int (++db_count)); flags |= SQLITE_OPEN_MEMORY; #else - xsignal1 (Qerror, build_string ("sqlite in-memory is not available")); + xsignal1 (Qsqlite_error, build_string ("sqlite in-memory is not available")); #endif } @@ -342,7 +346,7 @@ bind_values (sqlite3 *db, sqlite3_stmt *stmt, Lisp_Object values) if (blob) { if (SBYTES (value) != SCHARS (value)) - xsignal1 (Qerror, build_string ("BLOB values must be unibyte")); + xsignal1 (Qsqlite_error, build_string ("BLOB values must be unibyte")); ret = sqlite3_bind_blob (stmt, i + 1, SSDATA (value), SBYTES (value), NULL); @@ -422,16 +426,15 @@ row_to_value (sqlite3_stmt *stmt) } static Lisp_Object -sqlite_prepare_errmsg (int code, sqlite3 *sdb) +sqlite_prepare_errdata (int code, sqlite3 *sdb) { - Lisp_Object errmsg = build_string (sqlite3_errstr (code)); + Lisp_Object errstr = build_string (sqlite3_errstr (code)); + Lisp_Object errcode = make_fixnum (code); /* More details about what went wrong. */ - const char *sql_error = sqlite3_errmsg (sdb); - if (sql_error) - return CALLN (Fformat, build_string ("%s (%s)"), - errmsg, build_string (sql_error)); - else - return errmsg; + Lisp_Object ext_errcode = make_fixnum (sqlite3_extended_errcode (sdb)); + const char *errmsg = sqlite3_errmsg (sdb); + return list4 (errstr, errmsg ? build_string (errmsg) : Qnil, + errcode, ext_errcode); } DEFUN ("sqlite-execute", Fsqlite_execute, Ssqlite_execute, 2, 3, 0, @@ -447,7 +450,7 @@ Value is the number of affected rows. */) check_sqlite (db, false); CHECK_STRING (query); if (!(NILP (values) || CONSP (values) || VECTORP (values))) - xsignal1 (Qerror, build_string ("VALUES must be a list or a vector")); + xsignal1 (Qsqlite_error, build_string ("VALUES must be a list or a vector")); sqlite3 *sdb = XSQLITE (db)->db; Lisp_Object errmsg = Qnil, @@ -466,7 +469,7 @@ Value is the number of affected rows. */) sqlite3_reset (stmt); } - errmsg = sqlite_prepare_errmsg (ret, sdb); + errmsg = sqlite_prepare_errdata (ret, sdb); goto exit; } @@ -505,7 +508,7 @@ Value is the number of affected rows. */) exit: sqlite3_finalize (stmt); xsignal1 (ret == SQLITE_LOCKED || ret == SQLITE_BUSY? - Qsqlite_locked_error: Qerror, + Qsqlite_locked_error: Qsqlite_error, errmsg); } @@ -540,7 +543,7 @@ which means that we return a set object that can be queried with CHECK_STRING (query); if (!(NILP (values) || CONSP (values) || VECTORP (values))) - xsignal1 (Qerror, build_string ("VALUES must be a list or a vector")); + xsignal1 (Qsqlite_error, build_string ("VALUES must be a list or a vector")); sqlite3 *sdb = XSQLITE (db)->db; Lisp_Object retval = Qnil, errmsg = Qnil, @@ -553,7 +556,7 @@ which means that we return a set object that can be queried with { if (stmt) sqlite3_finalize (stmt); - errmsg = sqlite_prepare_errmsg (ret, sdb); + errmsg = sqlite_prepare_errdata (ret, sdb); goto exit; } @@ -589,7 +592,7 @@ which means that we return a set object that can be queried with exit: if (! NILP (errmsg)) - xsignal1 (Qerror, errmsg); + xsignal1 (Qsqlite_error, errmsg); return retval; } @@ -675,7 +678,7 @@ Only modules on Emacs' list of allowed modules can be loaded. */) } if (!do_allow) - xsignal (Qerror, build_string ("Module name not on allowlist")); + xsignal1 (Qsqlite_error, build_string ("Module name not on allowlist")); int result = sqlite3_load_extension (XSQLITE (db)->db, @@ -695,7 +698,7 @@ DEFUN ("sqlite-next", Fsqlite_next, Ssqlite_next, 1, 1, 0, int ret = sqlite3_step (XSQLITE (set)->stmt); if (ret != SQLITE_ROW && ret != SQLITE_OK && ret != SQLITE_DONE) - xsignal1 (Qerror, build_string (sqlite3_errmsg (XSQLITE (set)->db))); + xsignal1 (Qsqlite_error, build_string (sqlite3_errmsg (XSQLITE (set)->db))); if (ret == SQLITE_DONE) { @@ -794,9 +797,15 @@ syms_of_sqlite (void) defsubr (&Ssqlitep); defsubr (&Ssqlite_available_p); + DEFSYM (Qsqlite_error, "sqlite-error"); + Fput (Qsqlite_error, Qerror_conditions, + Fpurecopy (list2 (Qsqlite_error, Qerror))); + Fput (Qsqlite_error, Qerror_message, + build_pure_c_string ("Database error")); + DEFSYM (Qsqlite_locked_error, "sqlite-locked-error"); Fput (Qsqlite_locked_error, Qerror_conditions, - Fpurecopy (list2 (Qsqlite_locked_error, Qerror))); + Fpurecopy (list3 (Qsqlite_locked_error, Qsqlite_error, Qerror))); Fput (Qsqlite_locked_error, Qerror_message, build_pure_c_string ("Database locked")); commit f9ed80f9d0ac1cb157e4c0604cc39fd07e7f0842 Author: Alan Mackenzie Date: Fri Oct 28 12:13:08 2022 +0000 CC Mode: Fix wrong fontification of enum foo bar; in Objective C Mode This fixes bug #58795 and bug #58796. * lisp/progmodes/cc-langs.el (c-type-prefix-kwds): Add the missing entry for objc. diff --git a/lisp/progmodes/cc-langs.el b/lisp/progmodes/cc-langs.el index 6ccd6c30df..fc3977967b 100644 --- a/lisp/progmodes/cc-langs.el +++ b/lisp/progmodes/cc-langs.el @@ -2319,7 +2319,7 @@ Note that an alternative if the second part doesn't hold is `c-type-list-kwds'. Keywords on this list are typically also present on one of the `*-decl-kwds' lists." t nil - c '("struct" "union" "enum") + (c objc) '("struct" "union" "enum") c++ (append '("class" "typename") (c-lang-const c-type-prefix-kwds c))) commit 879c65a5fca4b543806c9b854e8a5ac3a99e172f Author: Po Lu Date: Fri Oct 28 20:06:06 2022 +0800 Simplify scroll valuator reset handling * src/xterm.c (xi_populate_device_from_info) (xi_reset_scroll_valuators_for_device_id, xi_handle_device_changed) (handle_one_xevent): * src/xterm.h (struct xi_scroll_valuator_t): Get rid of `pending_enter_reset', which was extremely convoluted and kept hitting server bugs. Now, valuators are reset upon all crossing events. diff --git a/src/xterm.c b/src/xterm.c index f2d6be5d46..f1bccddb6c 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -5369,7 +5369,6 @@ xi_populate_device_from_info (struct xi_device_t *xi_device, valuator->emacs_value = DBL_MIN; valuator->increment = info->increment; valuator->number = info->number; - valuator->pending_enter_reset = false; break; } @@ -5415,7 +5414,6 @@ xi_populate_device_from_info (struct xi_device_t *xi_device, { xi_device->valuators[c].invalid_p = false; xi_device->valuators[c].current_value = tem->current_value; - xi_device->valuators[c].pending_enter_reset = true; } } } @@ -5609,8 +5607,8 @@ xi_find_touch_point (struct xi_device_t *device, int detail) #ifdef HAVE_XINPUT2_1 static void -xi_reset_scroll_valuators_for_device_id (struct x_display_info *dpyinfo, int id, - bool pending_only) +xi_reset_scroll_valuators_for_device_id (struct x_display_info *dpyinfo, + int id) { struct xi_device_t *device = xi_device_from_id (dpyinfo, id); struct xi_scroll_valuator_t *valuator; @@ -5624,11 +5622,6 @@ xi_reset_scroll_valuators_for_device_id (struct x_display_info *dpyinfo, int id, for (int i = 0; i < device->scroll_valuator_count; ++i) { valuator = &device->valuators[i]; - - if (pending_only && !valuator->pending_enter_reset) - continue; - - valuator->pending_enter_reset = false; valuator->invalid_p = true; valuator->emacs_value = 0.0; } @@ -13113,14 +13106,6 @@ xi_handle_device_changed (struct x_display_info *dpyinfo, { valuator->invalid_p = false; valuator->current_value = valuator_info->value; - - /* Make sure that this is reset if the pointer moves - into a window of ours. - - Otherwise the valuator state could be left - invalid if the DeviceChange event happened with - the pointer outside any Emacs frame. */ - valuator->pending_enter_reset = true; } break; @@ -21412,8 +21397,8 @@ handle_one_xevent (struct x_display_info *dpyinfo, && enter->mode != XINotifyGrab && enter->mode != XINotifyPassiveGrab && enter->mode != XINotifyPassiveUngrab) - xi_reset_scroll_valuators_for_device_id (dpyinfo, enter->deviceid, - true); + xi_reset_scroll_valuators_for_device_id (dpyinfo, + enter->deviceid); #endif { @@ -21526,13 +21511,18 @@ handle_one_xevent (struct x_display_info *dpyinfo, retrieve the value of a valuator outside of each motion event. - As such, to prevent wildly inaccurate results when the - valuators have changed outside Emacs, we reset our - records of each valuator's value whenever the pointer - moves out of a frame (and not into one of its - children, which we know about). */ + As such, to prevent wildly inaccurate results when + the valuators have changed outside Emacs, we reset + our records of each valuator's value whenever the + pointer moves out of a frame. Ideally, this would + ignore events with a detail of XINotifyInferior, as + the window the pointer moved to would be one known to + Emacs, but the code to keep track of which valuators + had to be reset upon the corresponding XI_Enter event + was very complicated and kept running into server + bugs. */ #ifdef HAVE_XINPUT2_1 - if (leave->detail != XINotifyInferior && any + if (any /* xfwm4 selects for button events on the frame window, resulting in passive grabs being generated along with the delivery of emulated @@ -21547,7 +21537,7 @@ handle_one_xevent (struct x_display_info *dpyinfo, && leave->mode != XINotifyPassiveUngrab && leave->mode != XINotifyPassiveGrab) xi_reset_scroll_valuators_for_device_id (dpyinfo, - leave->deviceid, false); + leave->deviceid); #endif x_display_set_last_user_time (dpyinfo, leave->time, @@ -21584,13 +21574,12 @@ handle_one_xevent (struct x_display_info *dpyinfo, just looks up a top window on Xt builds. */ #ifdef HAVE_XINPUT2_1 - if (leave->detail != XINotifyInferior && f - && leave->mode != XINotifyUngrab + if (f && leave->mode != XINotifyUngrab && leave->mode != XINotifyGrab && leave->mode != XINotifyPassiveUngrab && leave->mode != XINotifyPassiveGrab) xi_reset_scroll_valuators_for_device_id (dpyinfo, - leave->deviceid, false); + leave->deviceid); #endif if (!f) diff --git a/src/xterm.h b/src/xterm.h index 537cabc957..1124dcceb4 100644 --- a/src/xterm.h +++ b/src/xterm.h @@ -213,20 +213,32 @@ struct color_name_cache_entry #ifdef HAVE_XINPUT2 #ifdef HAVE_XINPUT2_1 + struct xi_scroll_valuator_t { - bool invalid_p; - bool pending_enter_reset; + /* The ID of the valuator. */ + int number; + + /* Whether or not it represents X axis movement. */ + bool_bf horizontal : 1; + + /* Whether or not the value is currently invalid. */ + bool_bf invalid_p : 1; + + /* The current value. */ double current_value; + + /* Value used to tally up deltas until a threshold is met. */ double emacs_value; - double increment; - int number; - int horizontal; + /* The scroll increment. */ + double increment; }; + #endif #ifdef HAVE_XINPUT2_2 + struct xi_touch_point_t { struct xi_touch_point_t *next; @@ -234,6 +246,7 @@ struct xi_touch_point_t int number; double x, y; }; + #endif struct xi_device_t commit 90caddc98605e6dbf46be721f4a9ac8a72e8d7ac Author: Po Lu Date: Fri Oct 28 19:55:18 2022 +0800 Clean up some xwidgets code * src/xterm.c (handle_one_xevent): [HAVE_XWIDGETS]: Fix coding style and remove extra ifdefs. Also avoid redundant frame lookups in some cases. diff --git a/src/xterm.c b/src/xterm.c index c8ac30c72c..f2d6be5d46 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -18962,11 +18962,12 @@ handle_one_xevent (struct x_display_info *dpyinfo, goto OTHER; case Expose: - f = x_window_to_frame (dpyinfo, event->xexpose.window); + #ifdef HAVE_XWIDGETS { - struct xwidget_view *xv = - xwidget_view_from_window (event->xexpose.window); + struct xwidget_view *xv; + + xv = xwidget_view_from_window (event->xexpose.window); if (xv) { @@ -18975,6 +18976,8 @@ handle_one_xevent (struct x_display_info *dpyinfo, } } #endif + + f = x_window_to_frame (dpyinfo, event->xexpose.window); if (f) { if (!FRAME_VISIBLE_P (f)) @@ -19778,28 +19781,16 @@ handle_one_xevent (struct x_display_info *dpyinfo, case EnterNotify: x_display_set_last_user_time (dpyinfo, event->xcrossing.time, - event->xcrossing.send_event, false); - -#ifdef HAVE_XINPUT2 - /* For whatever reason, the X server continues to deliver - EnterNotify and LeaveNotify events despite us selecting for - related XI_Enter and XI_Leave events. It's not just our - problem, since windows created by "xinput test-xi2" suffer - from the same defect. Simply ignore all such events while - the input extension is enabled. (bug#57468) */ - - if (dpyinfo->supports_xi2) - goto OTHER; -#endif - - if (x_top_window_to_frame (dpyinfo, event->xcrossing.window)) - x_detect_focus_change (dpyinfo, any, event, &inev.ie); + event->xcrossing.send_event, + false); #ifdef HAVE_XWIDGETS { - struct xwidget_view *xvw = xwidget_view_from_window (event->xcrossing.window); + struct xwidget_view *xvw; Mouse_HLInfo *hlinfo; + xvw = xwidget_view_from_window (event->xcrossing.window); + if (xvw) { xwidget_motion_or_crossing (xvw, event); @@ -19813,14 +19804,28 @@ handle_one_xevent (struct x_display_info *dpyinfo, } if (any_help_event_p) - { - do_help = -1; - } + do_help = -1; + goto OTHER; } } #endif +#ifdef HAVE_XINPUT2 + /* For whatever reason, the X server continues to deliver + EnterNotify and LeaveNotify events despite us selecting for + related XI_Enter and XI_Leave events. It's not just our + problem, since windows created by "xinput test-xi2" suffer + from the same defect. Simply ignore all such events while + the input extension is enabled. (bug#57468) */ + + if (dpyinfo->supports_xi2) + goto OTHER; +#endif + + if (x_top_window_to_frame (dpyinfo, event->xcrossing.window)) + x_detect_focus_change (dpyinfo, any, event, &inev.ie); + f = any; if (f && event->xcrossing.window == FRAME_X_WINDOW (f)) @@ -19931,7 +19936,9 @@ handle_one_xevent (struct x_display_info *dpyinfo, #ifdef HAVE_XWIDGETS { - struct xwidget_view *xvw = xwidget_view_from_window (event->xcrossing.window); + struct xwidget_view *xvw; + + xvw = xwidget_view_from_window (event->xcrossing.window); if (xvw) { @@ -20291,7 +20298,9 @@ handle_one_xevent (struct x_display_info *dpyinfo, f = 0; #endif #ifdef HAVE_XWIDGETS - struct xwidget_view *xvw = xwidget_view_from_window (event->xmotion.window); + struct xwidget_view *xvw; + + xvw = xwidget_view_from_window (event->xmotion.window); if (xvw) xwidget_motion_or_crossing (xvw, event); @@ -20742,7 +20751,9 @@ handle_one_xevent (struct x_display_info *dpyinfo, event->xbutton.send_event, true); #ifdef HAVE_XWIDGETS - struct xwidget_view *xvw = xwidget_view_from_window (event->xbutton.window); + struct xwidget_view *xvw; + + xvw = xwidget_view_from_window (event->xbutton.window); if (xvw) { @@ -21407,10 +21418,10 @@ handle_one_xevent (struct x_display_info *dpyinfo, { #ifdef HAVE_XWIDGETS - struct xwidget_view *xwidget_view = xwidget_view_from_window (enter->event); -#endif + struct xwidget_view *xwidget_view; + + xwidget_view = xwidget_view_from_window (enter->event); -#ifdef HAVE_XWIDGETS if (xwidget_view) { xwidget_motion_or_crossing (xwidget_view, event); @@ -21544,8 +21555,9 @@ handle_one_xevent (struct x_display_info *dpyinfo, #ifdef HAVE_XWIDGETS { - struct xwidget_view *xvw - = xwidget_view_from_window (leave->event); + struct xwidget_view *xvw; + + xvw = xwidget_view_from_window (leave->event); if (xvw) { commit 93aff6e5334ee9f398727395b02c2e7913d84bc2 Author: Po Lu Date: Fri Oct 28 11:41:08 2022 +0000 Fix Haiku subprocess support * src/callproc.c: Disable posix_spawn on Haiku until some things are fixed. diff --git a/src/callproc.c b/src/callproc.c index 1337d2b9de..4d4b86629c 100644 --- a/src/callproc.c +++ b/src/callproc.c @@ -39,7 +39,10 @@ extern char **environ; && (defined HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR \ || defined HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR_NP) \ && defined HAVE_DECL_POSIX_SPAWN_SETSID \ - && HAVE_DECL_POSIX_SPAWN_SETSID == 1 + && HAVE_DECL_POSIX_SPAWN_SETSID == 1 \ + /* posix_spawnattr_setflags rejects POSIX_SPAWN_SETSID on \ + Haiku */ \ + && !defined HAIKU # include # define USABLE_POSIX_SPAWN 1 #else commit 36ca409d72d47aee91fe0b76431c35825789f877 Author: Po Lu Date: Fri Oct 28 19:35:39 2022 +0800 Fix build without Cairo FreeType and fontconfig support * src/xsettings.c (apply_xft_settings): Don't enable Xft code unless Cairo FreeType support is present. (bug#58830) diff --git a/src/xsettings.c b/src/xsettings.c index 14098e21f5..deacddcfde 100644 --- a/src/xsettings.c +++ b/src/xsettings.c @@ -804,7 +804,9 @@ static void apply_xft_settings (Display_Info *dpyinfo, struct xsettings *settings) { -#if defined USE_CAIRO || defined HAVE_XFT +#if defined HAVE_XFT \ + || (defined USE_CAIRO && defined CAIRO_HAS_FC_FONT \ + && defined CAIRO_HAS_FT_FONT) FcPattern *pat; struct xsettings oldsettings; bool changed = false; @@ -940,7 +942,7 @@ apply_xft_settings (Display_Info *dpyinfo, } else FcPatternDestroy (pat); -#endif /* HAVE_XFT */ +#endif /* HAVE_XFT || (USE_CAIRO && CAIRO_HAS_FC_FONT && CAIRO_HAS_FT_FONT) */ } #endif commit e893fd10735239aeecae83805fc4cc6b37156a01 Author: Wamm K. D Date: Thu Oct 27 14:07:03 2022 -0500 Fix failing test for package Hierarchy This fixes a typo in one of the tests of Hierarchy which was causing the test to fail. * test/lisp/emacs-lisp/hierarchy-tests.el (hierarchy-delayed-add-trees): Fix typo. diff --git a/test/lisp/emacs-lisp/hierarchy-tests.el b/test/lisp/emacs-lisp/hierarchy-tests.el index d83460a2ba..97a0f7ba52 100644 --- a/test/lisp/emacs-lisp/hierarchy-tests.el +++ b/test/lisp/emacs-lisp/hierarchy-tests.el @@ -689,7 +689,7 @@ and then create a list of the number plus 0.0–0.9." (190.5 '191) (191 '192)))) (hierarchy (hierarchy-new))) - (hierarchy-add-trees hierarchy '(191 190.5) parentfn + (hierarchy-add-trees hierarchy '(190 190.5) parentfn #'hierarchy-examples-delayed--childrenfn nil t) (should (equal (hierarchy-roots hierarchy) '(192))) (should (equal (hierarchy-children hierarchy '192) '(191))) commit 52d4c98cec0901ef5cc1c55d5b3b33ac9d9c519f Author: Vincent Bernat Date: Tue Oct 25 23:02:16 2022 +0200 Fix detection of DPI changes in builds without Xft * src/xsettings.c (apply_xft_settings) [!HAVE_XFT]: Support XSETTINGS changes in non-XFT builds. (Bug#43128) Copyright-paperwork-exempt: yes diff --git a/src/xsettings.c b/src/xsettings.c index e4a9865d68..14098e21f5 100644 --- a/src/xsettings.c +++ b/src/xsettings.c @@ -804,16 +804,28 @@ static void apply_xft_settings (Display_Info *dpyinfo, struct xsettings *settings) { -#ifdef HAVE_XFT +#if defined USE_CAIRO || defined HAVE_XFT FcPattern *pat; struct xsettings oldsettings; bool changed = false; +#ifndef HAVE_XFT + cairo_font_options_t *options; +#endif + memset (&oldsettings, 0, sizeof (oldsettings)); pat = FcPatternCreate (); +#ifdef HAVE_XFT XftDefaultSubstitute (dpyinfo->display, XScreenNumberOfScreen (dpyinfo->screen), pat); +#else + FcConfigSubstitute (NULL, pat, FcMatchPattern); + options = cairo_font_options_create (); + cairo_ft_font_options_substitute (options, pat); + cairo_font_options_destroy (options); + FcDefaultSubstitute (pat); +#endif FcPatternGetBool (pat, FC_ANTIALIAS, 0, &oldsettings.aa); FcPatternGetBool (pat, FC_HINTING, 0, &oldsettings.hinting); #ifdef FC_HINT_STYLE @@ -912,8 +924,11 @@ apply_xft_settings (Display_Info *dpyinfo, - sizeof "%f") }; char buf[sizeof format + d_formats * d_growth + lf_formats * lf_growth]; - +#ifdef HAVE_XFT XftDefaultSet (dpyinfo->display, pat); +#else + FcPatternDestroy (pat); +#endif store_config_changed_event (Qfont_render, XCAR (dpyinfo->name_list_element)); Vxft_settings commit 510020d30d7aca81bd4a314aae5312ae537294a7 (refs/remotes/origin/emacs-28) Author: Eli Zaretskii Date: Thu Oct 27 10:28:34 2022 +0300 ; Improve doc strings in whitespace.el * lisp/whitespace.el (whitespace-hspace-regexp) (whitespace-space-regexp, whitespace-tab-regexp) (whitespace-trailing-regexp, whitespace-space-before-tab-regexp) (whitespace-indentation-regexp, whitespace-empty-at-bob-regexp) (whitespace-empty-at-eob-regexp) (whitespace-space-after-tab-regexp) (whitespace-big-indent-regexp, whitespace-line-column) (whitespace-display-mappings, whitespace-global-modes) (whitespace-action, whitespace-style, whitespace-space) (whitespace-hspace, whitespace-tab, whitespace-trailing) (whitespace-space-before-tab, whitespace-indentation) (whitespace-big-indent, whitespace-empty) (whitespace-space-after-tab): Doc fixes. diff --git a/lisp/whitespace.el b/lisp/whitespace.el index e2c8eecf89..e518f1e00a 100644 --- a/lisp/whitespace.el +++ b/lisp/whitespace.el @@ -272,120 +272,117 @@ indentation empty space-after-tab space-mark tab-mark newline-mark missing-newline-at-eof) - "Specify which kind of blank is visualized. + "Determine the kinds of whitespace are visualized. -It's a list containing some or all of the following values: +The value is a list containing one or more of the following symbols: - face enable all visualization via faces (see below). + face visualize by using faces (see below). - trailing trailing blanks are visualized via faces. - It has effect only if `face' (see above) + trailing visualize trailing blanks via faces. + This has effect only if `face' (see above) is present in `whitespace-style'. - tabs TABs are visualized via faces. - It has effect only if `face' (see above) + tabs visualize TABs via faces. + This has effect only if `face' (see above) is present in `whitespace-style'. - spaces SPACEs and HARD SPACEs are visualized via + spaces visualize SPACEs and HARD SPACEs via faces. - It has effect only if `face' (see above) + This has effect only if `face' (see above) is present in `whitespace-style'. - lines lines which have columns beyond - `whitespace-line-column' are highlighted via - faces. + lines highlight lines which have columns beyond + `whitespace-line-column' via faces. Whole line is highlighted. - It has precedence over `lines-tail' (see + This has precedence over `lines-tail' (see below). - It has effect only if `face' (see above) + This has effect only if `face' (see above) is present in `whitespace-style'. - lines-tail lines which have columns beyond - `whitespace-line-column' are highlighted via - faces. - But only the part of line which goes - beyond `whitespace-line-column' column. - It has effect only if `lines' (see above) - is not present in `whitespace-style' - and if `face' (see above) is present in + lines-tail highlighted lines which have columns beyond + `whitespace-line-column' via faces. + Only the part of line which goes beyond + `whitespace-line-column' column. + This has effect only if `lines' (see above) + is NOT present in `whitespace-style', + and if `face' (see above) IS present in `whitespace-style'. - newline NEWLINEs are visualized via faces. - It has effect only if `face' (see above) + newline visualize NEWLINEs via faces. + This has effect only if `face' (see above) is present in `whitespace-style'. - missing-newline-at-eof Missing newline at the end of the file is - visualized via faces. - It has effect only if `face' (see above) + missing-newline-at-eof visualize missing newline at the end of + the file via faces. + This has effect only if `face' (see above) is present in `whitespace-style'. - empty empty lines at beginning and/or end of buffer - are visualized via faces. - It has effect only if `face' (see above) + empty visualize empty lines at beginning and/or + end of buffer via faces. + This has effect only if `face' (see above) is present in `whitespace-style'. - indentation::tab `tab-width' or more SPACEs at beginning of line - are visualized via faces. - It has effect only if `face' (see above) + indentation::tab visualize `tab-width' or more SPACEs at + beginning of line via faces. + This has effect only if `face' (see above) is present in `whitespace-style'. - indentation::space TABs at beginning of line are visualized via + indentation::space visualize TABs at beginning of line via faces. - It has effect only if `face' (see above) + This has effect only if `face' (see above) is present in `whitespace-style'. - indentation `tab-width' or more SPACEs at beginning of line - are visualized, if `indent-tabs-mode' (which - see) is non-nil; otherwise, TABs at beginning - of line are visualized via faces. - It has effect only if `face' (see above) + indentation visualize `tab-width' or more SPACEs at + beginning of line, if `indent-tabs-mode' (which + see) is non-nil; otherwise, visualize TABs + at beginning of line via faces. + This has effect only if `face' (see above) is present in `whitespace-style'. - big-indent Big indentations are visualized via faces. - It has effect only if `face' (see above) + big-indent visualize big indentations via faces. + This has effect only if `face' (see above) is present in `whitespace-style'. - space-after-tab::tab `tab-width' or more SPACEs after a TAB - are visualized via faces. - It has effect only if `face' (see above) + space-after-tab::tab visualize `tab-width' or more SPACEs + after a TAB via faces. + This has effect only if `face' (see above) is present in `whitespace-style'. - space-after-tab::space TABs are visualized when `tab-width' or + space-after-tab::space visualize TABs when `tab-width' or more SPACEs occur after a TAB, via faces. - It has effect only if `face' (see above) + This has effect only if `face' (see above) is present in `whitespace-style'. - space-after-tab `tab-width' or more SPACEs after a TAB - are visualized, if `indent-tabs-mode' + space-after-tab visualize `tab-width' or more SPACEs + after a TAB, if `indent-tabs-mode' (which see) is non-nil; otherwise, - the TABs are visualized via faces. - It has effect only if `face' (see above) + visualize the TABs via faces. + This has effect only if `face' (see above) is present in `whitespace-style'. - space-before-tab::tab SPACEs before TAB are visualized via - faces. - It has effect only if `face' (see above) + space-before-tab::tab visualize SPACEs before TAB via faces. + This has effect only if `face' (see above) is present in `whitespace-style'. - space-before-tab::space TABs are visualized when SPACEs occur + space-before-tab::space visualize TABs when SPACEs occur before TAB, via faces. - It has effect only if `face' (see above) + This has effect only if `face' (see above) is present in `whitespace-style'. - space-before-tab SPACEs before TAB are visualized, if + space-before-tab visualize SPACEs before TAB, if `indent-tabs-mode' (which see) is - non-nil; otherwise, the TABs are - visualized via faces. - It has effect only if `face' (see above) + non-nil; otherwise, visualize TABs + via faces. + This has effect only if `face' (see above) is present in `whitespace-style'. - space-mark SPACEs and HARD SPACEs are visualized via + space-mark visualize SPACEs and HARD SPACEs via display table. - tab-mark TABs are visualized via display table. + tab-mark visualize TABs via display table. - newline-mark NEWLINEs are visualized via display table. + newline-mark visualize NEWLINEs via display table. Any other value is ignored. @@ -395,8 +392,7 @@ via display table. There is an evaluation order for some values, if they are included in `whitespace-style' list. For example, if indentation, indentation::tab and/or indentation::space are -included in `whitespace-style' list. The evaluation order for -these values is: +included in `whitespace-style' list, the evaluation order is: * For indentation: 1. indentation @@ -467,7 +463,9 @@ Used when `whitespace-style' includes the value `spaces'.") (((class color) (background light)) :background "LightYellow" :foreground "lightgray") (t :inverse-video t)) - "Face used to visualize SPACE." + "Face used to visualize SPACE. + +See `whitespace-space-regexp'." :group 'whitespace) @@ -482,7 +480,9 @@ Used when `whitespace-style' includes the value `spaces'.") (((class color) (background light)) :background "LemonChiffon3" :foreground "lightgray") (t :inverse-video t)) - "Face used to visualize HARD SPACE." + "Face used to visualize HARD SPACE. + +See `whitespace-hspace-regexp'." :group 'whitespace) @@ -498,7 +498,9 @@ Used when `whitespace-style' includes the value `tabs'.") (((class color) (background light)) :background "beige" :foreground "lightgray") (t :inverse-video t)) - "Face used to visualize TAB." + "Face used to visualize TAB. + +See `whitespace-tab-regexp'." :group 'whitespace) @@ -532,7 +534,9 @@ Used when `whitespace-style' includes the value `trailing'.") '((default :weight bold) (((class mono)) :inverse-video t :underline t) (t :background "red1" :foreground "yellow")) - "Face used to visualize trailing blanks." + "Face used to visualize trailing blanks. + +See '`whitespace-trailing-regexp'." :group 'whitespace) @@ -560,7 +564,9 @@ Used when `whitespace-style' includes the value `space-before-tab'.") (defface whitespace-space-before-tab '((((class mono)) :inverse-video t :weight bold :underline t) (t :background "DarkOrange" :foreground "firebrick")) - "Face used to visualize SPACEs before TAB." + "Face used to visualize SPACEs before TAB. + +See `whitespace-space-before-tab-regexp'." :group 'whitespace) @@ -572,13 +578,17 @@ Used when `whitespace-style' includes the value `indentation'.") (defface whitespace-indentation '((((class mono)) :inverse-video t :weight bold :underline t) (t :background "yellow" :foreground "firebrick")) - "Face used to visualize `tab-width' or more SPACEs at beginning of line." + "Face used to visualize `tab-width' or more SPACEs at beginning of line. + +See `whitespace-indentation-regexp'." :group 'whitespace) (defface whitespace-big-indent '((((class mono)) :inverse-video t :weight bold :underline t) (t :background "red" :foreground "firebrick")) - "Face used to visualize big indentation." + "Face used to visualize big indentation. + +See `whitespace-big-indent-regexp'." :group 'whitespace) (defface whitespace-missing-newline-at-eof @@ -594,7 +604,9 @@ Used when `whitespace-style' includes the value `empty'.") (defface whitespace-empty '((((class mono)) :inverse-video t :weight bold :underline t) (t :background "yellow" :foreground "firebrick" :extend t)) - "Face used to visualize empty lines at beginning and/or end of buffer." + "Face used to visualize empty lines at beginning and/or end of buffer. + +See `whitespace-empty-at-bob-regexp' and `whitespace-empty-at-eob-regexp." :group 'whitespace) @@ -607,14 +619,17 @@ Used when `whitespace-style' includes the value `space-after-tab'.") (defface whitespace-space-after-tab '((((class mono)) :inverse-video t :weight bold :underline t) (t :background "yellow" :foreground "firebrick")) - "Face used to visualize `tab-width' or more SPACEs after TAB." + "Face used to visualize `tab-width' or more SPACEs after TAB. + +See `whitespace-space-after-tab-regexp'." :group 'whitespace) (defcustom whitespace-hspace-regexp "\\(\u00A0+\\)" - "Specify HARD SPACE characters regexp. + "Regexp to match HARD SPACE characters that should be visualized. +The HARD SPACE characters are highlighted using the `whitespace-hspace' face. Here are some examples: \"\\\\(^\\xA0+\\\\)\" \ @@ -626,19 +641,21 @@ visualize leading and/or trailing HARD SPACEs. \"\\t\\\\(\\xA0+\\\\)\\t\" \ visualize only HARD SPACEs between TABs. -NOTE: Enclose always by \\\\( and \\\\) the elements to highlight. +NOTE: Always enclose the elements to highlight in \\\\(...\\\\). Use exactly one pair of enclosing \\\\( and \\\\). -Used when `whitespace-style' includes `spaces'." +This variable is used when `whitespace-style' includes `spaces'." :type '(regexp :tag "HARD SPACE Chars") :group 'whitespace) (defcustom whitespace-space-regexp "\\( +\\)" - "Specify SPACE characters regexp. + "Regexp to match SPACE characters that should be visualized. -If you're using `mule' package, there may be other characters -besides \" \" that should be considered SPACE. +The SPACE characters are highlighted using the `whitespace-space' face. +By default only ASCII SPACE character is visualized, but if you +are typing in some non-Latin language, there may be other +characters besides \" \" that should be considered SPACE. Here are some examples: @@ -648,19 +665,21 @@ Here are some examples: visualize leading and/or trailing SPACEs. \"\\t\\\\( +\\\\)\\t\" visualize only SPACEs between TABs. -NOTE: Enclose always by \\\\( and \\\\) the elements to highlight. +NOTE: Always enclose the elements to highlight in \\\\(...\\\\). Use exactly one pair of enclosing \\\\( and \\\\). -Used when `whitespace-style' includes `spaces'." +This variable is used when `whitespace-style' includes `spaces'." :type '(regexp :tag "SPACE Chars") :group 'whitespace) (defcustom whitespace-tab-regexp "\\(\t+\\)" - "Specify TAB characters regexp. + "Regexp to match TAB characters that should be visualized. -If you're using `mule' package, there may be other characters -besides \"\\t\" that should be considered TAB. +The TAB characters are highlighted using the `whitespace-tab' face. +By default only ASCII TAB character is visualized, but if you +are typing in some non-Latin language, there may be other +characters besides \" \" that should be considered a TAB. Here are some examples: @@ -670,37 +689,40 @@ Here are some examples: visualize leading and/or trailing TABs. \" \\\\(\\t+\\\\) \" visualize only TABs between SPACEs. -NOTE: Enclose always by \\\\( and \\\\) the elements to highlight. +NOTE: Always enclose the elements to highlight in \\\\(...\\\\). Use exactly one pair of enclosing \\\\( and \\\\). -Used when `whitespace-style' includes `tabs'." +This variable is used when `whitespace-style' includes `tabs'." :type '(regexp :tag "TAB Chars") :group 'whitespace) (defcustom whitespace-trailing-regexp "\\([\t \u00A0]+\\)$" - "Specify trailing characters regexp. + "Regexp to match trailing characters that should be visualized. +The trailing characters are highlighted using the `whitespace-trailing' face. There may be other characters besides: \" \" \"\\t\" \"\\u00A0\" that should be considered blank. -NOTE: Enclose always by \"\\\\(\" and \"\\\\)$\" the elements to highlight. +NOTE: Always enclose the elements to highlight in \"\\\\(\"...\"\\\\)$\". Use exactly one pair of enclosing elements above. -Used when `whitespace-style' includes `trailing'." +This variable is used when `whitespace-style' includes `trailing'." :type '(regexp :tag "Trailing Chars") :group 'whitespace) (defcustom whitespace-space-before-tab-regexp "\\( +\\)\\(\t+\\)" - "Specify SPACEs before TAB regexp. + "Regexp to match SPACEs before TAB that should be visualized. -Used when `whitespace-style' includes `space-before-tab', -`space-before-tab::tab' or `space-before-tab::space'." +The SPACE characters are highlighted using the `whitespace-space-before-tab' +face. +This variable is used when `whitespace-style' includes +`space-before-tab', `space-before-tab::tab' or `space-before-tab::space'." :type '(regexp :tag "SPACEs Before TAB") :group 'whitespace) @@ -708,12 +730,15 @@ Used when `whitespace-style' includes `space-before-tab', (defcustom whitespace-indentation-regexp '("^\t*\\(\\( \\{%d\\}\\)+\\)[^\n\t]" . "^ *\\(\t+\\)[^\n]") - "Specify regexp for `tab-width' or more SPACEs at beginning of line. + "Regexps to match indentation whitespace that should be visualized. -It is a cons where the cons car is used for SPACEs visualization -and the cons cdr is used for TABs visualization. +The value should be a cons whose car specifies the regexp to match +visualization of SPACEs, and the cdr specifies the regexp to match +visualization of TABs. -Used when `whitespace-style' includes `indentation', +The indentation characters are highlighted using the `whitespace-indentationp' +face. +This variable is used when `whitespace-style' includes `indentation', `indentation::tab' or `indentation::space'." :type '(cons (string :tag "Indentation SPACEs") (regexp :tag "Indentation TABs")) @@ -721,17 +746,19 @@ Used when `whitespace-style' includes `indentation', (defcustom whitespace-empty-at-bob-regexp "\\`\\(\\([ \t]*\n\\)+\\)" - "Specify regexp for empty lines at beginning of buffer. + "Regexp to match empty lines at beginning of buffer that should be visualized. -Used when `whitespace-style' includes `empty'." +The empty lines are highlighted using the `whitespace-empty' face. +This variable is used when `whitespace-style' includes `empty'." :type '(regexp :tag "Empty Lines At Beginning Of Buffer") :group 'whitespace) (defcustom whitespace-empty-at-eob-regexp "^\\([ \t\n]+\\)\\'" - "Specify regexp for empty lines at end of buffer. + "Regexp to match empty lines at end of buffer that should be visualized. -Used when `whitespace-style' includes `empty'." +The empty lines are highlighted using the `whitespace-empty' face. +This variable is used when `whitespace-style' includes `empty'." :type '(regexp :tag "Empty Lines At End Of Buffer") :group 'whitespace) @@ -739,12 +766,16 @@ Used when `whitespace-style' includes `empty'." (defcustom whitespace-space-after-tab-regexp '("\t+\\(\\( \\{%d,\\}\\)+\\)" . "\\(\t+\\) \\{%d,\\}") - "Specify regexp for `tab-width' or more SPACEs after TAB. + "Regexps to match multiple SPACEs after TAB that should be visualized. -It is a cons where the cons car is used for SPACEs visualization -and the cons cdr is used for TABs visualization. +The SPACE and TAB characters will be visualized if there at least +as many SPACEs as `tab-width' after a TAB. +The value should be a cons whose car is used for SPACEs visualization +and whose cdr is used for TABs visualization. -Used when `whitespace-style' includes `space-after-tab', +The SPACE characters are highlighted using the `whitespace-space-after-tab' +face. +This variable is used when `whitespace-style' includes `space-after-tab', `space-after-tab::tab' or `space-after-tab::space'." :type '(cons (string :tag "SPACEs After TAB") string) @@ -752,27 +783,33 @@ Used when `whitespace-style' includes `space-after-tab', (defcustom whitespace-big-indent-regexp "^\\(\\(?:\t\\{4,\\}\\| \\{32,\\}\\)[\t ]*\\)" - "Specify big indentation regexp. + "Regexp to match big indentation at BOL that should be visualized. -If you're using `mule' package, there may be other characters -besides \"\\t\" that should be considered TAB. +The indentation characters are highlighted using the `whitespace-big-indent' +face. +If you're using non-Latin languages, there may be other characters +besides \"\\t\" that should be considered a TAB. -NOTE: Enclose always by \\\\( and \\\\) the elements to highlight. +NOTE: Always enclose the elements to highlight in \\\\(...\\\\). Use exactly one pair of enclosing \\\\( and \\\\). -Used when `whitespace-style' includes `big-indent'." +This variable is used when `whitespace-style' includes `big-indent'." :version "25.1" :type '(regexp :tag "Detect too much indentation at the beginning of a line") :group 'whitespace) (defcustom whitespace-line-column 80 - "Specify column beyond which the line is highlighted. + "Column beyond which the line is highlighted. + +The value must be an integer or nil. If nil, use the value +of the `fill-column' variable. -It must be an integer or nil. If nil, the `fill-column' variable value is -used. +The characters beyond the column specified by this variable are +highlighted using the `whitespace-line' face. -Used when `whitespace-style' includes `lines' or `lines-tail'." +This varioable is used when `whitespace-style' includes `lines' +or `lines-tail' or `lines-char'." :type '(choice :tag "Line Length Limit" (integer :tag "Line Length") (const :tag "Use fill-column" nil)) @@ -800,7 +837,7 @@ Used when `whitespace-style' includes `lines' or `lines-tail'." ;; If this is a problem for you, please, comment the line below. (tab-mark ?\t [?» ?\t] [?\\ ?\t]) ; tab - right guillemet ) - "Specify an alist of mappings for displaying characters. + "Alist of mappings for displaying characters. Each element has the following form: @@ -820,15 +857,15 @@ KIND is the kind of character. CHAR is the character to be mapped. VECTOR is a vector of characters to be displayed in place of CHAR. - The first display vector that can be displayed is used; + The first vector that can be displayed by the terminal is used; if no display vector for a mapping can be displayed, then that character is displayed unmodified. The NEWLINE character is displayed using the face given by `whitespace-newline' variable. -Used when `whitespace-style' includes `tab-mark', `space-mark' or -`newline-mark'." +This variable is used when `whitespace-style' includes `tab-mark', +`space-mark' or `newline-mark'." :type '(repeat (list :tag "Character Mapping" (choice :tag "Char Kind" @@ -850,8 +887,7 @@ Used when `whitespace-style' includes `tab-mark', `space-mark' or Global `whitespace-mode' is controlled by the command `global-whitespace-mode'. -If nil, means no modes have `whitespace-mode' automatically -turned on. +If nil, no modes have `whitespace-mode' automatically turned on. If t, all modes that support `whitespace-mode' have it automatically turned on. @@ -878,16 +914,16 @@ C++ modes only." (defcustom whitespace-action nil "Specify which action is taken when a buffer is visited or written. -It's a list containing some or all of the following values: +The value is a list containing one or more of the following symbols: nil no action is taken. - cleanup cleanup any bogus whitespace always when local + cleanup always cleanup any bogus whitespace when local whitespace is turned on. See `whitespace-cleanup' and `whitespace-cleanup-region'. - report-on-bogus report if there is any bogus whitespace always + report-on-bogus always report if there is any bogus whitespace when local whitespace is turned on. auto-cleanup cleanup any bogus whitespace when buffer is @@ -895,8 +931,8 @@ It's a list containing some or all of the following values: See `whitespace-cleanup' and `whitespace-cleanup-region'. - abort-on-bogus abort if there is any bogus whitespace and the - buffer is written. + abort-on-bogus signal an error when writing the buffer if there is + any bogus whitespace in the buffer. warn-if-read-only give a warning if `cleanup' or `auto-cleanup' is included in `whitespace-action' and the commit 9d7ba2b1998afc3664c37d9d1b6f6ca2d68356e9 (refs/remotes/origin/feature/noverlay) Author: Matt Armstrong Date: Sun Oct 23 08:58:00 2022 -0700 Fix compiler warning about possible null pointer dereference * src/itree.c (interval_tree_remove_fix): Use 'eassert' to both check for non-null and suppress a compiler warning. diff --git a/src/itree.c b/src/itree.c index 501226b7e2..e824f2c891 100644 --- a/src/itree.c +++ b/src/itree.c @@ -919,6 +919,7 @@ interval_tree_remove_fix (struct itree_tree *tree, interval_tree_rotate_left (tree, parent); other = parent->right; } + eassume (other != NULL); if (null_safe_is_black (other->left) /* 2.a */ && null_safe_is_black (other->right)) @@ -956,6 +957,7 @@ interval_tree_remove_fix (struct itree_tree *tree, interval_tree_rotate_right (tree, parent); other = parent->left; } + eassume (other != NULL); if (null_safe_is_black (other->right) /* 2.b */ && null_safe_is_black (other->left)) commit b78be2bf7a9ac8b71d25529d5736373f51852c36 Author: Matt Armstrong Date: Sat Oct 22 20:48:10 2022 -0700 Fix `get-pos-property' for the new overlay implementation. Some of the trickier edge cases weren't handled properly. See bug#58706. * src/editfns.c (overlays_around): Extend the search range past POS by one to fetch overlays beginning at POS. Fetch empty overlays, as they may be extended by an insertion and thus be relevant to `get-pos-property'. Make a note that the function now, unfortunately, may return out of range overlays. (Fget_pos_property): Deal with 'overlays_around' returning out of range overlays. diff --git a/src/editfns.c b/src/editfns.c index 0038017585..c892c02927 100644 --- a/src/editfns.c +++ b/src/editfns.c @@ -265,12 +265,20 @@ If you set the marker not to point anywhere, the buffer will have no mark. */) /* Find all the overlays in the current buffer that touch position POS. Return the number found, and store them in a vector in VEC - of length LEN. */ + of length LEN. + + Note: this can return overlays that do not touch POS. The caller + should filter these out. */ static ptrdiff_t overlays_around (ptrdiff_t pos, Lisp_Object *vec, ptrdiff_t len) { - return overlays_in (pos - 1, pos, false, &vec, &len, false, false, NULL); + /* Find all potentially rear-advance overlays at (POS - 1). Find + all overlays at POS, so end at (POS + 1). Find even empty + overlays, which due to the way 'overlays-in' works implies that + we might also fetch empty overlays starting at (POS + 1). */ + return overlays_in (pos - 1, pos + 1, false, &vec, &len, + true, false, NULL); } DEFUN ("get-pos-property", Fget_pos_property, Sget_pos_property, 2, 3, 0, @@ -333,7 +341,9 @@ at POSITION. */) if ((OVERLAY_START (ol) == posn && OVERLAY_FRONT_ADVANCE_P (ol)) || (OVERLAY_END (ol) == posn - && ! OVERLAY_REAR_ADVANCE_P (ol))) + && ! OVERLAY_REAR_ADVANCE_P (ol)) + || OVERLAY_START (ol) > posn + || OVERLAY_END (ol) < posn) ; /* The overlay will not cover a char inserted at point. */ else { commit 555bc1f8b3ed8b02fb5acb013ed24073b0666585 Author: Matt Armstrong Date: Sat Oct 22 20:46:30 2022 -0700 Add `get-pos-property' tests covering bug#58706 * test/src/buffer-tests.el (get-pos-property-overlay-beg): New test. (get-pos-property-overlay-empty-rear-advance): ditto. (get-pos-property-overlay-past-rear-advance): ditto. (get-pos-property-overlay-at-narrowed-end): ditto. diff --git a/test/src/buffer-tests.el b/test/src/buffer-tests.el index c6d176cc17..e020732524 100644 --- a/test/src/buffer-tests.el +++ b/test/src/buffer-tests.el @@ -1108,6 +1108,49 @@ with parameters from the *Messages* buffer modification." (should-not (delete-all-overlays)))) +;; +==========================================================================+ +;; | get-pos-property +;; +==========================================================================+ + +(ert-deftest get-pos-property-overlay-beg () + "Test `get-pos-property' at the beginning of an overlay. +Regression test for bug#58706." + (with-temp-buffer + (insert (make-string 10000 ?x)) + (let ((overlay (make-overlay 9999 10001))) + (overlay-put overlay 'forty-two 42)) + (should (equal 42 (get-pos-property 9999 'forty-two))))) + +(ert-deftest get-pos-property-overlay-empty-rear-advance () + "Test `get-pos-property' at the end of an empty rear-advance overlay. +Regression test for bug#58706." + (with-temp-buffer + (insert (make-string 10000 ?x)) + (let ((overlay (make-overlay 9999 9999 nil nil t))) + (overlay-put overlay 'forty-two 42)) + (should (equal 42 (get-pos-property 9999 'forty-two))))) + +(ert-deftest get-pos-property-overlay-past-rear-advance () + "Test `get-pos-property' past the end of an empty rear-advance overlay. +Regression test for bug#58706." + (with-temp-buffer + (insert (make-string 10000 ?x)) + (let ((overlay (make-overlay 9998 9998 nil nil t))) + (overlay-put overlay 'forty-two 42)) + (should (equal nil (get-pos-property 9999 'forty-two))))) + +(ert-deftest get-pos-property-overlay-at-narrowed-end () + "Test `get-pos-property' at the end of a narrowed region. +Regression test for bug#58706." + (with-temp-buffer + (insert (make-string 11000 ?x)) + (narrow-to-region 9998 10000) + (let ((overlay (make-overlay 10000 10000 nil t nil))) + (overlay-put overlay 'forty-two 42)) + (should (equal nil (get-pos-property 9999 'forty-two))))) + +;; FIXME: add more `get-pos-property' tests + ;; +==========================================================================+ ;; | get-char-property(-and-overlay) ;; +==========================================================================+ commit f2a51774a934f29848c5796b46faa5e3b022c401 Author: Matt Armstrong Date: Fri Oct 21 19:45:13 2022 -0700 Fix a narrow-to-region vs. overlays-at bug See bug#58703. * src/buffer.c (overlays_in): Add a new TRAILING arg expressing the behavior wanted by `overlays-at', namely to include all overlays beginning at the POS passed to `overlays-at', even if POS is the end of the narrowed region. Pass true and the search range is extended to ZV+1 if END is greater than ZV, just as is done for EMPTY. (overlays_at): Pass 'true' for the new trailing arg. At present this is the only caller passing 'true'. (mouse_face_overlay_overlaps): Pass 'false' for the new trailing arg. (disable_line_numbers_overlay_at_eob): ditto. (Foverlays_in): ditto. * src/editfns.c (overlays_around): ditto. * test/src/buffer-tests.el (sorted-overlays): Add a spot test for this. diff --git a/src/buffer.c b/src/buffer.c index fe6b515493..dcb3c3944b 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -2943,6 +2943,10 @@ the normal hook `change-major-mode-hook'. */) END, provided END denotes the position at the end of the accessible part of the buffer. + If TRAILING is true, include overlays that begin at END, provided + END denotes the position at the end of the accessible part of the + buffer. + Return the number found, and store them in a vector in *VEC_PTR. Store in *LEN_PTR the size allocated for the vector. Store in *NEXT_PTR the next position after POS where an overlay starts, @@ -2959,7 +2963,8 @@ the normal hook `change-major-mode-hook'. */) ptrdiff_t overlays_in (ptrdiff_t beg, ptrdiff_t end, bool extend, - Lisp_Object **vec_ptr, ptrdiff_t *len_ptr, bool empty, + Lisp_Object **vec_ptr, ptrdiff_t *len_ptr, + bool empty, bool trailing, ptrdiff_t *next_ptr) { ptrdiff_t idx = 0; @@ -2968,9 +2973,14 @@ overlays_in (ptrdiff_t beg, ptrdiff_t end, bool extend, Lisp_Object *vec = *vec_ptr; struct itree_node *node; - ITREE_FOREACH (node, current_buffer->overlays, beg, - /* Find empty OV at ZV ? */ - (end >= ZV && empty) ? ZV + 1 : ZV, ASCENDING) + /* Extend the search range if overlays beginning at ZV are + wanted. */ + ptrdiff_t search_end = ZV; + if (end >= ZV && (empty || trailing)) + ++search_end; + + ITREE_FOREACH (node, current_buffer->overlays, beg, search_end, + ASCENDING) { if (node->begin > end) { @@ -3022,7 +3032,8 @@ overlays_at (ptrdiff_t pos, bool extend, Lisp_Object **vec_ptr, ptrdiff_t *len_ptr, ptrdiff_t *next_ptr) { - return overlays_in (pos, pos + 1, extend, vec_ptr, len_ptr, false, next_ptr); + return overlays_in (pos, pos + 1, extend, vec_ptr, len_ptr, + false, true, next_ptr); } ptrdiff_t @@ -3085,11 +3096,11 @@ mouse_face_overlay_overlaps (Lisp_Object overlay) size = ARRAYELTS (vbuf); v = vbuf; - n = overlays_in (start, end, 0, &v, &size, true, NULL); + n = overlays_in (start, end, 0, &v, &size, true, false, NULL); if (n > size) { SAFE_NALLOCA (v, 1, n); - overlays_in (start, end, 0, &v, &n, true, NULL); + overlays_in (start, end, 0, &v, &n, true, false, NULL); } for (i = 0; i < n; ++i) @@ -3114,11 +3125,11 @@ disable_line_numbers_overlay_at_eob (void) size = ARRAYELTS (vbuf); v = vbuf; - n = overlays_in (ZV, ZV, 0, &v, &size, false, NULL); + n = overlays_in (ZV, ZV, 0, &v, &size, false, false, NULL); if (n > size) { SAFE_NALLOCA (v, 1, n); - overlays_in (ZV, ZV, 0, &v, &n, false, NULL); + overlays_in (ZV, ZV, 0, &v, &n, false, false, NULL); } for (i = 0; i < n; ++i) @@ -3798,7 +3809,7 @@ end of the accessible part of the buffer. */) /* Put all the overlays we want in a vector in overlay_vec. Store the length in len. */ noverlays = overlays_in (XFIXNUM (beg), XFIXNUM (end), 1, &overlay_vec, &len, - true, NULL); + true, false, NULL); /* Make a list of them all. */ result = Flist (noverlays, overlay_vec); diff --git a/src/buffer.h b/src/buffer.h index ce1b7b27b0..3ea4125645 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -1173,7 +1173,7 @@ extern void compact_buffer (struct buffer *); extern void evaporate_overlays (ptrdiff_t); extern ptrdiff_t overlays_at (ptrdiff_t, bool, Lisp_Object **, ptrdiff_t *, ptrdiff_t *); extern ptrdiff_t overlays_in (ptrdiff_t, ptrdiff_t, bool, Lisp_Object **, - ptrdiff_t *, bool, ptrdiff_t *); + ptrdiff_t *, bool, bool, ptrdiff_t *); extern ptrdiff_t previous_overlay_change (ptrdiff_t); extern ptrdiff_t next_overlay_change (ptrdiff_t); extern ptrdiff_t sort_overlays (Lisp_Object *, ptrdiff_t, struct window *); diff --git a/src/editfns.c b/src/editfns.c index 1af6ea1b11..0038017585 100644 --- a/src/editfns.c +++ b/src/editfns.c @@ -270,7 +270,7 @@ If you set the marker not to point anywhere, the buffer will have no mark. */) static ptrdiff_t overlays_around (ptrdiff_t pos, Lisp_Object *vec, ptrdiff_t len) { - return overlays_in (pos - 1, pos, false, &vec, &len, false, NULL); + return overlays_in (pos - 1, pos, false, &vec, &len, false, false, NULL); } DEFUN ("get-pos-property", Fget_pos_property, Sget_pos_property, 2, 3, 0, diff --git a/test/src/buffer-tests.el b/test/src/buffer-tests.el index 6d5d9913a0..c6d176cc17 100644 --- a/test/src/buffer-tests.el +++ b/test/src/buffer-tests.el @@ -860,6 +860,33 @@ with parameters from the *Messages* buffer modification." (should-length 1 (overlays-at 15)) (should-length 1 (overlays-at (point-max)))))) +(defun sorted-overlays (overlays) + (sort + (mapcar (lambda (overlay) + (list (overlay-start overlay) + (overlay-end overlay))) + overlays) + (lambda (first second) + (cl-loop for a in first + for b in second + thereis (< a b) + until (> a b))))) + +(defun sorted-overlays-at (pos) + (sorted-overlays (overlays-at pos))) + +(defun sorted-overlays-in (beg end) + (sorted-overlays (overlays-in beg end))) + +(ert-deftest test-overlays-at-narrow-to-region-end () + ;; See bug#58703. + (with-temp-buffer + (insert (make-string 30 ?x)) + (make-overlay 10 11) + (narrow-to-region 10 10) + (should (equal + '((10 11)) + (sorted-overlays-at 10))))) ;; +==========================================================================+ ;; | overlay-in @@ -937,18 +964,6 @@ with parameters from the *Messages* buffer modification." (deftest-overlays-in-1 ae 9 11 (a) (a 10 10)) (deftest-overlays-in-1 af 10 11 (a) (a 10 10)) -(defun sorted-overlays-in (beg end) - (sort - (mapcar (lambda (overlay) - (list (overlay-start overlay) - (overlay-end overlay))) - (overlays-in beg end)) - (lambda (first second) - (cl-loop for a in first - for b in second - thereis (< a b) - until (> a b))))) - ;; behavior for empty range (ert-deftest test-overlays-in-empty-range () (with-temp-buffer commit a2fde77b5cc15ec5a1c29ca72c97c806204818a9 Author: Matt Armstrong Date: Fri Oct 21 16:07:08 2022 -0700 Fix handling of overlays that begin at END in 'overlays_in' When passed EMPTY, 'overlays_in' should return empty overlays at END. It was doing so, but it was also returning any other overlay that happened to begin at END. bug#58672 * src/buffer.c (overlays_in): Don't return overlays at END unless they are empty overlays. (disable_line_numbers_overlay_at_eob): Pass 'false' instead of 'NULL' for the bool 'empty' arg. * test/src/buffer-tests.el (sorted-overlays-in): New helper function. (test-overlays-in-empty-range): New test exhaustively covering these edge conditions. (test-overlays-in-empty-range-bug58672): Simple test for one case. diff --git a/src/buffer.c b/src/buffer.c index 3286cd338f..fe6b515493 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -2940,7 +2940,8 @@ the normal hook `change-major-mode-hook'. */) [BEG, END). If EMPTY is true, include empty overlays in that range and also at - END, provided END denotes the position at the end of the buffer. + END, provided END denotes the position at the end of the accessible + part of the buffer. Return the number found, and store them in a vector in *VEC_PTR. Store in *LEN_PTR the size allocated for the vector. @@ -2968,7 +2969,7 @@ overlays_in (ptrdiff_t beg, ptrdiff_t end, bool extend, struct itree_node *node; ITREE_FOREACH (node, current_buffer->overlays, beg, - /* Find empty OV at Z ? */ + /* Find empty OV at ZV ? */ (end >= ZV && empty) ? ZV + 1 : ZV, ASCENDING) { if (node->begin > end) @@ -2985,6 +2986,8 @@ overlays_in (ptrdiff_t beg, ptrdiff_t end, bool extend, ITREE_FOREACH_ABORT (); break; } + if (empty && node->begin != node->end) + continue; } if (! empty && node->begin == node->end) @@ -3111,11 +3114,11 @@ disable_line_numbers_overlay_at_eob (void) size = ARRAYELTS (vbuf); v = vbuf; - n = overlays_in (ZV, ZV, 0, &v, &size, NULL, NULL); + n = overlays_in (ZV, ZV, 0, &v, &size, false, NULL); if (n > size) { SAFE_NALLOCA (v, 1, n); - overlays_in (ZV, ZV, 0, &v, &n, NULL, NULL); + overlays_in (ZV, ZV, 0, &v, &n, false, NULL); } for (i = 0; i < n; ++i) diff --git a/test/src/buffer-tests.el b/test/src/buffer-tests.el index 3833f88c5c..6d5d9913a0 100644 --- a/test/src/buffer-tests.el +++ b/test/src/buffer-tests.el @@ -937,6 +937,51 @@ with parameters from the *Messages* buffer modification." (deftest-overlays-in-1 ae 9 11 (a) (a 10 10)) (deftest-overlays-in-1 af 10 11 (a) (a 10 10)) +(defun sorted-overlays-in (beg end) + (sort + (mapcar (lambda (overlay) + (list (overlay-start overlay) + (overlay-end overlay))) + (overlays-in beg end)) + (lambda (first second) + (cl-loop for a in first + for b in second + thereis (< a b) + until (> a b))))) + +;; behavior for empty range +(ert-deftest test-overlays-in-empty-range () + (with-temp-buffer + (insert (make-string 4 ?x)) + (cl-loop for start from (point-min) to (point-max) + do (cl-loop for end from start to (point-max) + do (when (<= start end) + (make-overlay start end)))) + + (cl-loop for pos from (point-min) to (point-max) + do (ert-info ((format "after (overlay-recenter %d)" pos)) + (overlay-recenter pos) + (should (equal + '((1 1)) + (sorted-overlays-in (point-min) (point-min)))) + (should (equal + '((1 3) (1 4) (1 5) (2 2)) + (sorted-overlays-in 2 2))) + (should (equal + '((1 4) (1 5) (2 4) (2 5) (3 3)) + (sorted-overlays-in 3 3))) + (should (equal + '((1 5) (2 5) (3 5) (4 4)) + (sorted-overlays-in 4 4))) + (should (equal + '((5 5)) + (sorted-overlays-in (point-max) (point-max)))))))) + +(ert-deftest test-overlays-in-empty-range-bug58672 () + (with-temp-buffer + (insert (make-string 10 ?=)) + (make-overlay 5 7 nil nil t) + (should (equal nil (overlays-in 5 5))))) ;; behavior at point-max (ert-deftest test-overlays-in-2 () commit 37a1145410f7d61883ea689255ee7e564c2fb3d0 Author: Matt Armstrong Date: Wed Oct 19 16:16:09 2022 -0700 Rename all exported itree.h functions with the itree_ prefix For the most part, I replaced the interval_tree_ prefix with itree_, interval_node_ with itree_node_, etc. * src/alloc.c: Rename everywhere as appropriate. * src/alloc.c: ditto. * src/buffer.c: ditto. * src/buffer.h: ditto. * src/itree.c: ditto. * src/itree.h: ditto. diff --git a/src/alloc.c b/src/alloc.c index a08249b1b1..f69c65dedc 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -3712,7 +3712,7 @@ build_overlay (bool front_advance, bool rear_advance, PVEC_OVERLAY); Lisp_Object overlay = make_lisp_ptr (p, Lisp_Vectorlike); struct itree_node *node = xmalloc (sizeof (*node)); - interval_node_init (node, front_advance, rear_advance, overlay); + itree_node_init (node, front_advance, rear_advance, overlay); p->interval = node; p->buffer = NULL; set_overlay_plist (overlay, plist); diff --git a/src/buffer.c b/src/buffer.c index 228c6e6056..3286cd338f 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -644,9 +644,9 @@ add_buffer_overlay (struct buffer *b, struct Lisp_Overlay *ov, { eassert (! ov->buffer); if (! b->overlays) - b->overlays = interval_tree_create (); + b->overlays = itree_create (); ov->buffer = b; - itree_insert_node (b->overlays, ov->interval, begin, end); + itree_insert (b->overlays, ov->interval, begin, end); } /* Copy overlays of buffer FROM to buffer TO. */ @@ -911,7 +911,7 @@ remove_buffer_overlay (struct buffer *b, struct Lisp_Overlay *ov) { eassert (b->overlays); eassert (ov->buffer == b); - interval_tree_remove (ov->buffer->overlays, ov->interval); + itree_remove (ov->buffer->overlays, ov->interval); ov->buffer = NULL; } @@ -951,7 +951,7 @@ delete_all_overlays (struct buffer *b) /* Where are the nodes freed ? --ap */ XOVERLAY (node->data)->buffer = NULL; } - interval_tree_clear (b->overlays); + itree_clear (b->overlays); } static void @@ -960,7 +960,7 @@ free_buffer_overlays (struct buffer *b) /* Actually this does not free any overlay, but the tree only. --ap */ if (b->overlays) { - interval_tree_destroy (b->overlays); + itree_destroy (b->overlays); b->overlays = NULL; } } @@ -980,7 +980,7 @@ set_overlays_multibyte (bool multibyte) struct itree_node **nodes = NULL; struct itree_tree *tree = current_buffer->overlays; - const intmax_t size = interval_tree_size (tree); + const intmax_t size = itree_size (tree); /* We can't use `interval_node_set_region` at the same time as we iterate over the itree, so we need an auxiliary storage @@ -999,8 +999,8 @@ set_overlays_multibyte (bool multibyte) if (multibyte) { - ptrdiff_t begin = interval_node_begin (tree, node); - ptrdiff_t end = interval_node_end (tree, node); + ptrdiff_t begin = itree_node_begin (tree, node); + ptrdiff_t end = itree_node_end (tree, node); /* This models the behavior of markers. (The behavior of text-intervals differs slightly.) */ @@ -1010,12 +1010,12 @@ set_overlays_multibyte (bool multibyte) while (end < Z_BYTE && !CHAR_HEAD_P (FETCH_BYTE (end))) end++; - interval_node_set_region (tree, node, BYTE_TO_CHAR (begin), + itree_node_set_region (tree, node, BYTE_TO_CHAR (begin), BYTE_TO_CHAR (end)); } else { - interval_node_set_region (tree, node, CHAR_TO_BYTE (node->begin), + itree_node_set_region (tree, node, CHAR_TO_BYTE (node->begin), CHAR_TO_BYTE (node->end)); } } @@ -3446,7 +3446,7 @@ adjust_overlays_for_insert (ptrdiff_t pos, ptrdiff_t length) but we may need to update the value of the overlay center. */ if (! current_buffer->overlays) return; - interval_tree_insert_gap (current_buffer->overlays, pos, length); + itree_insert_gap (current_buffer->overlays, pos, length); } void @@ -3454,7 +3454,7 @@ adjust_overlays_for_delete (ptrdiff_t pos, ptrdiff_t length) { if (! current_buffer->overlays) return; - interval_tree_delete_gap (current_buffer->overlays, pos, length); + itree_delete_gap (current_buffer->overlays, pos, length); } @@ -3594,7 +3594,7 @@ buffer. */) add_buffer_overlay (XBUFFER (buffer), XOVERLAY (overlay), n_beg, n_end); } else - interval_node_set_region (b->overlays, XOVERLAY (overlay)->interval, + itree_node_set_region (b->overlays, XOVERLAY (overlay)->interval, n_beg, n_end); /* If the overlay has changed buffers, do a thorough redisplay. */ diff --git a/src/buffer.h b/src/buffer.h index ee0c8dd836..ce1b7b27b0 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -1398,7 +1398,7 @@ overlay_start (struct Lisp_Overlay *ov) { if (! ov->buffer) return -1; - return interval_node_begin (ov->buffer->overlays, ov->interval); + return itree_node_begin (ov->buffer->overlays, ov->interval); } INLINE ptrdiff_t @@ -1406,7 +1406,7 @@ overlay_end (struct Lisp_Overlay *ov) { if (! ov->buffer) return -1; - return interval_node_end (ov->buffer->overlays, ov->interval); + return itree_node_end (ov->buffer->overlays, ov->interval); } /* Return the start of OV in its buffer, or -1 if OV is not associated diff --git a/src/itree.c b/src/itree.c index 6d54a36c3b..501226b7e2 100644 --- a/src/itree.c +++ b/src/itree.c @@ -511,7 +511,7 @@ interval_tree_validate (struct itree_tree *tree, struct itree_node *node) /* Initialize an allocated node. */ void -interval_node_init (struct itree_node *node, +itree_node_init (struct itree_node *node, bool front_advance, bool rear_advance, Lisp_Object data) { @@ -528,7 +528,7 @@ interval_node_init (struct itree_node *node, /* Return NODE's begin value, computing it if necessary. */ ptrdiff_t -interval_node_begin (struct itree_tree *tree, +itree_node_begin (struct itree_tree *tree, struct itree_node *node) { interval_tree_validate (tree, node); @@ -538,7 +538,7 @@ interval_node_begin (struct itree_tree *tree, /* Return NODE's end value, computing it if necessary. */ ptrdiff_t -interval_node_end (struct itree_tree *tree, +itree_node_end (struct itree_tree *tree, struct itree_node *node) { interval_tree_validate (tree, node); @@ -548,7 +548,7 @@ interval_node_end (struct itree_tree *tree, /* Allocate an interval_tree. Free with interval_tree_destroy. */ struct itree_tree* -interval_tree_create (void) +itree_create (void) { /* FIXME? Maybe avoid the initialization of itree_null in the same way that is used to call mem_init in alloc.c? It's not really @@ -556,14 +556,14 @@ interval_tree_create (void) itree_init (); struct itree_tree *tree = xmalloc (sizeof (*tree)); - interval_tree_clear (tree); + itree_clear (tree); return tree; } /* Reset the tree TREE to its empty state. */ void -interval_tree_clear (struct itree_tree *tree) +itree_clear (struct itree_tree *tree) { tree->root = NULL; tree->otick = 1; @@ -583,7 +583,7 @@ interval_tree_init (struct interval_tree *tree) /* Release a tree, freeing its allocated memory. */ void -interval_tree_destroy (struct itree_tree *tree) +itree_destroy (struct itree_tree *tree) { eassert (tree->root == NULL); /* if (tree->iter) @@ -594,7 +594,7 @@ interval_tree_destroy (struct itree_tree *tree) /* Return the number of nodes in TREE. */ intmax_t -interval_tree_size (struct itree_tree *tree) +itree_size (struct itree_tree *tree) { return tree->size; } @@ -821,7 +821,7 @@ interval_tree_insert (struct itree_tree *tree, struct itree_node *node) } void -itree_insert_node (struct itree_tree *tree, struct itree_node *node, +itree_insert (struct itree_tree *tree, struct itree_node *node, ptrdiff_t begin, ptrdiff_t end) { node->begin = begin; @@ -833,14 +833,14 @@ itree_insert_node (struct itree_tree *tree, struct itree_node *node, /* Safely modify a node's interval. */ void -interval_node_set_region (struct itree_tree *tree, +itree_node_set_region (struct itree_tree *tree, struct itree_node *node, ptrdiff_t begin, ptrdiff_t end) { interval_tree_validate (tree, node); if (begin != node->begin) { - interval_tree_remove (tree, node); + itree_remove (tree, node); node->begin = min (begin, PTRDIFF_MAX - 1); node->end = max (node->begin, end); interval_tree_insert (tree, node); @@ -1054,7 +1054,7 @@ interval_tree_transplant (struct itree_tree *tree, /* Remove NODE from TREE and return it. NODE must exist in TREE. */ struct itree_node* -interval_tree_remove (struct itree_tree *tree, struct itree_node *node) +itree_remove (struct itree_tree *tree, struct itree_node *node) { eassert (interval_tree_contains (tree, node)); eassert (check_tree (tree, true)); /* FIXME: Too expensive. */ @@ -1181,7 +1181,7 @@ itree_iterator_finish (struct itree_iterator *iter) front_advance setting. */ void -interval_tree_insert_gap (struct itree_tree *tree, +itree_insert_gap (struct itree_tree *tree, ptrdiff_t pos, ptrdiff_t length) { if (length <= 0 || tree->root == NULL) @@ -1201,7 +1201,7 @@ interval_tree_insert_gap (struct itree_tree *tree, interval_stack_push (saved, node); } for (int i = 0; i < saved->length; ++i) - interval_tree_remove (tree, nav_nodeptr (saved->nodes[i])); + itree_remove (tree, nav_nodeptr (saved->nodes[i])); /* We can't use an iterator here, because we can't effectively narrow AND shift some subtree at the same time. */ @@ -1265,7 +1265,7 @@ interval_tree_insert_gap (struct itree_tree *tree, intersecting it. */ void -interval_tree_delete_gap (struct itree_tree *tree, +itree_delete_gap (struct itree_tree *tree, ptrdiff_t pos, ptrdiff_t length) { if (length <= 0 || tree->root == NULL) diff --git a/src/itree.h b/src/itree.h index b4116a1fb7..3df07ac1b7 100644 --- a/src/itree.h +++ b/src/itree.h @@ -75,10 +75,9 @@ struct itree_node adjustment before use as buffer positions. NOTE: BEGIN and END must not be modified while the node is part - of a tree. Use interval_tree_insert_gap and - interval_tree_delete_gap instead. + of a tree. Use itree_insert_gap and itree_delete_gap instead. - NOTE: The interval generators ensure nodes are clean before + NOTE: The interval iterators ensure nodes are clean before yielding them, so BEGIN and END may be safely used as buffer positions then. */ @@ -107,19 +106,21 @@ enum itree_order { ITREE_PRE_ORDER, }; -void interval_node_init (struct itree_node *, bool, bool, Lisp_Object); -ptrdiff_t interval_node_begin (struct itree_tree *, struct itree_node *); -ptrdiff_t interval_node_end (struct itree_tree *, struct itree_node *); -void interval_node_set_region (struct itree_tree *, struct itree_node *, ptrdiff_t, ptrdiff_t); -struct itree_tree *interval_tree_create (void); -void interval_tree_destroy (struct itree_tree *); -intmax_t interval_tree_size (struct itree_tree *); -void interval_tree_clear (struct itree_tree *); -void itree_insert_node (struct itree_tree *tree, struct itree_node *node, - ptrdiff_t begin, ptrdiff_t end); -struct itree_node *interval_tree_remove (struct itree_tree *, struct itree_node *); -void interval_tree_insert_gap (struct itree_tree *, ptrdiff_t, ptrdiff_t); -void interval_tree_delete_gap (struct itree_tree *, ptrdiff_t, ptrdiff_t); +void itree_node_init (struct itree_node *, bool, bool, Lisp_Object); +ptrdiff_t itree_node_begin (struct itree_tree *, struct itree_node *); +ptrdiff_t itree_node_end (struct itree_tree *, struct itree_node *); +void itree_node_set_region (struct itree_tree *, struct itree_node *, + ptrdiff_t, ptrdiff_t); +struct itree_tree *itree_create (void); +void itree_destroy (struct itree_tree *); +intmax_t itree_size (struct itree_tree *); +void itree_clear (struct itree_tree *); +void itree_insert (struct itree_tree *tree, struct itree_node *node, + ptrdiff_t begin, ptrdiff_t end); +struct itree_node *itree_remove (struct itree_tree *, + struct itree_node *); +void itree_insert_gap (struct itree_tree *, ptrdiff_t, ptrdiff_t); +void itree_delete_gap (struct itree_tree *, ptrdiff_t, ptrdiff_t); /* Iteration functions. Almost all code should use ITREE_FOREACH instead. */ commit f421b58db5d34f45773a73c699b4b1a5a5b9da03 Author: Matt Armstrong Date: Wed Oct 19 15:56:07 2022 -0700 Prefix all itree.h type names with itree_ Rename interval_node -> itree_node, interval_tree -> itree_tree, interval_tree_order -> itree_order. * src/alloc.c: Renames. * src/buffer.c: ditto. * src/itree.c: ditto. * src/itree.h: ditto. * src/lisp.h: ditto. * src/pdumper.h: ditto. * src/textprop.h: ditto. * src/xdisp.h: ditto. diff --git a/src/alloc.c b/src/alloc.c index d7e0a99ffe..a08249b1b1 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -3711,7 +3711,7 @@ build_overlay (bool front_advance, bool rear_advance, struct Lisp_Overlay *p = ALLOCATE_PSEUDOVECTOR (struct Lisp_Overlay, plist, PVEC_OVERLAY); Lisp_Object overlay = make_lisp_ptr (p, Lisp_Vectorlike); - struct interval_node *node = xmalloc (sizeof (*node)); + struct itree_node *node = xmalloc (sizeof (*node)); interval_node_init (node, front_advance, rear_advance, overlay); p->interval = node; p->buffer = NULL; @@ -6518,7 +6518,7 @@ mark_overlay (struct Lisp_Overlay *ov) /* Mark the overlay subtree rooted at NODE. */ static void -mark_overlays (struct interval_node *node) +mark_overlays (struct itree_node *node) { if (node == NULL) return; diff --git a/src/buffer.c b/src/buffer.c index 74c6705cbd..228c6e6056 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -655,7 +655,7 @@ static void copy_overlays (struct buffer *from, struct buffer *to) { eassert (to && ! to->overlays); - struct interval_node *node; + struct itree_node *node; ITREE_FOREACH (node, from->overlays, PTRDIFF_MIN, PTRDIFF_MAX, ASCENDING) { @@ -932,13 +932,13 @@ drop_overlay (struct Lisp_Overlay *ov) void delete_all_overlays (struct buffer *b) { - struct interval_node *node; + struct itree_node *node; if (! b->overlays) return; /* FIXME: This loop sets the overlays' `buffer` field to NULL but - doesn't set the interval_nodes' `parent`, `left` and `right` + doesn't set the itree_nodes' `parent`, `left` and `right` fields accordingly. I believe it's harmless, but a bit untidy since other parts of the code are careful to set those fields to NULL when the overlay is deleted. @@ -978,8 +978,8 @@ set_overlays_multibyte (bool multibyte) if (! current_buffer->overlays || Z == Z_BYTE) return; - struct interval_node **nodes = NULL; - struct interval_tree *tree = current_buffer->overlays; + struct itree_node **nodes = NULL; + struct itree_tree *tree = current_buffer->overlays; const intmax_t size = interval_tree_size (tree); /* We can't use `interval_node_set_region` at the same time @@ -988,14 +988,14 @@ set_overlays_multibyte (bool multibyte) USE_SAFE_ALLOCA; SAFE_NALLOCA (nodes, 1, size); { - struct interval_node *node, **cursor = nodes; + struct itree_node *node, **cursor = nodes; ITREE_FOREACH (node, tree, PTRDIFF_MIN, PTRDIFF_MAX, ASCENDING) *(cursor++) = node; } for (int i = 0; i < size; ++i, ++nodes) { - struct interval_node * const node = *nodes; + struct itree_node * const node = *nodes; if (multibyte) { @@ -2436,7 +2436,7 @@ advance_to_char_boundary (ptrdiff_t byte_pos) static void swap_buffer_overlays (struct buffer *buffer, struct buffer *other) { - struct interval_node *node; + struct itree_node *node; ITREE_FOREACH (node, buffer->overlays, PTRDIFF_MIN, PTRDIFF_MAX, ASCENDING) XOVERLAY (node->data)->buffer = other; @@ -2965,7 +2965,7 @@ overlays_in (ptrdiff_t beg, ptrdiff_t end, bool extend, ptrdiff_t len = *len_ptr; ptrdiff_t next = ZV; Lisp_Object *vec = *vec_ptr; - struct interval_node *node; + struct itree_node *node; ITREE_FOREACH (node, current_buffer->overlays, beg, /* Find empty OV at Z ? */ @@ -3026,7 +3026,7 @@ ptrdiff_t next_overlay_change (ptrdiff_t pos) { ptrdiff_t next = ZV; - struct interval_node *node; + struct itree_node *node; ITREE_FOREACH (node, current_buffer->overlays, pos, next, ASCENDING) { @@ -3052,7 +3052,7 @@ next_overlay_change (ptrdiff_t pos) ptrdiff_t previous_overlay_change (ptrdiff_t pos) { - struct interval_node *node; + struct itree_node *node; ptrdiff_t prev = BEGV; ITREE_FOREACH (node, current_buffer->overlays, prev, pos, DESCENDING) @@ -3135,7 +3135,7 @@ disable_line_numbers_overlay_at_eob (void) bool overlay_touches_p (ptrdiff_t pos) { - struct interval_node *node; + struct itree_node *node; /* We need to find overlays ending in pos, as well as empty ones at pos. */ @@ -3347,7 +3347,7 @@ ptrdiff_t overlay_strings (ptrdiff_t pos, struct window *w, unsigned char **pstr) { bool multibyte = ! NILP (BVAR (current_buffer, enable_multibyte_characters)); - struct interval_node *node; + struct itree_node *node; overlay_heads.used = overlay_heads.bytes = 0; overlay_tails.used = overlay_tails.bytes = 0; @@ -3848,7 +3848,7 @@ However, the overlays you get are the real objects that the buffer uses. */) (void) { Lisp_Object overlays = Qnil; - struct interval_node *node; + struct itree_node *node; ITREE_FOREACH (node, current_buffer->overlays, BEG, Z, DESCENDING) overlays = Fcons (node->data, overlays); @@ -3980,7 +3980,7 @@ report_overlay_modification (Lisp_Object start, Lisp_Object end, bool after, if (!after) { - struct interval_node *node; + struct itree_node *node; EMACS_INT begin_arg = XFIXNUM (start); EMACS_INT end_arg = XFIXNUM (end); /* We are being called before a change. @@ -4072,7 +4072,7 @@ void evaporate_overlays (ptrdiff_t pos) { Lisp_Object hit_list = Qnil; - struct interval_node *node; + struct itree_node *node; ITREE_FOREACH (node, current_buffer->overlays, pos, pos, ASCENDING) { @@ -4913,7 +4913,7 @@ defvar_per_buffer (struct Lisp_Buffer_Objfwd *bo_fwd, const char *namestring, #ifdef ITREE_DEBUG static Lisp_Object -make_lispy_interval_node (const struct interval_node *node) +make_lispy_itree_node (const struct itree_node *node) { return listn (12, intern (":begin"), @@ -4931,12 +4931,12 @@ make_lispy_interval_node (const struct interval_node *node) } static Lisp_Object -overlay_tree (const struct interval_tree *tree, - const struct interval_node *node) +overlay_tree (const struct itree_tree *tree, + const struct itree_node *node) { if (node == ITREE_NULL) return Qnil; - return list3 (make_lispy_interval_node (node), + return list3 (make_lispy_itree_node (node), overlay_tree (tree, node->left), overlay_tree (tree, node->right)); } diff --git a/src/buffer.h b/src/buffer.h index afcdfcd9a0..ee0c8dd836 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -698,7 +698,7 @@ struct buffer bool_bf long_line_optimizations_p : 1; /* The inveral tree containing this buffer's overlays. */ - struct interval_tree *overlays; + struct itree_tree *overlays; /* Changes in the buffer are recorded here for undo, and t means don't record anything. This information belongs to the base diff --git a/src/itree.c b/src/itree.c index f7597ef86a..6d54a36c3b 100644 --- a/src/itree.c +++ b/src/itree.c @@ -135,7 +135,7 @@ along with GNU Emacs. If not, see . */ typedef uintptr_t nodeptr_and_flag; static inline nodeptr_and_flag -make_nav (struct interval_node *ptr, bool flag) +make_nav (struct itree_node *ptr, bool flag) { uintptr_t v = (uintptr_t) ptr; /* We assume alignment imposes the LSB is clear for us to use it. */ @@ -143,10 +143,10 @@ make_nav (struct interval_node *ptr, bool flag) return v | !!flag; } -static inline struct interval_node * +static inline struct itree_node * nav_nodeptr (nodeptr_and_flag nav) { - return (struct interval_node *) (nav & (~(uintptr_t)1)); + return (struct itree_node *) (nav & (~(uintptr_t)1)); } static inline bool @@ -170,7 +170,7 @@ interval_stack_create (intmax_t initial_size) { struct interval_stack *stack = xmalloc (sizeof (struct interval_stack)); stack->size = max (0, initial_size); - stack->nodes = xmalloc (stack->size * sizeof (struct interval_node*)); + stack->nodes = xmalloc (stack->size * sizeof (struct itree_node*)); stack->length = 0; return stack; } @@ -206,7 +206,7 @@ interval_stack_ensure_space (struct interval_stack *stack, intmax_t nelements) static inline void interval_stack_push_flagged (struct interval_stack *stack, - struct interval_node *node, bool flag) + struct itree_node *node, bool flag) { eassert (node && node != NULL); @@ -223,7 +223,7 @@ interval_stack_push_flagged (struct interval_stack *stack, } static inline void -interval_stack_push (struct interval_stack *stack, struct interval_node *node) +interval_stack_push (struct interval_stack *stack, struct itree_node *node) { interval_stack_push_flagged (stack, node, false); } @@ -246,7 +246,7 @@ struct itree_iterator ptrdiff_t begin; ptrdiff_t end; uintmax_t otick; /* A copy of the tree's `otick`. */ - enum interval_tree_order order; + enum itree_order order; bool running; const char* file; int line; @@ -260,7 +260,7 @@ struct itree_iterator static struct itree_iterator *iter; static int -interval_tree_max_height (const struct interval_tree *tree) +interval_tree_max_height (const struct itree_tree *tree) { return 2 * log (tree->size + 1) / log (2) + 0.5; } @@ -268,7 +268,7 @@ interval_tree_max_height (const struct interval_tree *tree) /* Allocate a new iterator for TREE. */ static struct itree_iterator * -itree_iterator_create (struct interval_tree *tree) +itree_iterator_create (struct itree_tree *tree) { struct itree_iterator *g = xmalloc (sizeof *g); /* 19 here just avoids starting with a silly-small stack. @@ -300,7 +300,7 @@ struct check_subtree_result }; static struct check_subtree_result -check_subtree (struct interval_node *node, +check_subtree (struct itree_node *node, bool check_red_black_invariants, uintmax_t tree_otick, ptrdiff_t offset, ptrdiff_t min_begin, ptrdiff_t max_begin) @@ -369,7 +369,7 @@ check_subtree (struct interval_node *node, entire tree and validates all invariants. */ static bool -check_tree (struct interval_tree *tree, +check_tree (struct itree_tree *tree, bool check_red_black_invariants) { eassert (tree != NULL); @@ -380,7 +380,7 @@ check_tree (struct interval_tree *tree, eassert (tree->root->parent == NULL); eassert (!check_red_black_invariants || !tree->root->red); - struct interval_node *node = tree->root; + struct itree_node *node = tree->root; struct check_subtree_result result = check_subtree (node, check_red_black_invariants, tree->otick, node->offset, PTRDIFF_MIN, @@ -396,19 +396,19 @@ check_tree (struct interval_tree *tree, * +=======================================================================+ */ static bool -null_safe_is_red (struct interval_node *node) +null_safe_is_red (struct itree_node *node) { return node != NULL && node->red; } static bool -null_safe_is_black (struct interval_node *node) +null_safe_is_black (struct itree_node *node) { return node == NULL || !node->red; /* NULL nodes are black */ } static inline ptrdiff_t -itree_newlimit (struct interval_node *node) +itree_newlimit (struct itree_node *node) { eassert (node != NULL); return max (node->end, @@ -423,7 +423,7 @@ itree_newlimit (struct interval_node *node) /* Update NODE's limit attribute according to its children. */ static void -interval_tree_update_limit (struct interval_node *node) +interval_tree_update_limit (struct itree_node *node) { if (node == NULL) return; @@ -438,7 +438,7 @@ interval_tree_update_limit (struct interval_node *node) */ static void -interval_tree_inherit_offset (uintmax_t otick, struct interval_node *node) +interval_tree_inherit_offset (uintmax_t otick, struct itree_node *node) { eassert (node->parent == NULL || node->parent->otick >= node->otick); if (node->otick == otick) @@ -475,7 +475,7 @@ interval_tree_inherit_offset (uintmax_t otick, struct interval_node *node) stable, i.e. new_limit = old_limit. */ static void -interval_tree_propagate_limit (struct interval_node *node) +interval_tree_propagate_limit (struct itree_node *node) { if (node == NULL) return; @@ -491,8 +491,8 @@ interval_tree_propagate_limit (struct interval_node *node) } } -static struct interval_node* -interval_tree_validate (struct interval_tree *tree, struct interval_node *node) +static struct itree_node* +interval_tree_validate (struct itree_tree *tree, struct itree_node *node) { if (tree->otick == node->otick || node == NULL) @@ -511,7 +511,7 @@ interval_tree_validate (struct interval_tree *tree, struct interval_node *node) /* Initialize an allocated node. */ void -interval_node_init (struct interval_node *node, +interval_node_init (struct itree_node *node, bool front_advance, bool rear_advance, Lisp_Object data) { @@ -528,8 +528,8 @@ interval_node_init (struct interval_node *node, /* Return NODE's begin value, computing it if necessary. */ ptrdiff_t -interval_node_begin (struct interval_tree *tree, - struct interval_node *node) +interval_node_begin (struct itree_tree *tree, + struct itree_node *node) { interval_tree_validate (tree, node); return node->begin; @@ -538,8 +538,8 @@ interval_node_begin (struct interval_tree *tree, /* Return NODE's end value, computing it if necessary. */ ptrdiff_t -interval_node_end (struct interval_tree *tree, - struct interval_node *node) +interval_node_end (struct itree_tree *tree, + struct itree_node *node) { interval_tree_validate (tree, node); return node->end; @@ -547,7 +547,7 @@ interval_node_end (struct interval_tree *tree, /* Allocate an interval_tree. Free with interval_tree_destroy. */ -struct interval_tree* +struct itree_tree* interval_tree_create (void) { /* FIXME? Maybe avoid the initialization of itree_null in the same @@ -555,7 +555,7 @@ interval_tree_create (void) important though. */ itree_init (); - struct interval_tree *tree = xmalloc (sizeof (*tree)); + struct itree_tree *tree = xmalloc (sizeof (*tree)); interval_tree_clear (tree); return tree; } @@ -563,7 +563,7 @@ interval_tree_create (void) /* Reset the tree TREE to its empty state. */ void -interval_tree_clear (struct interval_tree *tree) +interval_tree_clear (struct itree_tree *tree) { tree->root = NULL; tree->otick = 1; @@ -583,7 +583,7 @@ interval_tree_init (struct interval_tree *tree) /* Release a tree, freeing its allocated memory. */ void -interval_tree_destroy (struct interval_tree *tree) +interval_tree_destroy (struct itree_tree *tree) { eassert (tree->root == NULL); /* if (tree->iter) @@ -594,7 +594,7 @@ interval_tree_destroy (struct interval_tree *tree) /* Return the number of nodes in TREE. */ intmax_t -interval_tree_size (struct interval_tree *tree) +interval_tree_size (struct itree_tree *tree) { return tree->size; } @@ -602,12 +602,12 @@ interval_tree_size (struct interval_tree *tree) /* Perform the familiar left-rotation on node NODE. */ static void -interval_tree_rotate_left (struct interval_tree *tree, - struct interval_node *node) +interval_tree_rotate_left (struct itree_tree *tree, + struct itree_node *node) { eassert (node->right != NULL); - struct interval_node *right = node->right; + struct itree_node *right = node->right; interval_tree_inherit_offset (tree->otick, node); interval_tree_inherit_offset (tree->otick, right); @@ -645,12 +645,12 @@ interval_tree_rotate_left (struct interval_tree *tree, /* Perform the familiar right-rotation on node NODE. */ static void -interval_tree_rotate_right (struct interval_tree *tree, - struct interval_node *node) +interval_tree_rotate_right (struct itree_tree *tree, + struct itree_node *node) { eassert (tree && node && node->left != NULL); - struct interval_node *left = node->left; + struct itree_node *left = node->left; interval_tree_inherit_offset (tree->otick, node); interval_tree_inherit_offset (tree->otick, left); @@ -684,8 +684,8 @@ interval_tree_rotate_right (struct interval_tree *tree, Rebalance the parents as needed to re-establish the RB invariants. */ static void -interval_tree_insert_fix (struct interval_tree *tree, - struct interval_node *node) +interval_tree_insert_fix (struct itree_tree *tree, + struct itree_node *node) { eassert (tree->root->red == false); @@ -699,7 +699,7 @@ interval_tree_insert_fix (struct interval_tree *tree, { /* We're on the left side of our grandparent, and OTHER is our "uncle". */ - struct interval_node *uncle = node->parent->parent->right; + struct itree_node *uncle = node->parent->parent->right; if (null_safe_is_red (uncle)) /* case 1.a */ { @@ -729,7 +729,7 @@ interval_tree_insert_fix (struct interval_tree *tree, else { /* This is the symmetrical case of above. */ - struct interval_node *uncle = node->parent->parent->left; + struct itree_node *uncle = node->parent->parent->left; if (null_safe_is_red (uncle)) /* case 1.b */ { @@ -763,7 +763,7 @@ interval_tree_insert_fix (struct interval_tree *tree, Note, that inserting a node twice results in undefined behaviour. */ static void -interval_tree_insert (struct interval_tree *tree, struct interval_node *node) +interval_tree_insert (struct itree_tree *tree, struct itree_node *node) { eassert (node->begin <= node->end && node != NULL); /* FIXME: The assertion below fails because `delete_all_overlays` @@ -772,8 +772,8 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) && node->parent == NULL) */; eassert (check_tree (tree, true)); /* FIXME: Too expensive. */ - struct interval_node *parent = NULL; - struct interval_node *child = tree->root; + struct itree_node *parent = NULL; + struct itree_node *child = tree->root; uintmax_t otick = tree->otick; /* It's the responsability of the caller to set `otick` on the node, to "confirm" that the begin/end fields are up to date. */ @@ -821,7 +821,7 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) } void -itree_insert_node (struct interval_tree *tree, struct interval_node *node, +itree_insert_node (struct itree_tree *tree, struct itree_node *node, ptrdiff_t begin, ptrdiff_t end) { node->begin = begin; @@ -833,8 +833,8 @@ itree_insert_node (struct interval_tree *tree, struct interval_node *node, /* Safely modify a node's interval. */ void -interval_node_set_region (struct interval_tree *tree, - struct interval_node *node, +interval_node_set_region (struct itree_tree *tree, + struct itree_node *node, ptrdiff_t begin, ptrdiff_t end) { interval_tree_validate (tree, node); @@ -856,10 +856,10 @@ interval_node_set_region (struct interval_tree *tree, /* Return true, if NODE is a member of TREE. */ static bool -interval_tree_contains (struct interval_tree *tree, struct interval_node *node) +interval_tree_contains (struct itree_tree *tree, struct itree_node *node) { eassert (node); - struct interval_node *other; + struct itree_node *other; ITREE_FOREACH (other, tree, node->begin, PTRDIFF_MAX, ASCENDING) if (other == node) { @@ -871,7 +871,7 @@ interval_tree_contains (struct interval_tree *tree, struct interval_node *node) } static bool -itree_limit_is_stable (struct interval_node *node) +itree_limit_is_stable (struct itree_node *node) { if (node == NULL) return true; @@ -879,8 +879,8 @@ itree_limit_is_stable (struct interval_node *node) return (newlimit == node->limit); } -static struct interval_node* -interval_tree_subtree_min (uintmax_t otick, struct interval_node *node) +static struct itree_node* +interval_tree_subtree_min (uintmax_t otick, struct itree_node *node) { if (node == NULL) return node; @@ -895,9 +895,9 @@ interval_tree_subtree_min (uintmax_t otick, struct interval_node *node) so re-balance the parents to re-establish the RB invariants. */ static void -interval_tree_remove_fix (struct interval_tree *tree, - struct interval_node *node, - struct interval_node *parent) +interval_tree_remove_fix (struct itree_tree *tree, + struct itree_node *node, + struct itree_node *parent) { if (parent == NULL) eassert (node == tree->root); @@ -910,7 +910,7 @@ interval_tree_remove_fix (struct interval_tree *tree, if (node == parent->left) { - struct interval_node *other = parent->right; + struct itree_node *other = parent->right; if (null_safe_is_red (other)) /* case 1.a */ { @@ -947,7 +947,7 @@ interval_tree_remove_fix (struct interval_tree *tree, } else { - struct interval_node *other = parent->left; + struct itree_node *other = parent->left; if (null_safe_is_red (other)) /* 1.b */ { @@ -991,7 +991,7 @@ interval_tree_remove_fix (struct interval_tree *tree, /* Return accumulated offsets of NODE's parents. */ static ptrdiff_t -itree_total_offset (struct interval_node *node) +itree_total_offset (struct itree_node *node) { eassert (node != NULL); ptrdiff_t offset = 0; @@ -1011,9 +1011,9 @@ itree_total_offset (struct interval_node *node) unchanged. Caller is responsible for recalculation of `limit`. Requires both nodes to be using the same effective `offset`. */ static void -interval_tree_replace_child (struct interval_tree *tree, - struct interval_node *source, - struct interval_node *dest) +interval_tree_replace_child (struct itree_tree *tree, + struct itree_node *source, + struct itree_node *dest) { eassert (tree && dest != NULL); eassert (source == NULL @@ -1037,9 +1037,9 @@ interval_tree_replace_child (struct interval_tree *tree, recalculation of `limit`. Requires both nodes to be using the same effective `offset`. */ static void -interval_tree_transplant (struct interval_tree *tree, - struct interval_node *source, - struct interval_node *dest) +interval_tree_transplant (struct itree_tree *tree, + struct itree_node *source, + struct itree_node *dest) { interval_tree_replace_child (tree, source, dest); source->left = dest->left; @@ -1053,8 +1053,8 @@ interval_tree_transplant (struct interval_tree *tree, /* Remove NODE from TREE and return it. NODE must exist in TREE. */ -struct interval_node* -interval_tree_remove (struct interval_tree *tree, struct interval_node *node) +struct itree_node* +interval_tree_remove (struct itree_tree *tree, struct itree_node *node) { eassert (interval_tree_contains (tree, node)); eassert (check_tree (tree, true)); /* FIXME: Too expensive. */ @@ -1063,7 +1063,7 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) `node` has at most one child this is `node` itself. Otherwise, it is the in order successor of `node`. */ interval_tree_inherit_offset (tree->otick, node); - struct interval_node *splice + struct itree_node *splice = (node->left == NULL || node->right == NULL) ? node : interval_tree_subtree_min (tree->otick, node->right); @@ -1072,12 +1072,12 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) `subtree` will not be modified other than changing its parent to `splice`. */ eassert (splice->left == NULL || splice->right == NULL); - struct interval_node *subtree + struct itree_node *subtree = (splice->left != NULL) ? splice->left : splice->right; /* Save a pointer to the parent of where `subtree` will eventually be in `subtree_parent`. */ - struct interval_node *subtree_parent + struct itree_node *subtree_parent = (splice->parent != node) ? splice->parent : splice; /* If `splice` is black removing it may violate Red-Black @@ -1135,8 +1135,8 @@ itree_iterator_busy_p (void) given ORDER. Only one iterator per tree can be running at any time. */ struct itree_iterator * -itree_iterator_start (struct interval_tree *tree, ptrdiff_t begin, - ptrdiff_t end, enum interval_tree_order order, +itree_iterator_start (struct itree_tree *tree, ptrdiff_t begin, + ptrdiff_t end, enum itree_order order, const char *file, int line) { /* struct itree_iterator *iter = tree->iter; */ @@ -1181,7 +1181,7 @@ itree_iterator_finish (struct itree_iterator *iter) front_advance setting. */ void -interval_tree_insert_gap (struct interval_tree *tree, +interval_tree_insert_gap (struct itree_tree *tree, ptrdiff_t pos, ptrdiff_t length) { if (length <= 0 || tree->root == NULL) @@ -1193,7 +1193,7 @@ interval_tree_insert_gap (struct interval_tree *tree, /* Nodes with front_advance starting at pos may mess up the tree order, so we need to remove them first. */ struct interval_stack *saved = interval_stack_create (0); - struct interval_node *node = NULL; + struct itree_node *node = NULL; ITREE_FOREACH (node, tree, pos, pos + 1, PRE_ORDER) { if (node->begin == pos && node->front_advance @@ -1265,7 +1265,7 @@ interval_tree_insert_gap (struct interval_tree *tree, intersecting it. */ void -interval_tree_delete_gap (struct interval_tree *tree, +interval_tree_delete_gap (struct itree_tree *tree, ptrdiff_t pos, ptrdiff_t length) { if (length <= 0 || tree->root == NULL) @@ -1277,7 +1277,7 @@ interval_tree_delete_gap (struct interval_tree *tree, might unintentionally bring shifted nodes back into our search space. */ const int size = interval_tree_max_height (tree) + 1; struct interval_stack *stack = interval_stack_create (size); - struct interval_node *node; + struct itree_node *node; interval_stack_push (stack, tree->root); nodeptr_and_flag nav; @@ -1327,7 +1327,7 @@ interval_tree_delete_gap (struct interval_tree *tree, a NODE2 strictly bigger than NODE1 should also be included). */ static inline bool -interval_node_intersects (const struct interval_node *node, +interval_node_intersects (const struct itree_node *node, ptrdiff_t begin, ptrdiff_t end) { return (begin < node->end && node->begin < end) @@ -1337,13 +1337,13 @@ interval_node_intersects (const struct interval_node *node, /* Return the next node of the iterator in the order given when it was started; or NULL if there are no more nodes. */ -struct interval_node * +struct itree_node * itree_iterator_next (struct itree_iterator *g) { eassert (g->running); - struct interval_node * const null = NULL; - struct interval_node *node; + struct itree_node * const null = NULL; + struct itree_node *node; /* The `visited` flag stored in each node is used here (and only here): We keep a "workstack" of nodes we need to consider. This stack @@ -1363,8 +1363,8 @@ itree_iterator_next (struct itree_iterator *g) visited = nav_flag (nav), node && !visited)) { - struct interval_node * const left = node->left; - struct interval_node * const right = node->right; + struct itree_node * const left = node->left; + struct itree_node * const right = node->right; interval_tree_inherit_offset (g->otick, node); eassert (itree_limit_is_stable (node)); diff --git a/src/itree.h b/src/itree.h index b0f7a1d193..b4116a1fb7 100644 --- a/src/itree.h +++ b/src/itree.h @@ -36,15 +36,14 @@ along with GNU Emacs. If not, see . */ returns as a side-effect. See ITREE_FOREACH. */ -struct interval_node; -struct interval_node +struct itree_node { /* The normal parent, left and right links found in binary trees. See also `red`, below, which completes the Red-Black tree representation. */ - struct interval_node *parent; - struct interval_node *left; - struct interval_node *right; + struct itree_node *parent; + struct itree_node *left; + struct itree_node *right; /* The following five fields comprise the interval abstraction. @@ -63,7 +62,7 @@ struct interval_node OTICK determines whether BEGIN, END, LIMIT and OFFSET are considered dirty. A node is clean when its OTICK is equal to the - OTICK of its tree (see struct interval_tree). Otherwise, it is + OTICK of its tree (see struct itree_tree). Otherwise, it is dirty. In a clean node, BEGIN, END and LIMIT are correct buffer @@ -95,44 +94,44 @@ struct interval_node bool_bf front_advance : 1; /* Same as for marker and overlays. */ }; -struct interval_tree +struct itree_tree { - struct interval_node *root; + struct itree_node *root; uintmax_t otick; /* offset tick, compared with node's otick. */ intmax_t size; /* Number of nodes in the tree. */ }; -enum interval_tree_order { +enum itree_order { ITREE_ASCENDING, ITREE_DESCENDING, ITREE_PRE_ORDER, }; -void interval_node_init (struct interval_node *, bool, bool, Lisp_Object); -ptrdiff_t interval_node_begin (struct interval_tree *, struct interval_node *); -ptrdiff_t interval_node_end (struct interval_tree *, struct interval_node *); -void interval_node_set_region (struct interval_tree *, struct interval_node *, ptrdiff_t, ptrdiff_t); -struct interval_tree *interval_tree_create (void); -void interval_tree_destroy (struct interval_tree *); -intmax_t interval_tree_size (struct interval_tree *); -void interval_tree_clear (struct interval_tree *); -void itree_insert_node (struct interval_tree *tree, struct interval_node *node, +void interval_node_init (struct itree_node *, bool, bool, Lisp_Object); +ptrdiff_t interval_node_begin (struct itree_tree *, struct itree_node *); +ptrdiff_t interval_node_end (struct itree_tree *, struct itree_node *); +void interval_node_set_region (struct itree_tree *, struct itree_node *, ptrdiff_t, ptrdiff_t); +struct itree_tree *interval_tree_create (void); +void interval_tree_destroy (struct itree_tree *); +intmax_t interval_tree_size (struct itree_tree *); +void interval_tree_clear (struct itree_tree *); +void itree_insert_node (struct itree_tree *tree, struct itree_node *node, ptrdiff_t begin, ptrdiff_t end); -struct interval_node *interval_tree_remove (struct interval_tree *, struct interval_node *); -void interval_tree_insert_gap (struct interval_tree *, ptrdiff_t, ptrdiff_t); -void interval_tree_delete_gap (struct interval_tree *, ptrdiff_t, ptrdiff_t); +struct itree_node *interval_tree_remove (struct itree_tree *, struct itree_node *); +void interval_tree_insert_gap (struct itree_tree *, ptrdiff_t, ptrdiff_t); +void interval_tree_delete_gap (struct itree_tree *, ptrdiff_t, ptrdiff_t); /* Iteration functions. Almost all code should use ITREE_FOREACH instead. */ bool itree_iterator_busy_p (void); struct itree_iterator * -itree_iterator_start (struct interval_tree *tree, ptrdiff_t begin, - ptrdiff_t end, enum interval_tree_order order, +itree_iterator_start (struct itree_tree *tree, ptrdiff_t begin, + ptrdiff_t end, enum itree_order order, const char *file, int line); void itree_iterator_narrow (struct itree_iterator *, ptrdiff_t, ptrdiff_t); void itree_iterator_finish (struct itree_iterator *); -struct interval_node *itree_iterator_next (struct itree_iterator *); +struct itree_node *itree_iterator_next (struct itree_iterator *); /* Iterate over the intervals between BEG and END in the tree T. N will hold successive nodes. ORDER can be one of : `ASCENDING`, diff --git a/src/lisp.h b/src/lisp.h index 7cd7871281..b256d65efc 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -2604,7 +2604,7 @@ struct Lisp_Overlay union vectorlike_header header; Lisp_Object plist; struct buffer *buffer; /* eassert (live buffer || NULL). */ - struct interval_node *interval; + struct itree_node *interval; } GCALIGNED_STRUCT; struct Lisp_Misc_Ptr diff --git a/src/pdumper.c b/src/pdumper.c index 1a2ecea71e..1830c6bd18 100644 --- a/src/pdumper.c +++ b/src/pdumper.c @@ -2134,13 +2134,13 @@ dump_marker (struct dump_context *ctx, const struct Lisp_Marker *marker) } static dump_off -dump_interval_node (struct dump_context *ctx, struct interval_node *node, +dump_interval_node (struct dump_context *ctx, struct itree_node *node, dump_off parent_offset) { #if CHECK_STRUCTS && !defined (HASH_interval_node_5765524F7E) # error "interval_node changed. See CHECK_STRUCTS comment in config.h." #endif - struct interval_node out; + struct itree_node out; dump_object_start (ctx, &out, sizeof (out)); if (node->parent) dump_field_fixup_later (ctx, &out, node, &node->parent); @@ -2161,17 +2161,17 @@ dump_interval_node (struct dump_context *ctx, struct interval_node *node, if (node->parent) dump_remember_fixup_ptr_raw (ctx, - offset + dump_offsetof (struct interval_node, parent), + offset + dump_offsetof (struct itree_node, parent), dump_interval_node (ctx, node->parent, offset)); if (node->left) dump_remember_fixup_ptr_raw (ctx, - offset + dump_offsetof (struct interval_node, left), + offset + dump_offsetof (struct itree_node, left), dump_interval_node (ctx, node->left, offset)); if (node->right) dump_remember_fixup_ptr_raw (ctx, - offset + dump_offsetof (struct interval_node, right), + offset + dump_offsetof (struct itree_node, right), dump_interval_node (ctx, node->right, offset)); return offset; } diff --git a/src/textprop.c b/src/textprop.c index b34246f5bc..45ead993a6 100644 --- a/src/textprop.c +++ b/src/textprop.c @@ -634,7 +634,7 @@ get_char_property_and_overlay (Lisp_Object position, register Lisp_Object prop, if (BUFFERP (object)) { struct buffer *b = XBUFFER (object); - struct interval_node *node; + struct itree_node *node; struct sortvec items[2]; struct sortvec *result = NULL; Lisp_Object result_tem = Qnil; diff --git a/src/xdisp.c b/src/xdisp.c index d585d57fd0..ca7e2820f4 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -6534,7 +6534,7 @@ load_overlay_strings (struct it *it, ptrdiff_t charpos) struct overlay_entry entriesbuf[20]; ptrdiff_t size = ARRAYELTS (entriesbuf); struct overlay_entry *entries = entriesbuf; - struct interval_node *node; + struct itree_node *node; USE_SAFE_ALLOCA; @@ -7001,7 +7001,7 @@ back_to_previous_line_start (struct it *it) static bool strings_with_newlines (ptrdiff_t startpos, ptrdiff_t endpos, struct window *w) { - struct interval_node *node; + struct itree_node *node; /* Process overlays. */ ITREE_FOREACH (node, current_buffer->overlays, startpos, endpos, DESCENDING) { commit 06252617b2c4cc9bcdd9407f1e709a7e0908cf27 Author: Matt Armstrong Date: Wed Oct 19 13:42:35 2022 -0700 Revert "mark_overlays: Use the normal ITREE_FOREACH" This reverts commit b8fbd42f0a7caa4cd9e2d50dd4e4b2101ac78acd, with edits. * src/alloc.c (mark_overlays): restore function. (mark_buffer): Call it, not ITREE_FOREACH. (garbage_collect): eassert (!itree_busy_p ()). * src/itree.h: Comment tweak: explain why GC is considered risky. It isn't that GC itself is risky, it is that GC can call ELisp by way of a hook, and running ELisp during iteration is risks nested iteration. diff --git a/src/alloc.c b/src/alloc.c index 00f2991f25..d7e0a99ffe 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -6279,6 +6279,11 @@ garbage_collect (void) image_prune_animation_caches (false); #endif + /* ELisp code run by `gc-post-hook' could result in itree iteration, + which must not happen while the itree is already busy. See + bug#58639. */ + eassert (!itree_iterator_busy_p ()); + if (!NILP (Vpost_gc_hook)) { specpdl_ref gc_count = inhibit_garbage_collection (); @@ -6510,6 +6515,18 @@ mark_overlay (struct Lisp_Overlay *ov) mark_object (ov->plist); } +/* Mark the overlay subtree rooted at NODE. */ + +static void +mark_overlays (struct interval_node *node) +{ + if (node == NULL) + return; + mark_object (node->data); + mark_overlays (node->left); + mark_overlays (node->right); +} + /* Mark Lisp_Objects and special pointers in BUFFER. */ static void @@ -6531,9 +6548,8 @@ mark_buffer (struct buffer *buffer) if (!BUFFER_LIVE_P (buffer)) mark_object (BVAR (buffer, undo_list)); - struct interval_node *node; - ITREE_FOREACH (node, buffer->overlays, PTRDIFF_MIN, PTRDIFF_MAX, ASCENDING) - mark_object (node->data); + if (buffer->overlays) + mark_overlays (buffer->overlays->root); /* If this is an indirect buffer, mark its base buffer. */ if (buffer->base_buffer && diff --git a/src/itree.h b/src/itree.h index 8d33ef223b..b0f7a1d193 100644 --- a/src/itree.h +++ b/src/itree.h @@ -149,7 +149,8 @@ struct interval_node *itree_iterator_next (struct itree_iterator *); it is cheap a pure. - Only a single iteration can happen at a time, so make sure none of the code within the loop can start another tree iteration, i.e. it shouldn't - be able to run ELisp code (or GC for that matter). + be able to run ELisp code, nor GC since GC can run ELisp by way + of `post-gc-hook`. - If you need to exit the loop early, you *have* to call `ITREE_ABORT` just before exiting (e.g. with `break` or `return`). - Non-local exits are not supported within the body of the loop. commit 8159d8b1a71dd59e31060f00b2abe20ad9d1f924 Author: Matt Armstrong Date: Wed Oct 19 08:41:31 2022 -0700 Remove the ITREE_NULL macro and use NULL everywhere. * src/itree.h: Delete the ITREE_NULL macro. * src/itree.c (check_subtree): Use NULL everywhere. * src/pdumper.c (dump_buffer): ditto. diff --git a/src/buffer.h b/src/buffer.h index deb0367d99..afcdfcd9a0 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -1275,7 +1275,7 @@ INLINE bool buffer_has_overlays (void) { return current_buffer->overlays - && (current_buffer->overlays->root != ITREE_NULL); + && (current_buffer->overlays->root != NULL); } /* Functions for accessing a character or byte, diff --git a/src/itree.c b/src/itree.c index aabf33fcb3..f7597ef86a 100644 --- a/src/itree.c +++ b/src/itree.c @@ -208,7 +208,7 @@ static inline void interval_stack_push_flagged (struct interval_stack *stack, struct interval_node *node, bool flag) { - eassert (node && node != ITREE_NULL); + eassert (node && node != NULL); /* FIXME: While the stack used in the iterator is bounded by the tree depth and could be easily pre-allocated to a large enough size to avoid @@ -308,12 +308,12 @@ check_subtree (struct interval_node *node, struct check_subtree_result result = { .size = 0, .limit = PTRDIFF_MIN, .black_height = 0 }; - if (node == ITREE_NULL) + if (node == NULL) return result; /* Validate structure. */ - eassert (node->left == ITREE_NULL || node->left->parent == node); - eassert (node->right == ITREE_NULL || node->right->parent == node); + eassert (node->left == NULL || node->left->parent == node); + eassert (node->right == NULL || node->right->parent == node); /* Validate otick. A node's otick must be <= to the tree's otick and <= to its parent's otick. @@ -323,7 +323,7 @@ check_subtree (struct interval_node *node, doesn't always update otick. It could, but it is not clear there is a need. */ eassert (node->otick <= tree_otick); - eassert (node->parent == ITREE_NULL || node->otick <= node->parent->otick); + eassert (node->parent == NULL || node->otick <= node->parent->otick); eassert (node->otick != tree_otick || node->offset == 0); offset += node->offset; @@ -350,7 +350,7 @@ check_subtree (struct interval_node *node, if (check_red_black_invariants) { eassert (left_result.black_height == right_result.black_height); - eassert (node->parent == ITREE_NULL || !node->red || !node->parent->red); + eassert (node->parent == NULL || !node->red || !node->parent->red); } result.size = 1 + left_result.size + right_result.size; @@ -374,10 +374,10 @@ check_tree (struct interval_tree *tree, { eassert (tree != NULL); eassert (tree->size >= 0); - eassert ((tree->size == 0) == (tree->root == ITREE_NULL)); - if (tree->root == ITREE_NULL) + eassert ((tree->size == 0) == (tree->root == NULL)); + if (tree->root == NULL) return true; - eassert (tree->root->parent == ITREE_NULL); + eassert (tree->root->parent == NULL); eassert (!check_red_black_invariants || !tree->root->red); struct interval_node *node = tree->root; @@ -398,24 +398,24 @@ check_tree (struct interval_tree *tree, static bool null_safe_is_red (struct interval_node *node) { - return node != ITREE_NULL && node->red; + return node != NULL && node->red; } static bool null_safe_is_black (struct interval_node *node) { - return node == ITREE_NULL || !node->red; /* NULL nodes are black */ + return node == NULL || !node->red; /* NULL nodes are black */ } static inline ptrdiff_t itree_newlimit (struct interval_node *node) { - eassert (node != ITREE_NULL); + eassert (node != NULL); return max (node->end, - max (node->left == ITREE_NULL + max (node->left == NULL ? PTRDIFF_MIN : node->left->limit + node->left->offset, - node->right == ITREE_NULL + node->right == NULL ? PTRDIFF_MIN : node->right->limit + node->right->offset)); } @@ -425,7 +425,7 @@ itree_newlimit (struct interval_node *node) static void interval_tree_update_limit (struct interval_node *node) { - if (node == ITREE_NULL) + if (node == NULL) return; node->limit = itree_newlimit (node); @@ -440,7 +440,7 @@ interval_tree_update_limit (struct interval_node *node) static void interval_tree_inherit_offset (uintmax_t otick, struct interval_node *node) { - eassert (node->parent == ITREE_NULL || node->parent->otick >= node->otick); + eassert (node->parent == NULL || node->parent->otick >= node->otick); if (node->otick == otick) { eassert (node->offset == 0); @@ -458,16 +458,16 @@ interval_tree_inherit_offset (uintmax_t otick, struct interval_node *node) node->begin += node->offset; node->end += node->offset; node->limit += node->offset; - if (node->left != ITREE_NULL) + if (node->left != NULL) node->left->offset += node->offset; - if (node->right != ITREE_NULL) + if (node->right != NULL) node->right->offset += node->offset; node->offset = 0; } /* The only thing that matters about `otick` is whether it's equal to that of the tree. We could also "blindly" inherit from parent->otick, but we need to tree's `otick` anyway for when there's no parent. */ - if (node->parent == ITREE_NULL || node->parent->otick == otick) + if (node->parent == NULL || node->parent->otick == otick) node->otick = otick; } @@ -477,7 +477,7 @@ interval_tree_inherit_offset (uintmax_t otick, struct interval_node *node) static void interval_tree_propagate_limit (struct interval_node *node) { - if (node == ITREE_NULL) + if (node == NULL) return; while (1) { @@ -485,7 +485,7 @@ interval_tree_propagate_limit (struct interval_node *node) if (newlimit == node->limit) break; node->limit = newlimit; - if (node->parent == ITREE_NULL) + if (node->parent == NULL) break; node = node->parent; } @@ -495,7 +495,7 @@ static struct interval_node* interval_tree_validate (struct interval_tree *tree, struct interval_node *node) { - if (tree->otick == node->otick || node == ITREE_NULL) + if (tree->otick == node->otick || node == NULL) return node; if (node != tree->root) interval_tree_validate (tree, node->parent); @@ -515,9 +515,9 @@ interval_node_init (struct interval_node *node, bool front_advance, bool rear_advance, Lisp_Object data) { - node->parent = ITREE_NULL; - node->left = ITREE_NULL; - node->right = ITREE_NULL; + node->parent = NULL; + node->left = NULL; + node->right = NULL; node->begin = -1; node->end = -1; node->front_advance = front_advance; @@ -565,7 +565,7 @@ interval_tree_create (void) void interval_tree_clear (struct interval_tree *tree) { - tree->root = ITREE_NULL; + tree->root = NULL; tree->otick = 1; tree->size = 0; } @@ -585,7 +585,7 @@ interval_tree_init (struct interval_tree *tree) void interval_tree_destroy (struct interval_tree *tree) { - eassert (tree->root == ITREE_NULL); + eassert (tree->root == NULL); /* if (tree->iter) * itree_iterator_destroy (tree->iter); */ xfree (tree); @@ -605,7 +605,7 @@ static void interval_tree_rotate_left (struct interval_tree *tree, struct interval_node *node) { - eassert (node->right != ITREE_NULL); + eassert (node->right != NULL); struct interval_node *right = node->right; @@ -614,11 +614,11 @@ interval_tree_rotate_left (struct interval_tree *tree, /* Turn right's left subtree into node's right subtree. */ node->right = right->left; - if (right->left != ITREE_NULL) + if (right->left != NULL) right->left->parent = node; /* right's parent was node's parent. */ - if (right != ITREE_NULL) + if (right != NULL) right->parent = node->parent; /* Get the parent to point to right instead of node. */ @@ -634,7 +634,7 @@ interval_tree_rotate_left (struct interval_tree *tree, /* Put node on right's left. */ right->left = node; - if (node != ITREE_NULL) + if (node != NULL) node->parent = right; /* Order matters here. */ @@ -648,7 +648,7 @@ static void interval_tree_rotate_right (struct interval_tree *tree, struct interval_node *node) { - eassert (tree && node && node->left != ITREE_NULL); + eassert (tree && node && node->left != NULL); struct interval_node *left = node->left; @@ -656,10 +656,10 @@ interval_tree_rotate_right (struct interval_tree *tree, interval_tree_inherit_offset (tree->otick, left); node->left = left->right; - if (left->right != ITREE_NULL) + if (left->right != NULL) left->right->parent = node; - if (left != ITREE_NULL) + if (left != NULL) left->parent = node->parent; if (node != tree->root) { @@ -672,7 +672,7 @@ interval_tree_rotate_right (struct interval_tree *tree, tree->root = left; left->right = node; - if (node != ITREE_NULL) + if (node != NULL) node->parent = left; interval_tree_update_limit (left); @@ -765,14 +765,14 @@ interval_tree_insert_fix (struct interval_tree *tree, static void interval_tree_insert (struct interval_tree *tree, struct interval_node *node) { - eassert (node->begin <= node->end && node != ITREE_NULL); + eassert (node->begin <= node->end && node != NULL); /* FIXME: The assertion below fails because `delete_all_overlays` - doesn't set left/right/parent to ITREE_NULL. */ - /* eassert (node->left == ITREE_NULL && node->right == ITREE_NULL - && node->parent == ITREE_NULL) */; + doesn't set left/right/parent to NULL. */ + /* eassert (node->left == NULL && node->right == NULL + && node->parent == NULL) */; eassert (check_tree (tree, true)); /* FIXME: Too expensive. */ - struct interval_node *parent = ITREE_NULL; + struct interval_node *parent = NULL; struct interval_node *child = tree->root; uintmax_t otick = tree->otick; /* It's the responsability of the caller to set `otick` on the node, @@ -781,7 +781,7 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) /* Find the insertion point, accumulate node's offset and update ancestors limit values. */ - while (child != ITREE_NULL) + while (child != NULL) { interval_tree_inherit_offset (otick, child); parent = child; @@ -793,7 +793,7 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) } /* Insert the node */ - if (parent == ITREE_NULL) + if (parent == NULL) tree->root = node; else if (node->begin <= parent->begin) parent->left = node; @@ -802,11 +802,11 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) /* Init the node */ node->parent = parent; - node->left = ITREE_NULL; - node->right = ITREE_NULL; + node->left = NULL; + node->right = NULL; node->offset = 0; node->limit = node->end; - eassert (node->parent == ITREE_NULL || node->parent->otick >= node->otick); + eassert (node->parent == NULL || node->parent->otick >= node->otick); /* Fix/update the tree */ ++tree->size; @@ -848,7 +848,7 @@ interval_node_set_region (struct interval_tree *tree, else if (end != node->end) { node->end = max (node->begin, end); - eassert (node != ITREE_NULL); + eassert (node != NULL); interval_tree_propagate_limit (node); } } @@ -873,7 +873,7 @@ interval_tree_contains (struct interval_tree *tree, struct interval_node *node) static bool itree_limit_is_stable (struct interval_node *node) { - if (node == ITREE_NULL) + if (node == NULL) return true; ptrdiff_t newlimit = itree_newlimit (node); return (newlimit == node->limit); @@ -882,10 +882,10 @@ itree_limit_is_stable (struct interval_node *node) static struct interval_node* interval_tree_subtree_min (uintmax_t otick, struct interval_node *node) { - if (node == ITREE_NULL) + if (node == NULL) return node; while ((interval_tree_inherit_offset (otick, node), - node->left != ITREE_NULL)) + node->left != NULL)) node = node->left; return node; } @@ -899,12 +899,12 @@ interval_tree_remove_fix (struct interval_tree *tree, struct interval_node *node, struct interval_node *parent) { - if (parent == ITREE_NULL) + if (parent == NULL) eassert (node == tree->root); else - eassert (node == ITREE_NULL || node->parent == parent); + eassert (node == NULL || node->parent == parent); - while (parent != ITREE_NULL && null_safe_is_black (node)) + while (parent != NULL && null_safe_is_black (node)) { eassert (node == parent->left || node == parent->right); @@ -925,7 +925,7 @@ interval_tree_remove_fix (struct interval_tree *tree, { other->red = true; node = parent; - eassert (node != ITREE_NULL); + eassert (node != NULL); parent = node->parent; } else @@ -942,7 +942,7 @@ interval_tree_remove_fix (struct interval_tree *tree, other->right->red = false; interval_tree_rotate_left (tree, parent); node = tree->root; - parent = ITREE_NULL; + parent = NULL; } } else @@ -962,7 +962,7 @@ interval_tree_remove_fix (struct interval_tree *tree, { other->red = true; node = parent; - eassert (node != ITREE_NULL); + eassert (node != NULL); parent = node->parent; } else @@ -980,12 +980,12 @@ interval_tree_remove_fix (struct interval_tree *tree, other->left->red = false; interval_tree_rotate_right (tree, parent); node = tree->root; - parent = ITREE_NULL; + parent = NULL; } } } - if (node != ITREE_NULL) + if (node != NULL) node->red = false; } @@ -993,9 +993,9 @@ interval_tree_remove_fix (struct interval_tree *tree, static ptrdiff_t itree_total_offset (struct interval_node *node) { - eassert (node != ITREE_NULL); + eassert (node != NULL); ptrdiff_t offset = 0; - while (node->parent != ITREE_NULL) + while (node->parent != NULL) { node = node->parent; offset += node->offset; @@ -1015,8 +1015,8 @@ interval_tree_replace_child (struct interval_tree *tree, struct interval_node *source, struct interval_node *dest) { - eassert (tree && dest != ITREE_NULL); - eassert (source == ITREE_NULL + eassert (tree && dest != NULL); + eassert (source == NULL || itree_total_offset (source) == itree_total_offset (dest)); if (dest == tree->root) @@ -1026,7 +1026,7 @@ interval_tree_replace_child (struct interval_tree *tree, else dest->parent->right = source; - if (source != ITREE_NULL) + if (source != NULL) source->parent = dest->parent; } /* Replace DEST with SOURCE in the tree. Copies the following fields @@ -1043,10 +1043,10 @@ interval_tree_transplant (struct interval_tree *tree, { interval_tree_replace_child (tree, source, dest); source->left = dest->left; - if (source->left != ITREE_NULL) + if (source->left != NULL) source->left->parent = source; source->right = dest->right; - if (source->right != ITREE_NULL) + if (source->right != NULL) source->right->parent = source; source->red = dest->red; } @@ -1064,16 +1064,16 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) it is the in order successor of `node`. */ interval_tree_inherit_offset (tree->otick, node); struct interval_node *splice - = (node->left == ITREE_NULL || node->right == ITREE_NULL) + = (node->left == NULL || node->right == NULL) ? node : interval_tree_subtree_min (tree->otick, node->right); /* Find `subtree`, the only child of `splice` (may be NULL). Note: `subtree` will not be modified other than changing its parent to `splice`. */ - eassert (splice->left == ITREE_NULL || splice->right == ITREE_NULL); + eassert (splice->left == NULL || splice->right == NULL); struct interval_node *subtree - = (splice->left != ITREE_NULL) ? splice->left : splice->right; + = (splice->left != NULL) ? splice->left : splice->right; /* Save a pointer to the parent of where `subtree` will eventually be in `subtree_parent`. */ @@ -1109,7 +1109,7 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) if (removed_black) interval_tree_remove_fix (tree, subtree, subtree_parent); - eassert ((tree->size == 0) == (tree->root == ITREE_NULL)); + eassert ((tree->size == 0) == (tree->root == NULL)); eassert (check_tree (tree, true)); /* FIXME: Too expensive. */ /* Clear fields related to the tree for sanity while debugging. */ @@ -1152,7 +1152,7 @@ itree_iterator_start (struct interval_tree *tree, ptrdiff_t begin, iter->otick = tree->otick; iter->order = order; interval_stack_clear (iter->stack); - if (begin <= end && tree->root != ITREE_NULL) + if (begin <= end && tree->root != NULL) interval_stack_push_flagged (iter->stack, tree->root, false); iter->file = file; iter->line = line; @@ -1184,7 +1184,7 @@ void interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t length) { - if (length <= 0 || tree->root == ITREE_NULL) + if (length <= 0 || tree->root == NULL) return; uintmax_t ootick = tree->otick; @@ -1205,7 +1205,7 @@ interval_tree_insert_gap (struct interval_tree *tree, /* We can't use an iterator here, because we can't effectively narrow AND shift some subtree at the same time. */ - if (tree->root != ITREE_NULL) + if (tree->root != NULL) { const int size = interval_tree_max_height (tree) + 1; struct interval_stack *stack = interval_stack_create (size); @@ -1216,7 +1216,7 @@ interval_tree_insert_gap (struct interval_tree *tree, { /* Process in pre-order. */ interval_tree_inherit_offset (tree->otick, node); - if (node->right != ITREE_NULL) + if (node->right != NULL) { if (node->begin > pos) { @@ -1227,7 +1227,7 @@ interval_tree_insert_gap (struct interval_tree *tree, else interval_stack_push (stack, node->right); } - if (node->left != ITREE_NULL + if (node->left != NULL && pos <= node->left->limit + node->left->offset) interval_stack_push (stack, node->left); @@ -1237,7 +1237,7 @@ interval_tree_insert_gap (struct interval_tree *tree, if (node->end > pos || (node->end == pos && node->rear_advance)) { node->end += length; - eassert (node != ITREE_NULL); + eassert (node != NULL); interval_tree_propagate_limit (node); } } @@ -1268,7 +1268,7 @@ void interval_tree_delete_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t length) { - if (length <= 0 || tree->root == ITREE_NULL) + if (length <= 0 || tree->root == NULL) return; /* FIXME: Don't allocate stack anew every time. */ @@ -1285,7 +1285,7 @@ interval_tree_delete_gap (struct interval_tree *tree, { node = nav_nodeptr (nav); interval_tree_inherit_offset (tree->otick, node); - if (node->right != ITREE_NULL) + if (node->right != NULL) { if (node->begin > pos + length) { @@ -1296,7 +1296,7 @@ interval_tree_delete_gap (struct interval_tree *tree, else interval_stack_push (stack, node->right); } - if (node->left != ITREE_NULL + if (node->left != NULL && pos <= node->left->limit + node->left->offset) interval_stack_push (stack, node->left); @@ -1305,7 +1305,7 @@ interval_tree_delete_gap (struct interval_tree *tree, if (node->end > pos) { node->end = max (pos , node->end - length); - eassert (node != ITREE_NULL); + eassert (node != NULL); interval_tree_propagate_limit (node); } } @@ -1342,7 +1342,7 @@ itree_iterator_next (struct itree_iterator *g) { eassert (g->running); - struct interval_node * const null = ITREE_NULL; + struct interval_node * const null = NULL; struct interval_node *node; /* The `visited` flag stored in each node is used here (and only here): diff --git a/src/itree.h b/src/itree.h index f98f028ea5..8d33ef223b 100644 --- a/src/itree.h +++ b/src/itree.h @@ -95,9 +95,6 @@ struct interval_node bool_bf front_advance : 1; /* Same as for marker and overlays. */ }; -/* FIXME: replace ITREE_NULL -> NULL everywhere */ -#define ITREE_NULL NULL - struct interval_tree { struct interval_node *root; diff --git a/src/pdumper.c b/src/pdumper.c index e39f5f1109..1a2ecea71e 100644 --- a/src/pdumper.c +++ b/src/pdumper.c @@ -2863,7 +2863,7 @@ dump_buffer (struct dump_context *ctx, const struct buffer *in_buffer) DUMP_FIELD_COPY (out, buffer, inhibit_buffer_hooks); DUMP_FIELD_COPY (out, buffer, long_line_optimizations_p); - if (buffer->overlays && buffer->overlays->root != ITREE_NULL) + if (buffer->overlays && buffer->overlays->root != NULL) /* We haven't implemented the code to dump overlays. */ emacs_abort (); else commit 1c44f6c83d88914cfee159ae021be33851e0112f Author: Matt Armstrong Date: Sat Oct 15 20:04:12 2022 -0700 ; * src/buffer.c (set_overlay_region): remove unused function. diff --git a/src/buffer.c b/src/buffer.c index e1303f2a88..74c6705cbd 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -3535,15 +3535,6 @@ modify_overlay (struct buffer *buf, ptrdiff_t start, ptrdiff_t end) modiff_incr (&BUF_OVERLAY_MODIFF (buf), 1); } -INLINE void -set_overlay_region (struct Lisp_Overlay *ov, ptrdiff_t begin, ptrdiff_t end) -{ - eassert (ov->buffer); - begin = clip_to_bounds (BEG, begin, ov->buffer->text->z); - end = clip_to_bounds (begin, end, ov->buffer->text->z); - interval_node_set_region (ov->buffer->overlays, ov->interval, begin, end); -} - DEFUN ("move-overlay", Fmove_overlay, Smove_overlay, 3, 4, 0, doc: /* Set the endpoints of OVERLAY to BEG and END in BUFFER. If BUFFER is omitted, leave OVERLAY in the same buffer it inhabits now. commit 19f5431cf644517776e2945912dd5d5e8933b3dc Author: Stefan Monnier Date: Mon Oct 17 14:10:06 2022 -0400 * src/itree.c: Eliminate all prototypes for static functions Massive code reorganization to move definitions of static functions before their first use, so as to remove the need for redundant prototypes. While at it, fix a few places where the used more than 80 column. diff --git a/src/itree.c b/src/itree.c index 0ba9e662bc..aabf33fcb3 100644 --- a/src/itree.c +++ b/src/itree.c @@ -128,34 +128,33 @@ along with GNU Emacs. If not, see . */ incremented whenever some node's offset has changed. */ -/* FIXME: The code seems to use "generator" and "iterator" - inconsistently/interchangeably. We should fix this naming. */ - -static struct interval_node *interval_tree_validate (struct interval_tree *, struct interval_node *); -static bool interval_node_intersects (const struct interval_node *, ptrdiff_t, ptrdiff_t); -static int interval_tree_max_height (const struct interval_tree *); -static void interval_tree_update_limit (struct interval_node *); -static void interval_tree_inherit_offset (uintmax_t otick, struct interval_node *); -static void interval_tree_propagate_limit (struct interval_node *); -static void interval_tree_rotate_left (struct interval_tree *, struct interval_node *); -static void interval_tree_rotate_right (struct interval_tree *, struct interval_node *); -static void interval_tree_insert_fix (struct interval_tree *, struct interval_node *); -static void interval_tree_replace_child (struct interval_tree *, - struct interval_node *, - struct interval_node *); -static void interval_tree_transplant (struct interval_tree *tree, - struct interval_node *source, - struct interval_node *dest); -static struct itree_iterator * -itree_iterator_create (struct interval_tree *); -static void interval_tree_insert (struct interval_tree *, struct interval_node *); -static bool null_safe_is_red (struct interval_node *node); -static bool null_safe_is_black (struct interval_node *node); - -/* +------------------------------------------------------------------------------------+ */ +/* +=======================================================================+ + * | Stack + * +=======================================================================+ */ typedef uintptr_t nodeptr_and_flag; +static inline nodeptr_and_flag +make_nav (struct interval_node *ptr, bool flag) +{ + uintptr_t v = (uintptr_t) ptr; + /* We assume alignment imposes the LSB is clear for us to use it. */ + eassert (!(v & 1)); + return v | !!flag; +} + +static inline struct interval_node * +nav_nodeptr (nodeptr_and_flag nav) +{ + return (struct interval_node *) (nav & (~(uintptr_t)1)); +} + +static inline bool +nav_flag (nodeptr_and_flag nav) +{ + return (bool) (nav & 1); +} + /* Simple dynamic array. */ struct interval_stack { @@ -164,6 +163,82 @@ struct interval_stack size_t length; }; +/* This is just a simple dynamic array with stack semantics. */ + +static struct interval_stack* +interval_stack_create (intmax_t initial_size) +{ + struct interval_stack *stack = xmalloc (sizeof (struct interval_stack)); + stack->size = max (0, initial_size); + stack->nodes = xmalloc (stack->size * sizeof (struct interval_node*)); + stack->length = 0; + return stack; +} + +static void +interval_stack_destroy (struct interval_stack *stack) +{ + if (! stack) + return; + if (stack->nodes) + xfree (stack->nodes); + xfree (stack); +} + +static void +interval_stack_clear (struct interval_stack *stack) +{ + stack->length = 0; +} + +static inline void +interval_stack_ensure_space (struct interval_stack *stack, intmax_t nelements) +{ + if (nelements > stack->size) + { + stack->size = (nelements + 1) * 2; + stack->nodes = xrealloc (stack->nodes, + stack->size * sizeof (*stack->nodes)); + } +} + +/* Push NODE on the STACK, while settings its visited flag to FLAG. */ + +static inline void +interval_stack_push_flagged (struct interval_stack *stack, + struct interval_node *node, bool flag) +{ + eassert (node && node != ITREE_NULL); + + /* FIXME: While the stack used in the iterator is bounded by the tree + depth and could be easily pre-allocated to a large enough size to avoid + this "ensure" check, `interval_stack_push` is also used elsewhere to + simply collect some subset of the overlays, where it's only bounded by + the total number of overlays in the buffer (which can be large and thus + preferably not pre-allocated needlessly). */ + interval_stack_ensure_space (stack, stack->length + 1); + + stack->nodes[stack->length] = make_nav (node, flag); + stack->length++; +} + +static inline void +interval_stack_push (struct interval_stack *stack, struct interval_node *node) +{ + interval_stack_push_flagged (stack, node, false); +} + +static inline nodeptr_and_flag +interval_stack_pop (struct interval_stack *stack) +{ + if (stack->length == 0) + return make_nav (NULL, false); + return stack->nodes[--stack->length]; +} + + +/* +-----------------------------------------------------------------------+ */ + /* State used when iterating interval. */ struct itree_iterator { @@ -184,6 +259,33 @@ struct itree_iterator So we just use a single global iterator instead for now. */ static struct itree_iterator *iter; +static int +interval_tree_max_height (const struct interval_tree *tree) +{ + return 2 * log (tree->size + 1) / log (2) + 0.5; +} + +/* Allocate a new iterator for TREE. */ + +static struct itree_iterator * +itree_iterator_create (struct interval_tree *tree) +{ + struct itree_iterator *g = xmalloc (sizeof *g); + /* 19 here just avoids starting with a silly-small stack. + FIXME: Since this stack only needs to be about 2*max_depth + in the worst case, we could completely pre-allocate it to something + like word-bit-size * 2 and then never worry about growing it. */ + const int size = (tree ? interval_tree_max_height (tree) : 19) + 1; + + g->stack = interval_stack_create (size); + g->running = false; + g->begin = 0; + g->end = 0; + g->file = NULL; + g->line = 0; + return g; +} + static void itree_init (void) { @@ -289,108 +391,122 @@ check_tree (struct interval_tree *tree, return true; } -/* +===================================================================================+ - * | Stack - * +===================================================================================+ */ +/* +=======================================================================+ + * | Internal Functions + * +=======================================================================+ */ -static inline nodeptr_and_flag -make_nav (struct interval_node *ptr, bool flag) +static bool +null_safe_is_red (struct interval_node *node) { - uintptr_t v = (uintptr_t) ptr; - /* We assume alignment imposes the LSB is clear for us to use it. */ - eassert (!(v & 1)); - return v | !!flag; + return node != ITREE_NULL && node->red; } -static inline struct interval_node * -nav_nodeptr (nodeptr_and_flag nav) +static bool +null_safe_is_black (struct interval_node *node) { - return (struct interval_node *) (nav & (~(uintptr_t)1)); + return node == ITREE_NULL || !node->red; /* NULL nodes are black */ } -static inline bool -nav_flag (nodeptr_and_flag nav) +static inline ptrdiff_t +itree_newlimit (struct interval_node *node) { - return (bool) (nav & 1); + eassert (node != ITREE_NULL); + return max (node->end, + max (node->left == ITREE_NULL + ? PTRDIFF_MIN + : node->left->limit + node->left->offset, + node->right == ITREE_NULL + ? PTRDIFF_MIN + : node->right->limit + node->right->offset)); } -/* This is just a simple dynamic array with stack semantics. */ - -static struct interval_stack* -interval_stack_create (intmax_t initial_size) -{ - struct interval_stack *stack = xmalloc (sizeof (struct interval_stack)); - stack->size = max (0, initial_size); - stack->nodes = xmalloc (stack->size * sizeof (struct interval_node*)); - stack->length = 0; - return stack; -} +/* Update NODE's limit attribute according to its children. */ static void -interval_stack_destroy (struct interval_stack *stack) +interval_tree_update_limit (struct interval_node *node) { - if (! stack) + if (node == ITREE_NULL) return; - if (stack->nodes) - xfree (stack->nodes); - xfree (stack); + + node->limit = itree_newlimit (node); } +/* Apply NODE's offset to its begin, end and limit values and + propagate it to its children. + + Does nothing, if NODE is clean, i.e. NODE.otick = tree.otick . +*/ + static void -interval_stack_clear (struct interval_stack *stack) +interval_tree_inherit_offset (uintmax_t otick, struct interval_node *node) { - stack->length = 0; -} + eassert (node->parent == ITREE_NULL || node->parent->otick >= node->otick); + if (node->otick == otick) + { + eassert (node->offset == 0); + return; + } -static inline void -interval_stack_ensure_space (struct interval_stack *stack, intmax_t nelements) -{ - if (nelements > stack->size) + /* Offsets can be inherited from dirty nodes (with out of date + otick) during removal, since we do not travel down from the root + in that case. In this case rotations are performed on + potentially "dirty" nodes, where we only need to make sure the + *local* offsets are zero. */ + + if (node->offset) { - stack->size = (nelements + 1) * 2; - stack->nodes = xrealloc (stack->nodes, - stack->size * sizeof (*stack->nodes)); + node->begin += node->offset; + node->end += node->offset; + node->limit += node->offset; + if (node->left != ITREE_NULL) + node->left->offset += node->offset; + if (node->right != ITREE_NULL) + node->right->offset += node->offset; + node->offset = 0; } + /* The only thing that matters about `otick` is whether it's equal to + that of the tree. We could also "blindly" inherit from parent->otick, + but we need to tree's `otick` anyway for when there's no parent. */ + if (node->parent == ITREE_NULL || node->parent->otick == otick) + node->otick = otick; } -/* Push NODE on the STACK, while settings its visited flag to FLAG. */ +/* Update limit of NODE and its ancestors. Stop when it becomes + stable, i.e. new_limit = old_limit. */ -static inline void -interval_stack_push_flagged (struct interval_stack *stack, - struct interval_node *node, bool flag) +static void +interval_tree_propagate_limit (struct interval_node *node) { - eassert (node && node != ITREE_NULL); - - /* FIXME: While the stack used in the iterator is bounded by the tree - depth and could be easily pre-allocated to a large enough size to avoid - this "ensure" check, `interval_stack_push` is also used elsewhere to - simply collect some subset of the overlays, where it's only bounded by - the total number of overlays in the buffer (which can be large and thus - preferably not pre-allocated needlessly). */ - interval_stack_ensure_space (stack, stack->length + 1); + if (node == ITREE_NULL) + return; - stack->nodes[stack->length] = make_nav (node, flag); - stack->length++; + while (1) { + ptrdiff_t newlimit = itree_newlimit (node); + if (newlimit == node->limit) + break; + node->limit = newlimit; + if (node->parent == ITREE_NULL) + break; + node = node->parent; + } } -static inline void -interval_stack_push (struct interval_stack *stack, struct interval_node *node) +static struct interval_node* +interval_tree_validate (struct interval_tree *tree, struct interval_node *node) { - interval_stack_push_flagged (stack, node, false); -} -static inline nodeptr_and_flag -interval_stack_pop (struct interval_stack *stack) -{ - if (stack->length == 0) - return make_nav (NULL, false); - return stack->nodes[--stack->length]; + if (tree->otick == node->otick || node == ITREE_NULL) + return node; + if (node != tree->root) + interval_tree_validate (tree, node->parent); + + interval_tree_inherit_offset (tree->otick, node); + return node; } - -/* +===================================================================================+ +/* +=======================================================================+ * | Tree operations - * +===================================================================================+ */ + * +=======================================================================+ */ /* Initialize an allocated node. */ @@ -399,6 +515,9 @@ interval_node_init (struct interval_node *node, bool front_advance, bool rear_advance, Lisp_Object data) { + node->parent = ITREE_NULL; + node->left = ITREE_NULL; + node->right = ITREE_NULL; node->begin = -1; node->end = -1; node->front_advance = front_advance; @@ -426,29 +545,6 @@ interval_node_end (struct interval_tree *tree, return node->end; } -/* Safely modify a node's interval. */ - -void -interval_node_set_region (struct interval_tree *tree, - struct interval_node *node, - ptrdiff_t begin, ptrdiff_t end) -{ - interval_tree_validate (tree, node); - if (begin != node->begin) - { - interval_tree_remove (tree, node); - node->begin = min (begin, PTRDIFF_MAX - 1); - node->end = max (node->begin, end); - interval_tree_insert (tree, node); - } - else if (end != node->end) - { - node->end = max (node->begin, end); - eassert (node != ITREE_NULL); - interval_tree_propagate_limit (node); - } -} - /* Allocate an interval_tree. Free with interval_tree_destroy. */ struct interval_tree* @@ -503,15 +599,177 @@ interval_tree_size (struct interval_tree *tree) return tree->size; } -/* Insert a NODE into the TREE. +/* Perform the familiar left-rotation on node NODE. */ - Note, that inserting a node twice results in undefined behaviour. -*/ +static void +interval_tree_rotate_left (struct interval_tree *tree, + struct interval_node *node) +{ + eassert (node->right != ITREE_NULL); + + struct interval_node *right = node->right; + + interval_tree_inherit_offset (tree->otick, node); + interval_tree_inherit_offset (tree->otick, right); + + /* Turn right's left subtree into node's right subtree. */ + node->right = right->left; + if (right->left != ITREE_NULL) + right->left->parent = node; + + /* right's parent was node's parent. */ + if (right != ITREE_NULL) + right->parent = node->parent; + + /* Get the parent to point to right instead of node. */ + if (node != tree->root) + { + if (node == node->parent->left) + node->parent->left = right; + else + node->parent->right = right; + } + else + tree->root = right; + + /* Put node on right's left. */ + right->left = node; + if (node != ITREE_NULL) + node->parent = right; + + /* Order matters here. */ + interval_tree_update_limit (node); + interval_tree_update_limit (right); +} + +/* Perform the familiar right-rotation on node NODE. */ + +static void +interval_tree_rotate_right (struct interval_tree *tree, + struct interval_node *node) +{ + eassert (tree && node && node->left != ITREE_NULL); + + struct interval_node *left = node->left; + + interval_tree_inherit_offset (tree->otick, node); + interval_tree_inherit_offset (tree->otick, left); + + node->left = left->right; + if (left->right != ITREE_NULL) + left->right->parent = node; + + if (left != ITREE_NULL) + left->parent = node->parent; + if (node != tree->root) + { + if (node == node->parent->right) + node->parent->right = left; + else + node->parent->left = left; + } + else + tree->root = left; + + left->right = node; + if (node != ITREE_NULL) + node->parent = left; + + interval_tree_update_limit (left); + interval_tree_update_limit (node); +} + +/* Repair the tree after an insertion. + The new NODE was added as red, so we may have 2 reds in a row. + Rebalance the parents as needed to re-establish the RB invariants. */ + +static void +interval_tree_insert_fix (struct interval_tree *tree, + struct interval_node *node) +{ + eassert (tree->root->red == false); + + while (null_safe_is_red (node->parent)) + { + /* NODE is red and its parent is red. This is a violation of + red-black tree property #3. */ + eassert (node->red); + + if (node->parent == node->parent->parent->left) + { + /* We're on the left side of our grandparent, and OTHER is + our "uncle". */ + struct interval_node *uncle = node->parent->parent->right; + + if (null_safe_is_red (uncle)) /* case 1.a */ + { + /* Uncle and parent are red but should be black because + NODE is red. Change the colors accordingly and + proceed with the grandparent. */ + node->parent->red = false; + uncle->red = false; + node->parent->parent->red = true; + node = node->parent->parent; + } + else + { + /* Parent and uncle have different colors; parent is + red, uncle is black. */ + if (node == node->parent->right) /* case 2.a */ + { + node = node->parent; + interval_tree_rotate_left (tree, node); + } + /* case 3.a */ + node->parent->red = false; + node->parent->parent->red = true; + interval_tree_rotate_right (tree, node->parent->parent); + } + } + else + { + /* This is the symmetrical case of above. */ + struct interval_node *uncle = node->parent->parent->left; + + if (null_safe_is_red (uncle)) /* case 1.b */ + { + node->parent->red = false; + uncle->red = false; + node->parent->parent->red = true; + node = node->parent->parent; + } + else + { + if (node == node->parent->left) /* case 2.b */ + { + node = node->parent; + interval_tree_rotate_right (tree, node); + } + /* case 3.b */ + node->parent->red = false; + node->parent->parent->red = true; + interval_tree_rotate_left (tree, node->parent->parent); + } + } + } + + /* The root may have been changed to red due to the algorithm. + Set it to black so that property #5 is satisfied. */ + tree->root->red = false; + eassert (check_tree (tree, true)); /* FIXME: Too expensive. */ +} + +/* Insert a NODE into the TREE. + Note, that inserting a node twice results in undefined behaviour. */ static void interval_tree_insert (struct interval_tree *tree, struct interval_node *node) { - eassert (node && node->begin <= node->end && node != ITREE_NULL); + eassert (node->begin <= node->end && node != ITREE_NULL); + /* FIXME: The assertion below fails because `delete_all_overlays` + doesn't set left/right/parent to ITREE_NULL. */ + /* eassert (node->left == ITREE_NULL && node->right == ITREE_NULL + && node->parent == ITREE_NULL) */; eassert (check_tree (tree, true)); /* FIXME: Too expensive. */ struct interval_node *parent = ITREE_NULL; @@ -546,15 +804,20 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) node->parent = parent; node->left = ITREE_NULL; node->right = ITREE_NULL; - node->red = node != tree->root; node->offset = 0; node->limit = node->end; eassert (node->parent == ITREE_NULL || node->parent->otick >= node->otick); /* Fix/update the tree */ ++tree->size; - eassert (check_tree (tree, false)); /* FIXME: Too expensive. */ - interval_tree_insert_fix (tree, node); + if (node == tree->root) + node->red = false; + else + { + node->red = true; + eassert (check_tree (tree, false)); /* FIXME: Too expensive. */ + interval_tree_insert_fix (tree, node); + } } void @@ -567,6 +830,29 @@ itree_insert_node (struct interval_tree *tree, struct interval_node *node, interval_tree_insert (tree, node); } +/* Safely modify a node's interval. */ + +void +interval_node_set_region (struct interval_tree *tree, + struct interval_node *node, + ptrdiff_t begin, ptrdiff_t end) +{ + interval_tree_validate (tree, node); + if (begin != node->begin) + { + interval_tree_remove (tree, node); + node->begin = min (begin, PTRDIFF_MAX - 1); + node->end = max (node->begin, end); + interval_tree_insert (tree, node); + } + else if (end != node->end) + { + node->end = max (node->begin, end); + eassert (node != ITREE_NULL); + interval_tree_propagate_limit (node); + } +} + /* Return true, if NODE is a member of TREE. */ static bool @@ -584,19 +870,6 @@ interval_tree_contains (struct interval_tree *tree, struct interval_node *node) return false; } -static inline ptrdiff_t -itree_newlimit (struct interval_node *node) -{ - eassert (node != ITREE_NULL); - return max (node->end, - max (node->left == ITREE_NULL - ? PTRDIFF_MIN - : node->left->limit + node->left->offset, - node->right == ITREE_NULL - ? PTRDIFF_MIN - : node->right->limit + node->right->offset)); -} - static bool itree_limit_is_stable (struct interval_node *node) { @@ -716,24 +989,86 @@ interval_tree_remove_fix (struct interval_tree *tree, node->red = false; } -/* Remove NODE from TREE and return it. NODE must exist in TREE. */ - -struct interval_node* -interval_tree_remove (struct interval_tree *tree, struct interval_node *node) +/* Return accumulated offsets of NODE's parents. */ +static ptrdiff_t +itree_total_offset (struct interval_node *node) { - eassert (interval_tree_contains (tree, node)); - eassert (check_tree (tree, true)); /* FIXME: Too expensive. */ - - /* Find `splice`, the leaf node to splice out of the tree. When - `node` has at most one child this is `node` itself. Otherwise, - it is the in order successor of `node`. */ - interval_tree_inherit_offset (tree->otick, node); - struct interval_node *splice - = (node->left == ITREE_NULL || node->right == ITREE_NULL) - ? node - : interval_tree_subtree_min (tree->otick, node->right); + eassert (node != ITREE_NULL); + ptrdiff_t offset = 0; + while (node->parent != ITREE_NULL) + { + node = node->parent; + offset += node->offset; + } + return offset; +} - /* Find `subtree`, the only child of `splice` (may be NULL). Note: +/* Replace DEST with SOURCE as a child of DEST's parent. Adjusts + *only* the parent linkage of SOURCE and either the parent's child + link the tree root. + + Warning: DEST is left unmodified. SOURCE's child links are + unchanged. Caller is responsible for recalculation of `limit`. + Requires both nodes to be using the same effective `offset`. */ +static void +interval_tree_replace_child (struct interval_tree *tree, + struct interval_node *source, + struct interval_node *dest) +{ + eassert (tree && dest != ITREE_NULL); + eassert (source == ITREE_NULL + || itree_total_offset (source) == itree_total_offset (dest)); + + if (dest == tree->root) + tree->root = source; + else if (dest == dest->parent->left) + dest->parent->left = source; + else + dest->parent->right = source; + + if (source != ITREE_NULL) + source->parent = dest->parent; +} +/* Replace DEST with SOURCE in the tree. Copies the following fields + from DEST to SOURCE: red, parent, left, right. Also updates + parent, left and right in surrounding nodes to point to SOURCE. + + Warning: DEST is left unmodified. Caller is responsible for + recalculation of `limit`. Requires both nodes to be using the same + effective `offset`. */ +static void +interval_tree_transplant (struct interval_tree *tree, + struct interval_node *source, + struct interval_node *dest) +{ + interval_tree_replace_child (tree, source, dest); + source->left = dest->left; + if (source->left != ITREE_NULL) + source->left->parent = source; + source->right = dest->right; + if (source->right != ITREE_NULL) + source->right->parent = source; + source->red = dest->red; +} + +/* Remove NODE from TREE and return it. NODE must exist in TREE. */ + +struct interval_node* +interval_tree_remove (struct interval_tree *tree, struct interval_node *node) +{ + eassert (interval_tree_contains (tree, node)); + eassert (check_tree (tree, true)); /* FIXME: Too expensive. */ + + /* Find `splice`, the leaf node to splice out of the tree. When + `node` has at most one child this is `node` itself. Otherwise, + it is the in order successor of `node`. */ + interval_tree_inherit_offset (tree->otick, node); + struct interval_node *splice + = (node->left == ITREE_NULL || node->right == ITREE_NULL) + ? node + : interval_tree_subtree_min (tree->otick, node->right); + + /* Find `subtree`, the only child of `splice` (may be NULL). Note: `subtree` will not be modified other than changing its parent to `splice`. */ eassert (splice->left == ITREE_NULL || splice->right == ITREE_NULL); @@ -764,14 +1099,13 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) interval_tree_transplant (tree, splice, node); interval_tree_propagate_limit (subtree_parent); if (splice != subtree_parent) - interval_tree_propagate_limit (splice); + interval_tree_update_limit (splice); } interval_tree_propagate_limit (splice->parent); --tree->size; - /* Fix any black height violation caused by removing a black - node. */ + /* Fix any black height violation caused by removing a black node. */ if (removed_black) interval_tree_remove_fix (tree, subtree, subtree_parent); @@ -791,29 +1125,14 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) return node; } -static struct interval_node* -interval_tree_validate (struct interval_tree *tree, struct interval_node *node) -{ - - if (tree->otick == node->otick || node == ITREE_NULL) - return node; - if (node != tree->root) - interval_tree_validate (tree, node->parent); - - interval_tree_inherit_offset (tree->otick, node); - return node; -} - bool itree_iterator_busy_p (void) { return (iter && iter->running); } -/* Start a generator iterating all intervals in [BEGIN,END) in the - given ORDER. Only one iterator per tree can be running at any - time. -*/ +/* Start a iterator enumerating all intervals in [BEGIN,END) in the + given ORDER. Only one iterator per tree can be running at any time. */ struct itree_iterator * itree_iterator_start (struct interval_tree *tree, ptrdiff_t begin, @@ -852,29 +1171,24 @@ itree_iterator_finish (struct itree_iterator *iter) iter->running = false; } -static int -interval_tree_max_height (const struct interval_tree *tree) -{ - return 2 * log (tree->size + 1) / log (2) + 0.5; -} - -/* +===================================================================================+ +/* +=======================================================================+ * | Insert/Delete Gaps - * +===================================================================================+ */ + * +=======================================================================+ */ /* Insert a gap at POS of length LENGTH expanding all intervals intersecting it, while respecting their rear_advance and front_advance setting. */ void -interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t length) +interval_tree_insert_gap (struct interval_tree *tree, + ptrdiff_t pos, ptrdiff_t length) { if (length <= 0 || tree->root == ITREE_NULL) return; uintmax_t ootick = tree->otick; - /* FIXME: Don't allocate generator/stack anew every time. */ + /* FIXME: Don't allocate iterator/stack anew every time. */ /* Nodes with front_advance starting at pos may mess up the tree order, so we need to remove them first. */ @@ -889,7 +1203,7 @@ interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l for (int i = 0; i < saved->length; ++i) interval_tree_remove (tree, nav_nodeptr (saved->nodes[i])); - /* We can't use a generator here, because we can't effectively + /* We can't use an iterator here, because we can't effectively narrow AND shift some subtree at the same time. */ if (tree->root != ITREE_NULL) { @@ -951,16 +1265,16 @@ interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l intersecting it. */ void -interval_tree_delete_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t length) +interval_tree_delete_gap (struct interval_tree *tree, + ptrdiff_t pos, ptrdiff_t length) { if (length <= 0 || tree->root == ITREE_NULL) return; /* FIXME: Don't allocate stack anew every time. */ - /* Can't use the generator here, because by decrementing begin, we - might unintentionally bring shifted nodes back into our search - space. */ + /* Can't use the iterator here, because by decrementing begin, we + might unintentionally bring shifted nodes back into our search space. */ const int size = interval_tree_max_height (tree) + 1; struct interval_stack *stack = interval_stack_create (size); struct interval_node *node; @@ -1000,30 +1314,9 @@ interval_tree_delete_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l -/* +===================================================================================+ - * | Generator - * +===================================================================================+ */ - -/* Allocate a new generator for TREE. */ - -static struct itree_iterator * -itree_iterator_create (struct interval_tree *tree) -{ - struct itree_iterator *g = xmalloc (sizeof *g); - /* 19 here just avoids starting with a silly-small stack. - FIXME: Since this stack only needs to be about 2*max_depth - in the worst case, we could completely pre-allocate it to something - like word-bit-size * 2 and then never worry about growing it. */ - const int size = (tree ? interval_tree_max_height (tree) : 19) + 1; - - g->stack = interval_stack_create (size); - g->running = false; - g->begin = 0; - g->end = 0; - g->file = NULL; - g->line = 0; - return g; -} +/* +=======================================================================+ + * | Iterator + * +=======================================================================+ */ /* Return true, if NODE's interval intersects with [BEGIN, END). Note: We always include empty nodes at BEGIN (and not at END), @@ -1055,12 +1348,12 @@ itree_iterator_next (struct itree_iterator *g) /* The `visited` flag stored in each node is used here (and only here): We keep a "workstack" of nodes we need to consider. This stack consist of nodes of two types: nodes that we have decided - should be returned by the generator, and nodes which we may + should be returned by the iterator, and nodes which we may need to consider (including checking their children). We start an iteration with a stack containing just the root node marked as "not visited" which means that it (and its children) needs to be considered but we haven't yet decided whether it's included - in the generator's output. */ + in the iterator's output. */ do { nodeptr_and_flag nav; @@ -1082,7 +1375,7 @@ itree_iterator_next (struct itree_iterator *g) interval_stack_push_flagged (g->stack, right, false); if (interval_node_intersects (node, g->begin, g->end)) interval_stack_push_flagged (g->stack, node, true); - /* Node's children may still be off-set and we need to add it. */ + /* Node's children may still be off-set and we need to add it. */ if (left != null && g->begin <= left->limit + left->offset) interval_stack_push_flagged (g->stack, left, false); break; @@ -1124,320 +1417,3 @@ itree_iterator_narrow (struct itree_iterator *g, g->begin = max (begin, g->begin); g->end = min (end, g->end); } - - -/* +===================================================================================+ - * | Internal Functions - * +===================================================================================+ */ - -static bool -null_safe_is_red (struct interval_node *node) -{ - return node != ITREE_NULL && node->red; -} - -static bool -null_safe_is_black (struct interval_node *node) -{ - return node == ITREE_NULL || !node->red; /* NULL nodes are black */ -} - -/* Update NODE's limit attribute according to its children. */ - -static void -interval_tree_update_limit (struct interval_node *node) -{ - if (node == ITREE_NULL) - return; - - node->limit = itree_newlimit (node); -} - -/* Apply NODE's offset to its begin, end and limit values and - propagate it to its children. - - Does nothing, if NODE is clean, i.e. NODE.otick = tree.otick . -*/ - -static void -interval_tree_inherit_offset (uintmax_t otick, struct interval_node *node) -{ - eassert (node->parent == ITREE_NULL || node->parent->otick >= node->otick); - if (node->otick == otick) - { - eassert (node->offset == 0); - return; - } - - /* Offsets can be inherited from dirty nodes (with out of date - otick) during removal, since we do not travel down from the root - in that case. In this case rotations are performed on - potentially "dirty" nodes, where we only need to make sure the - *local* offsets are zero. */ - - if (node->offset) - { - node->begin += node->offset; - node->end += node->offset; - node->limit += node->offset; - if (node->left != ITREE_NULL) - node->left->offset += node->offset; - if (node->right != ITREE_NULL) - node->right->offset += node->offset; - node->offset = 0; - } - /* The only thing that matters about `otick` is whether it's equal to - that of the tree. We could also "blindly" inherit from parent->otick, - but we need to tree's `otick` anyway for when there's no parent. */ - if (node->parent == ITREE_NULL || node->parent->otick == otick) - node->otick = otick; -} - -/* Update limit of NODE and its ancestors. Stop when it becomes - stable, i.e. new_limit = old_limit. -*/ - -static void -interval_tree_propagate_limit (struct interval_node *node) -{ - if (node == ITREE_NULL) - return; - - while (1) { - ptrdiff_t newlimit = itree_newlimit (node); - if (newlimit == node->limit) - break; - node->limit = newlimit; - if (node->parent == ITREE_NULL) - break; - node = node->parent; - } -} - -/* Perform the familiar left-rotation on node NODE. */ - -static void -interval_tree_rotate_left (struct interval_tree *tree, struct interval_node *node) -{ - eassert (node->right != ITREE_NULL); - - struct interval_node *right = node->right; - - interval_tree_inherit_offset (tree->otick, node); - interval_tree_inherit_offset (tree->otick, right); - - /* Turn right's left subtree into node's right subtree. */ - node->right = right->left; - if (right->left != ITREE_NULL) - right->left->parent = node; - - /* right's parent was node's parent. */ - if (right != ITREE_NULL) - right->parent = node->parent; - - /* Get the parent to point to right instead of node. */ - if (node != tree->root) - { - if (node == node->parent->left) - node->parent->left = right; - else - node->parent->right = right; - } - else - tree->root = right; - - /* Put node on right's left. */ - right->left = node; - if (node != ITREE_NULL) - node->parent = right; - - /* Order matters here. */ - interval_tree_update_limit (node); - interval_tree_update_limit (right); -} - -/* Perform the familiar right-rotation on node NODE. */ - -static void -interval_tree_rotate_right (struct interval_tree *tree, struct interval_node *node) -{ - eassert (tree && node && node->left != ITREE_NULL); - - struct interval_node *left = node->left; - - interval_tree_inherit_offset (tree->otick, node); - interval_tree_inherit_offset (tree->otick, left); - - node->left = left->right; - if (left->right != ITREE_NULL) - left->right->parent = node; - - if (left != ITREE_NULL) - left->parent = node->parent; - if (node != tree->root) - { - if (node == node->parent->right) - node->parent->right = left; - else - node->parent->left = left; - } - else - tree->root = left; - - left->right = node; - if (node != ITREE_NULL) - node->parent = left; - - interval_tree_update_limit (left); - interval_tree_update_limit (node); -} - -/* Repair the tree after an insertion. - The new NODE was added as red, so we may have 2 reds in a row. - Rebalance the parents as needed to re-establish the RB invariants. */ - -static void -interval_tree_insert_fix (struct interval_tree *tree, struct interval_node *node) -{ - eassert (tree->root->red == false); - - while (null_safe_is_red (node->parent)) - { - /* NODE is red and its parent is red. This is a violation of - red-black tree property #3. */ - eassert (node->red); - - if (node->parent == node->parent->parent->left) - { - /* We're on the left side of our grandparent, and OTHER is - our "uncle". */ - struct interval_node *uncle = node->parent->parent->right; - - if (null_safe_is_red (uncle)) /* case 1.a */ - { - /* Uncle and parent are red but should be black because - NODE is red. Change the colors accordingly and - proceed with the grandparent. */ - node->parent->red = false; - uncle->red = false; - node->parent->parent->red = true; - node = node->parent->parent; - } - else - { - /* Parent and uncle have different colors; parent is - red, uncle is black. */ - if (node == node->parent->right) /* case 2.a */ - { - node = node->parent; - interval_tree_rotate_left (tree, node); - } - /* case 3.a */ - node->parent->red = false; - node->parent->parent->red = true; - interval_tree_rotate_right (tree, node->parent->parent); - } - } - else - { - /* This is the symmetrical case of above. */ - struct interval_node *uncle = node->parent->parent->left; - - if (null_safe_is_red (uncle)) /* case 1.b */ - { - node->parent->red = false; - uncle->red = false; - node->parent->parent->red = true; - node = node->parent->parent; - } - else - { - if (node == node->parent->left) /* case 2.b */ - { - node = node->parent; - interval_tree_rotate_right (tree, node); - } - /* case 3.b */ - node->parent->red = false; - node->parent->parent->red = true; - interval_tree_rotate_left (tree, node->parent->parent); - } - } - } - - /* The root may have been changed to red due to the algorithm. - Set it to black so that property #5 is satisfied. */ - tree->root->red = false; - eassert (check_tree (tree, true)); /* FIXME: Too expensive. */ -} - -/* Return accumulated offsets of NODE's parents. */ -static ptrdiff_t -itree_total_offset (struct interval_node *node) -{ - eassert (node != ITREE_NULL); - ptrdiff_t offset = 0; - while (node->parent != ITREE_NULL) - { - node = node->parent; - offset += node->offset; - } - return offset; -} - -/* Replace DEST with SOURCE as a child of DEST's parent. Adjusts - *only* the parent linkage of SOURCE and either the parent's child - link the tree root. - - Warning: DEST is left unmodified. SOURCE's child links are - unchanged. Caller is responsible for recalculation of `limit`. - Requires both nodes to be using the same effective `offset`. */ - -static void -interval_tree_replace_child (struct interval_tree *tree, - struct interval_node *source, - struct interval_node *dest) -{ - eassert (tree && dest != ITREE_NULL); - eassert (source == ITREE_NULL - || itree_total_offset (source) == itree_total_offset (dest)); - - if (dest == tree->root) - tree->root = source; - else if (dest == dest->parent->left) - dest->parent->left = source; - else - dest->parent->right = source; - - if (source != ITREE_NULL) - source->parent = dest->parent; -} - -/* Replace DEST with SOURCE in the tree. Copies the following fields - from DEST to SOURCE: red, parent, left, right. Also updates - parent, left and right in surrounding nodes to point to SOURCE. - - Warning: DEST is left unmodified. Caller is responsible for - recalculation of `limit`. Requires both nodes to be using the same - effective `offset`. */ -static void -interval_tree_transplant (struct interval_tree *tree, - struct interval_node *source, - struct interval_node *dest) -{ - interval_tree_replace_child (tree, source, dest); - source->left = dest->left; - if (source->left != ITREE_NULL) - source->left->parent = source; - source->right = dest->right; - if (source->right != ITREE_NULL) - source->right->parent = source; - source->red = dest->red; -} - - -/* +===================================================================================+ - * | Debugging - * +===================================================================================+ */ - -/* See Foverlay_tree in buffer.c */ commit 75bfb2614d310f0708619f66f9088784f5733c49 Author: Matt Armstrong Date: Sun Oct 16 16:03:30 2022 -0700 Rename itree iterators with itree_iterator prefix * src/itree.h: Rename struct interval_generator -> itree_iterator. Rename functions: itree_busy_p -> itree_iterator_busy_p, interval_tree_iter_start -> itree_iterator_start, interval_generator_narrow -> itree_iterator_narrow, interval_tree_iter_finish -> itree_iterator_finish, interval_generator_next -> itree_iterator_next. * src/itree.c: Use new names everywhere. * src/eval.c: ditto. diff --git a/src/eval.c b/src/eval.c index 37c25c7914..197eb6afcb 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1711,7 +1711,7 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object data, bool keyboard_quit) Lisp_Object clause = Qnil; struct handler *h; - eassert (!itree_busy_p ()); + eassert (!itree_iterator_busy_p ()); if (gc_in_progress || waiting_for_input) emacs_abort (); diff --git a/src/itree.c b/src/itree.c index 15af56e806..0ba9e662bc 100644 --- a/src/itree.c +++ b/src/itree.c @@ -146,8 +146,8 @@ static void interval_tree_replace_child (struct interval_tree *, static void interval_tree_transplant (struct interval_tree *tree, struct interval_node *source, struct interval_node *dest); -static struct interval_generator * -interval_generator_create (struct interval_tree *); +static struct itree_iterator * +itree_iterator_create (struct interval_tree *); static void interval_tree_insert (struct interval_tree *, struct interval_node *); static bool null_safe_is_red (struct interval_node *node); static bool null_safe_is_black (struct interval_node *node); @@ -165,7 +165,7 @@ struct interval_stack }; /* State used when iterating interval. */ -struct interval_generator +struct itree_iterator { struct interval_stack *stack; ptrdiff_t begin; @@ -182,12 +182,12 @@ struct interval_generator are limited by the fact we don't allow modifying the tree at the same time, making the use of nested iterations quite rare anyway. So we just use a single global iterator instead for now. */ -static struct interval_generator *iter; +static struct itree_iterator *iter; static void itree_init (void) { - iter = interval_generator_create (NULL); + iter = itree_iterator_create (NULL); } struct check_subtree_result @@ -481,7 +481,7 @@ static void interval_tree_init (struct interval_tree *tree) { interval_tree_clear (tree); - /* tree->iter = interval_generator_create (tree); */ + /* tree->iter = itree_iterator_create (tree); */ } #endif @@ -491,7 +491,7 @@ interval_tree_destroy (struct interval_tree *tree) { eassert (tree->root == ITREE_NULL); /* if (tree->iter) - * interval_generator_destroy (tree->iter); */ + * itree_iterator_destroy (tree->iter); */ xfree (tree); } @@ -805,7 +805,7 @@ interval_tree_validate (struct interval_tree *tree, struct interval_node *node) } bool -itree_busy_p (void) +itree_iterator_busy_p (void) { return (iter && iter->running); } @@ -815,13 +815,12 @@ itree_busy_p (void) time. */ -struct interval_generator * -interval_tree_iter_start (struct interval_tree *tree, - ptrdiff_t begin, ptrdiff_t end, - enum interval_tree_order order, - const char* file, int line) +struct itree_iterator * +itree_iterator_start (struct interval_tree *tree, ptrdiff_t begin, + ptrdiff_t end, enum interval_tree_order order, + const char *file, int line) { - /* struct interval_generator *iter = tree->iter; */ + /* struct itree_iterator *iter = tree->iter; */ if (iter->running) { fprintf (stderr, @@ -847,7 +846,7 @@ interval_tree_iter_start (struct interval_tree *tree, /* Stop using the iterator. */ void -interval_tree_iter_finish (struct interval_generator *iter) +itree_iterator_finish (struct itree_iterator *iter) { eassert (iter->running); iter->running = false; @@ -1007,10 +1006,10 @@ interval_tree_delete_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l /* Allocate a new generator for TREE. */ -static struct interval_generator * -interval_generator_create (struct interval_tree *tree) +static struct itree_iterator * +itree_iterator_create (struct interval_tree *tree) { - struct interval_generator *g = xmalloc (sizeof *g); + struct itree_iterator *g = xmalloc (sizeof *g); /* 19 here just avoids starting with a silly-small stack. FIXME: Since this stack only needs to be about 2*max_depth in the worst case, we could completely pre-allocate it to something @@ -1046,7 +1045,7 @@ interval_node_intersects (const struct interval_node *node, started; or NULL if there are no more nodes. */ struct interval_node * -interval_generator_next (struct interval_generator *g) +itree_iterator_next (struct itree_iterator *g) { eassert (g->running); @@ -1105,7 +1104,7 @@ interval_generator_next (struct interval_generator *g) break; } } - /* Node may have been invalidated by interval_generator_narrow + /* Node may have been invalidated by itree_iterator_narrow after it was pushed: Check if it still intersects. */ } while (node && ! interval_node_intersects (node, g->begin, g->end)); @@ -1116,8 +1115,8 @@ interval_generator_next (struct interval_generator *g) the current one. I.E. it can't grow on either side. */ void -interval_generator_narrow (struct interval_generator *g, - ptrdiff_t begin, ptrdiff_t end) +itree_iterator_narrow (struct itree_iterator *g, + ptrdiff_t begin, ptrdiff_t end) { eassert (g->running); eassert (begin >= g->begin); diff --git a/src/itree.h b/src/itree.h index 0e2e7d1f81..f98f028ea5 100644 --- a/src/itree.h +++ b/src/itree.h @@ -32,8 +32,8 @@ along with GNU Emacs. If not, see . */ node.end directly, is while the node is not part of any tree. NOTE: It is safe to read node.begin and node.end directly, if the - node came from a generator, because it validates the nodes it - returns as a side-effect. + node came from an iterator, because it validates the nodes it + returns as a side-effect. See ITREE_FOREACH. */ struct interval_node; @@ -122,14 +122,20 @@ void interval_tree_clear (struct interval_tree *); void itree_insert_node (struct interval_tree *tree, struct interval_node *node, ptrdiff_t begin, ptrdiff_t end); struct interval_node *interval_tree_remove (struct interval_tree *, struct interval_node *); -struct interval_generator *interval_tree_iter_start (struct interval_tree *, ptrdiff_t, ptrdiff_t, enum interval_tree_order, - const char* file, int line); -void interval_generator_narrow (struct interval_generator *, ptrdiff_t, ptrdiff_t); -void interval_tree_iter_finish (struct interval_generator *); -struct interval_node *interval_generator_next (struct interval_generator *); void interval_tree_insert_gap (struct interval_tree *, ptrdiff_t, ptrdiff_t); void interval_tree_delete_gap (struct interval_tree *, ptrdiff_t, ptrdiff_t); -bool itree_busy_p (void); + +/* Iteration functions. Almost all code should use ITREE_FOREACH + instead. */ +bool itree_iterator_busy_p (void); +struct itree_iterator * +itree_iterator_start (struct interval_tree *tree, ptrdiff_t begin, + ptrdiff_t end, enum interval_tree_order order, + const char *file, int line); +void itree_iterator_narrow (struct itree_iterator *, ptrdiff_t, + ptrdiff_t); +void itree_iterator_finish (struct itree_iterator *); +struct interval_node *itree_iterator_next (struct itree_iterator *); /* Iterate over the intervals between BEG and END in the tree T. N will hold successive nodes. ORDER can be one of : `ASCENDING`, @@ -162,16 +168,16 @@ bool itree_busy_p (void); if (!t) \ { } \ else \ - for (struct interval_generator *itree_iter_ \ - = interval_tree_iter_start (t, beg, end, ITREE_##order, \ + for (struct itree_iterator *itree_iter_ \ + = itree_iterator_start (t, beg, end, ITREE_##order, \ __FILE__, __LINE__); \ - ((n = interval_generator_next (itree_iter_)) \ - || (interval_tree_iter_finish (itree_iter_), false));) + ((n = itree_iterator_next (itree_iter_)) \ + || (itree_iterator_finish (itree_iter_), false));) #define ITREE_FOREACH_ABORT() \ - interval_tree_iter_finish (itree_iter_) + itree_iterator_finish (itree_iter_) #define ITREE_FOREACH_NARROW(beg, end) \ - interval_generator_narrow (itree_iter_, beg, end) + itree_iterator_narrow (itree_iter_, beg, end) #endif commit 4020dde296b9a1e5b313c39291fdc61a4fc42d96 Author: Matt Armstrong Date: Sun Oct 16 15:10:17 2022 -0700 ; * src/itree.c: remove 'inline' from functions with extern linkage diff --git a/src/itree.c b/src/itree.c index 6e2dfc6567..15af56e806 100644 --- a/src/itree.c +++ b/src/itree.c @@ -1045,7 +1045,7 @@ interval_node_intersects (const struct interval_node *node, /* Return the next node of the iterator in the order given when it was started; or NULL if there are no more nodes. */ -inline struct interval_node* +struct interval_node * interval_generator_next (struct interval_generator *g) { eassert (g->running); @@ -1115,7 +1115,7 @@ interval_generator_next (struct interval_generator *g) /* Limit G to the new interval [BEGIN, END), which must be a subset of the current one. I.E. it can't grow on either side. */ -inline void +void interval_generator_narrow (struct interval_generator *g, ptrdiff_t begin, ptrdiff_t end) { commit 258e618364a878658a5bb5f3997034d5ad17fead Author: Matt Armstrong Date: Wed Oct 12 10:06:03 2022 -0700 Delete the itree_null sentinel node, use NULL everywhere. This effort caught a few (already commited) places that were dereferencing through ITREE_NULL in a confusing way. It makes some functions have to check for NULL in more places, but in my experinece this is worth it from a code clarity point of view. In doing this I rewrote `interval_tree_remove` completely. There there was one final bug in that function that I simply could not find when I #define'd ITREE_NULL to NULL. I couldn't easily understand that function, so instead I rewrote it completely with a focus on code clarity. Bug went away. I have left the ITREE_NULL #define in code, to reduce code review noise. It is easily removed later, mechanically. * src/itree.h: #define ITREE_NULL to NULL and leave a FIXME. * src/itree.c (itree_null): Delete the itree_null static variable. (null_is_sane): Delete (and all callers). (interval_tree_insert): Insert the first node as black straight away. (itree_newlimit): Handle NULL children. (interval_tree_remove_fix): ditto. (interval_tree_insert_fix): ditto. (interval_tree_remove): Rewrite for clarity. (null_safe_is_red): New function. (null_safe_is_black): New function. (interval_tree_replace_child): Renamed from interval_tree_transplant. (interval_tree_transplant): New function that something I think is more like a full transplantation. (names are hard) diff --git a/src/itree.c b/src/itree.c index 1728a8ab3a..6e2dfc6567 100644 --- a/src/itree.c +++ b/src/itree.c @@ -140,44 +140,17 @@ static void interval_tree_propagate_limit (struct interval_node *); static void interval_tree_rotate_left (struct interval_tree *, struct interval_node *); static void interval_tree_rotate_right (struct interval_tree *, struct interval_node *); static void interval_tree_insert_fix (struct interval_tree *, struct interval_node *); -static void interval_tree_transplant (struct interval_tree *, struct interval_node *, struct interval_node *); -static struct interval_generator* interval_generator_create (struct interval_tree *); +static void interval_tree_replace_child (struct interval_tree *, + struct interval_node *, + struct interval_node *); +static void interval_tree_transplant (struct interval_tree *tree, + struct interval_node *source, + struct interval_node *dest); +static struct interval_generator * +interval_generator_create (struct interval_tree *); static void interval_tree_insert (struct interval_tree *, struct interval_node *); - -/* The sentinel node, the null node. */ -struct interval_node itree_null = { - .parent = NULL, /* never accessed */ - .left = &itree_null, - .right = &itree_null, - .begin = PTRDIFF_MIN, - .end = PTRDIFF_MIN, - .limit = PTRDIFF_MIN, /* => max(x, null.limit) = x */ - .offset = 0, - .otick = 0, - .red = false, - .rear_advance = false, - .front_advance = false, -}; - -static bool -null_is_sane (void) -{ - /* All sentinel node fields are read-only. */ - eassert (itree_null.parent == NULL); - eassert (itree_null.left == &itree_null); - eassert (itree_null.right == &itree_null); - eassert (itree_null.begin == PTRDIFF_MIN); - eassert (itree_null.end == PTRDIFF_MIN); - eassert (itree_null.limit == PTRDIFF_MIN); - eassert (itree_null.offset == 0); - eassert (itree_null.otick == 0); - eassert (itree_null.red == false); - eassert (itree_null.rear_advance == false); - eassert (itree_null.front_advance == false); - - /* if we get this far things must be good */ - return true; -} +static bool null_safe_is_red (struct interval_node *node); +static bool null_safe_is_black (struct interval_node *node); /* +------------------------------------------------------------------------------------+ */ @@ -214,8 +187,6 @@ static struct interval_generator *iter; static void itree_init (void) { - eassert (null_is_sane ()); - iter = interval_generator_create (NULL); } @@ -299,8 +270,6 @@ static bool check_tree (struct interval_tree *tree, bool check_red_black_invariants) { - eassert (null_is_sane ()); - eassert (tree != NULL); eassert (tree->size >= 0); eassert ((tree->size == 0) == (tree->root == ITREE_NULL)); @@ -549,7 +518,7 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) struct interval_node *child = tree->root; uintmax_t otick = tree->otick; /* It's the responsability of the caller to set `otick` on the node, - to "confirm" that the begin/end fields are uptodate. */ + to "confirm" that the begin/end fields are up to date. */ eassert (node->otick == otick); /* Find the insertion point, accumulate node's offset and update @@ -577,7 +546,7 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) node->parent = parent; node->left = ITREE_NULL; node->right = ITREE_NULL; - node->red = true; + node->red = node != tree->root; node->offset = 0; node->limit = node->end; eassert (node->parent == ITREE_NULL || node->parent->otick >= node->otick); @@ -620,8 +589,12 @@ itree_newlimit (struct interval_node *node) { eassert (node != ITREE_NULL); return max (node->end, - max (node->left->limit + node->left->offset, - node->right->limit + node->right->offset)); + max (node->left == ITREE_NULL + ? PTRDIFF_MIN + : node->left->limit + node->left->offset, + node->right == ITREE_NULL + ? PTRDIFF_MIN + : node->right->limit + node->right->offset)); } static bool @@ -653,17 +626,20 @@ interval_tree_remove_fix (struct interval_tree *tree, struct interval_node *node, struct interval_node *parent) { + if (parent == ITREE_NULL) + eassert (node == tree->root); + else eassert (node == ITREE_NULL || node->parent == parent); - eassert (parent == ITREE_NULL - || node == parent->left || node == parent->right); - while (parent != ITREE_NULL && !node->red) + while (parent != ITREE_NULL && null_safe_is_black (node)) { + eassert (node == parent->left || node == parent->right); + if (node == parent->left) { struct interval_node *other = parent->right; - if (other->red) /* case 1.a */ + if (null_safe_is_red (other)) /* case 1.a */ { other->red = false; parent->red = true; @@ -671,8 +647,8 @@ interval_tree_remove_fix (struct interval_tree *tree, other = parent->right; } - if (!other->left->red /* 2.a */ - && !other->right->red) + if (null_safe_is_black (other->left) /* 2.a */ + && null_safe_is_black (other->right)) { other->red = true; node = parent; @@ -681,7 +657,7 @@ interval_tree_remove_fix (struct interval_tree *tree, } else { - if (!other->right->red) /* 3.a */ + if (null_safe_is_black (other->right)) /* 3.a */ { other->left->red = false; other->red = true; @@ -700,7 +676,7 @@ interval_tree_remove_fix (struct interval_tree *tree, { struct interval_node *other = parent->left; - if (other->red) /* 1.b */ + if (null_safe_is_red (other)) /* 1.b */ { other->red = false; parent->red = true; @@ -708,8 +684,8 @@ interval_tree_remove_fix (struct interval_tree *tree, other = parent->left; } - if (!other->right->red /* 2.b */ - && !other->left->red) + if (null_safe_is_black (other->right) /* 2.b */ + && null_safe_is_black (other->left)) { other->red = true; node = parent; @@ -718,7 +694,7 @@ interval_tree_remove_fix (struct interval_tree *tree, } else { - if (!other->left->red) /* 3.b */ + if (null_safe_is_black (other->left)) /* 3.b */ { other->right->red = false; other->red = true; @@ -736,6 +712,7 @@ interval_tree_remove_fix (struct interval_tree *tree, } } + if (node != ITREE_NULL) node->red = false; } @@ -747,86 +724,70 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) eassert (interval_tree_contains (tree, node)); eassert (check_tree (tree, true)); /* FIXME: Too expensive. */ - /* `broken`, if non-NULL, holds a node that's being moved up to where a black - node used to be, which may thus require further fixups in its parents - (done in `interval_tree_remove_fix`). */ - struct interval_node *broken = NULL; - /* `broken` may be null but `interval_tree_remove_fix` still - needs to know its "parent". - Cormen et al.'s Introduction to Algorithms uses a trick where - they rely on the null sentinel node's `parent` field to hold - the right value. While this works, it breaks the rule that - the `parent` field is write-only making correctness much more tricky - and introducing a dependency on a global state (which is incompatible - with concurrency among other things), so instead we keep track of - `broken`'s parent manually. */ - struct interval_node *broken_parent = NULL; - + /* Find `splice`, the leaf node to splice out of the tree. When + `node` has at most one child this is `node` itself. Otherwise, + it is the in order successor of `node`. */ interval_tree_inherit_offset (tree->otick, node); - if (node->left == ITREE_NULL || node->right == ITREE_NULL) - { - struct interval_node *subst - = node->right == ITREE_NULL ? node->left : node->right; - if (!node->red) - { - broken = subst; - broken_parent = node->parent; /* The future parent. */ - } - interval_tree_transplant (tree, subst, node); - } - else + struct interval_node *splice + = (node->left == ITREE_NULL || node->right == ITREE_NULL) + ? node + : interval_tree_subtree_min (tree->otick, node->right); + + /* Find `subtree`, the only child of `splice` (may be NULL). Note: + `subtree` will not be modified other than changing its parent to + `splice`. */ + eassert (splice->left == ITREE_NULL || splice->right == ITREE_NULL); + struct interval_node *subtree + = (splice->left != ITREE_NULL) ? splice->left : splice->right; + + /* Save a pointer to the parent of where `subtree` will eventually + be in `subtree_parent`. */ + struct interval_node *subtree_parent + = (splice->parent != node) ? splice->parent : splice; + + /* If `splice` is black removing it may violate Red-Black + invariants, so note this for later. */ + + /* Replace `splice` with `subtree` under subtree's parent. If + `splice` is black, this creates a red-red violation, so remember + this now as the field can be overwritten when splice is + transplanted below. */ + interval_tree_replace_child (tree, subtree, splice); + bool removed_black = !splice->red; + + /* Replace `node` with `splice` in the tree and propagate limit + upwards, if necessary. Note: Limit propagation can stabilize at + any point, so we must call from bottom to top for every node that + has a new child. */ + if (splice != node) { - struct interval_node *min - = interval_tree_subtree_min (tree->otick, node->right); - struct interval_node *min_right = min->right; - struct interval_node *min_parent = min->parent; - - if (!min->red) - broken = min_right; - eassert (min != ITREE_NULL); - /* `min` should not have any offsets any more so we can move nodes - underneath it without risking changing their begin/end. */ - eassert (min->offset == 0); - if (min->parent == node) - broken_parent = min; /* The future parent. */ - else - { - interval_tree_transplant (tree, min_right, min); - broken_parent = min->parent; /* The parent. */ - min->right = node->right; - } - min->left = node->left; - min->left->parent = min; - min->red = node->red; - /* FIXME: At this point node->right->parent = min but node->right - is a parent of `min` so total_offsets gets stuck in an inf-loop! */ - interval_tree_transplant (tree, min, node); - /* We set min->right->parent after `interval_tree_transplant` so - that calls to `itree_total_offset` don't get stuck in an inf-loop. */ - if (min->right != ITREE_NULL) - min->right->parent = min; - interval_tree_update_limit (min); - /* This call "belongs" with the first `interval_tree_transplant` - (of `min_right`, done earlier in the `if`) but we prefer to do it - here ("late") because otherwise it would sometimes update part of - the tree with values that would be invalidated by the second - `interval_tree_transplant`. */ - interval_tree_propagate_limit (min_parent); + interval_tree_transplant (tree, splice, node); + interval_tree_propagate_limit (subtree_parent); + if (splice != subtree_parent) + interval_tree_propagate_limit (splice); } - interval_tree_propagate_limit (node->parent); - --tree->size; + interval_tree_propagate_limit (splice->parent); - if (broken) - { - eassert (check_tree (tree, false)); /* FIXME: Too expensive. */ - interval_tree_remove_fix (tree, broken, broken_parent); - } + --tree->size; - node->right = node->left = node->parent = NULL; + /* Fix any black height violation caused by removing a black + node. */ + if (removed_black) + interval_tree_remove_fix (tree, subtree, subtree_parent); eassert ((tree->size == 0) == (tree->root == ITREE_NULL)); eassert (check_tree (tree, true)); /* FIXME: Too expensive. */ + /* Clear fields related to the tree for sanity while debugging. */ + node->red = false; + node->right = node->left = node->parent = NULL; + node->limit = 0; + + /* Must be clean (all offsets applied). Also, some callers rely on + node's otick being the tree's otick. */ + eassert (node->otick == tree->otick); + eassert (node->offset == 0); + return node; } @@ -860,7 +821,6 @@ interval_tree_iter_start (struct interval_tree *tree, enum interval_tree_order order, const char* file, int line) { - eassert (null_is_sane ()); /* struct interval_generator *iter = tree->iter; */ if (iter->running) { @@ -1171,6 +1131,18 @@ interval_generator_narrow (struct interval_generator *g, * | Internal Functions * +===================================================================================+ */ +static bool +null_safe_is_red (struct interval_node *node) +{ + return node != ITREE_NULL && node->red; +} + +static bool +null_safe_is_black (struct interval_node *node) +{ + return node == ITREE_NULL || !node->red; /* NULL nodes are black */ +} + /* Update NODE's limit attribute according to its children. */ static void @@ -1224,9 +1196,6 @@ interval_tree_inherit_offset (uintmax_t otick, struct interval_node *node) /* Update limit of NODE and its ancestors. Stop when it becomes stable, i.e. new_limit = old_limit. - - NODE may also be the null node, in which case its parent is - used. (This feature is due to the RB algorithm.) */ static void @@ -1331,7 +1300,9 @@ interval_tree_rotate_right (struct interval_tree *tree, struct interval_node *no static void interval_tree_insert_fix (struct interval_tree *tree, struct interval_node *node) { - while (node->parent->red) + eassert (tree->root->red == false); + + while (null_safe_is_red (node->parent)) { /* NODE is red and its parent is red. This is a violation of red-black tree property #3. */ @@ -1343,7 +1314,7 @@ interval_tree_insert_fix (struct interval_tree *tree, struct interval_node *node our "uncle". */ struct interval_node *uncle = node->parent->parent->right; - if (uncle->red) /* case 1.a */ + if (null_safe_is_red (uncle)) /* case 1.a */ { /* Uncle and parent are red but should be black because NODE is red. Change the colors accordingly and @@ -1373,7 +1344,7 @@ interval_tree_insert_fix (struct interval_tree *tree, struct interval_node *node /* This is the symmetrical case of above. */ struct interval_node *uncle = node->parent->parent->left; - if (uncle->red) /* case 1.b */ + if (null_safe_is_red (uncle)) /* case 1.b */ { node->parent->red = false; uncle->red = false; @@ -1415,15 +1386,20 @@ itree_total_offset (struct interval_node *node) return offset; } -/* Link node SOURCE in DEST's place. - It's the caller's responsability to refresh the `limit`s - of DEST->parents afterwards. */ +/* Replace DEST with SOURCE as a child of DEST's parent. Adjusts + *only* the parent linkage of SOURCE and either the parent's child + link the tree root. + + Warning: DEST is left unmodified. SOURCE's child links are + unchanged. Caller is responsible for recalculation of `limit`. + Requires both nodes to be using the same effective `offset`. */ static void -interval_tree_transplant (struct interval_tree *tree, struct interval_node *source, - struct interval_node *dest) +interval_tree_replace_child (struct interval_tree *tree, + struct interval_node *source, + struct interval_node *dest) { - eassert (tree && source && dest && dest != ITREE_NULL); + eassert (tree && dest != ITREE_NULL); eassert (source == ITREE_NULL || itree_total_offset (source) == itree_total_offset (dest)); @@ -1438,6 +1414,28 @@ interval_tree_transplant (struct interval_tree *tree, struct interval_node *sour source->parent = dest->parent; } +/* Replace DEST with SOURCE in the tree. Copies the following fields + from DEST to SOURCE: red, parent, left, right. Also updates + parent, left and right in surrounding nodes to point to SOURCE. + + Warning: DEST is left unmodified. Caller is responsible for + recalculation of `limit`. Requires both nodes to be using the same + effective `offset`. */ +static void +interval_tree_transplant (struct interval_tree *tree, + struct interval_node *source, + struct interval_node *dest) +{ + interval_tree_replace_child (tree, source, dest); + source->left = dest->left; + if (source->left != ITREE_NULL) + source->left->parent = source; + source->right = dest->right; + if (source->right != ITREE_NULL) + source->right->parent = source; + source->red = dest->red; +} + /* +===================================================================================+ * | Debugging diff --git a/src/itree.h b/src/itree.h index 5733ec1530..0e2e7d1f81 100644 --- a/src/itree.h +++ b/src/itree.h @@ -95,9 +95,8 @@ struct interval_node bool_bf front_advance : 1; /* Same as for marker and overlays. */ }; -/* The sentinel node, the null node. */ -extern struct interval_node itree_null; -#define ITREE_NULL (&itree_null) +/* FIXME: replace ITREE_NULL -> NULL everywhere */ +#define ITREE_NULL NULL struct interval_tree { commit b86505387480fed81629cbc81cef6b70098bd607 Author: Stefan Monnier Date: Fri Oct 14 16:28:33 2022 -0400 src/textprop.c (get_char_property_and_overlay): Fix bug#58479 Correct `get-char-property` which failed to ignore empty overlays. * src/textprop.c (get_char_property_and_overlay): Make sure the overlay covers the character. * test/src/buffer-tests.el (buffer-tests--overlay-bug58479): New test. * src/buffer.h (buffer_has_overlays): Avoid `interval_tree_size`. diff --git a/src/buffer.h b/src/buffer.h index 288acd4f5e..deb0367d99 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -1275,7 +1275,7 @@ INLINE bool buffer_has_overlays (void) { return current_buffer->overlays - && (interval_tree_size (current_buffer->overlays) > 0); + && (current_buffer->overlays->root != ITREE_NULL); } /* Functions for accessing a character or byte, diff --git a/src/textprop.c b/src/textprop.c index 04fae52809..b34246f5bc 100644 --- a/src/textprop.c +++ b/src/textprop.c @@ -649,7 +649,8 @@ get_char_property_and_overlay (Lisp_Object position, register Lisp_Object prop, Lisp_Object tem = Foverlay_get (node->data, prop); struct sortvec *this; - if (NILP (tem) || (w && ! overlay_matches_window (w, node->data))) + if (NILP (tem) || node->end < pos + 1 + || (w && ! overlay_matches_window (w, node->data))) continue; this = (result == items ? items + 1 : items); diff --git a/test/src/buffer-tests.el b/test/src/buffer-tests.el index 9bccbdf2e8..3833f88c5c 100644 --- a/test/src/buffer-tests.el +++ b/test/src/buffer-tests.el @@ -1340,6 +1340,14 @@ with parameters from the *Messages* buffer modification." (overlay-put ov 'value i))) (should (eq 9 (get-char-property 1 'value))))) +(ert-deftest buffer-tests--overlay-bug58479 () + (with-temp-buffer + (insert "ab") + (let* ((pos (+ (point-min) 1)) + (ol (make-overlay pos pos))) + (overlay-put ol 'my-prop 'set) + (should (null (get-char-property pos 'my-prop)))))) + ;; +==========================================================================+ ;; | Other commit 65a7b5a802a15daa6274403fef822ec3c9b95469 Author: Matt Armstrong Date: Tue Oct 11 20:19:16 2022 -0700 ; * src/itree.c (check_subtree): fix logical error in eassert diff --git a/src/itree.c b/src/itree.c index deef0335cf..1728a8ab3a 100644 --- a/src/itree.c +++ b/src/itree.c @@ -277,7 +277,7 @@ check_subtree (struct interval_node *node, if (check_red_black_invariants) { eassert (left_result.black_height == right_result.black_height); - eassert (node->parent != ITREE_NULL || !node->red || !node->parent->red); + eassert (node->parent == ITREE_NULL || !node->red || !node->parent->red); } result.size = 1 + left_result.size + right_result.size; commit fda8723be640593a662d7ff9d4900b7f9e56423e Author: Matt Armstrong Date: Tue Oct 11 20:32:08 2022 -0700 ; * src/itree.c (check_tree): assert that the tree root is black diff --git a/src/itree.c b/src/itree.c index ef623d0850..deef0335cf 100644 --- a/src/itree.c +++ b/src/itree.c @@ -307,6 +307,7 @@ check_tree (struct interval_tree *tree, if (tree->root == ITREE_NULL) return true; eassert (tree->root->parent == ITREE_NULL); + eassert (!check_red_black_invariants || !tree->root->red); struct interval_node *node = tree->root; struct check_subtree_result result commit 034d50415858b18b032b116804bfefc1be421bb3 Author: Matt Armstrong Date: Tue Oct 11 11:41:47 2022 -0700 ; * .clang-format: Add ITREE_FOREACH. diff --git a/.clang-format b/.clang-format index 44200a3995..ac9f95c88a 100644 --- a/.clang-format +++ b/.clang-format @@ -6,7 +6,7 @@ BreakBeforeBinaryOperators: All BreakBeforeBraces: GNU ColumnLimit: 70 ContinuationIndentWidth: 2 -ForEachMacros: [FOR_EACH_TAIL, FOR_EACH_TAIL_SAFE] +ForEachMacros: [FOR_EACH_TAIL, FOR_EACH_TAIL_SAFE, ITREE_FOREACH] IncludeCategories: - Regex: '^$' Priority: -1 commit 12836db6e4e09378d41301b3d4e1fcff58132d3a Author: Stefan Monnier Date: Tue Oct 11 11:17:44 2022 -0400 itree.c (check_tree): Simplify * src/itree.c (struct check_subtree_result): Remove `complete`. (check_subtree): Remove `max_depth` arg (and adjust callers). Use 0 as black-depth of empty tree. Remove redundant `node->parent` check (already performed by the caller). (check_tree): Replace with `check_tree_common` (update all callers). Check the root's `parent` field. (check_tree_no_rb): Delete function, inlined in its sole caller. (interval_tree_remove): Add call to `check_tree` (without RB checks) before `interval_tree_remove_fix`. Move update of `size` field accordingly. diff --git a/src/itree.c b/src/itree.c index 9c5d8ce142..ef623d0850 100644 --- a/src/itree.c +++ b/src/itree.c @@ -221,55 +221,27 @@ itree_init (void) struct check_subtree_result { - /* Were all nodes visited? */ - bool complete; - - /* Computed node count of the tree. */ - int size; - - /* Computed limit of the tree (max END). */ - ptrdiff_t limit; - - /* Computed black height of the tree (count of black nodes from the - bottom up to the root). */ - int black_height; + int size; /* Node count of the tree. */ + ptrdiff_t limit; /* Limit of the tree (max END). */ + int black_height; /* Black height of the tree. */ }; static struct check_subtree_result check_subtree (struct interval_node *node, bool check_red_black_invariants, uintmax_t tree_otick, - int max_depth, ptrdiff_t offset, ptrdiff_t min_begin, + ptrdiff_t offset, ptrdiff_t min_begin, ptrdiff_t max_begin) { - struct check_subtree_result result = { .complete = false, - .size = 0, + struct check_subtree_result result = { .size = 0, .limit = PTRDIFF_MIN, .black_height = 0 }; if (node == ITREE_NULL) - { - /* Every nil node of a Red-Black tree is black */ - result.black_height = 1; - result.complete = true; - return result; - }; - - if (max_depth == 0) - { - result.complete = false; - return result; - } + return result; /* Validate structure. */ - eassert ( - node->parent == ITREE_NULL - || (node->parent->left == node || node->parent->right == node)); eassert (node->left == ITREE_NULL || node->left->parent == node); eassert (node->right == ITREE_NULL || node->right->parent == node); - /* No red nodes have red parents. */ - if (check_red_black_invariants && node->parent != ITREE_NULL) - eassert (!node->red || !node->parent->red); - /* Validate otick. A node's otick must be <= to the tree's otick and <= to its parent's otick. @@ -278,8 +250,7 @@ check_subtree (struct interval_node *node, doesn't always update otick. It could, but it is not clear there is a need. */ eassert (node->otick <= tree_otick); - eassert (node->parent == ITREE_NULL - || node->otick <= node->parent->otick); + eassert (node->parent == ITREE_NULL || node->otick <= node->parent->otick); eassert (node->otick != tree_otick || node->offset == 0); offset += node->offset; @@ -294,37 +265,24 @@ check_subtree (struct interval_node *node, struct check_subtree_result left_result = check_subtree (node->left, check_red_black_invariants, - tree_otick, max_depth - 1, offset, min_begin, - begin); + tree_otick, offset, min_begin, begin); struct check_subtree_result right_result = check_subtree (node->right, check_red_black_invariants, - tree_otick, max_depth - 1, offset, begin, - max_begin); + tree_otick, offset, begin, max_begin); eassert (left_result.limit <= limit); eassert (right_result.limit <= limit); + eassert (limit == max (end, max (left_result.limit, right_result.limit))); - result.complete = left_result.complete && right_result.complete; - if (result.complete) + if (check_red_black_invariants) { - result.size = 1 + left_result.size + right_result.size; - result.limit - = max (end, max (left_result.limit, right_result.limit)); - - eassert (limit == result.limit); - - if (check_red_black_invariants) - { - /* Every path from a node to a descendent leaf contains the - same number of black nodes. Often said this way: all - nodes have the same "black height". */ - eassert (left_result.black_height - == right_result.black_height); - result.black_height - = (node->red ? 0 : 1) + left_result.black_height; - } + eassert (left_result.black_height == right_result.black_height); + eassert (node->parent != ITREE_NULL || !node->red || !node->parent->red); } + result.size = 1 + left_result.size + right_result.size; + result.limit = limit; + result.black_height = (node->red ? 0 : 1) + left_result.black_height; return result; } @@ -338,8 +296,8 @@ check_subtree (struct interval_node *node, entire tree and validates all invariants. */ static bool -check_tree_common (struct interval_tree *tree, - bool check_red_black_invariants) +check_tree (struct interval_tree *tree, + bool check_red_black_invariants) { eassert (null_is_sane ()); @@ -348,48 +306,19 @@ check_tree_common (struct interval_tree *tree, eassert ((tree->size == 0) == (tree->root == ITREE_NULL)); if (tree->root == ITREE_NULL) return true; - - /* Limit the traversal depth to what 'interval_tree_max_height' - returns. Later, verify that this is enough height to traverse - the complete tree. */ - const int max_height = interval_tree_max_height (tree); - eassert (max_height >= 0); - eassert (max_height <= 120); - - /* NOTE: if this check is too expensive an easy fix is to reduce - max_height to for large trees, then relax the assertion on - result.complete. Assertions in check_subtree will still be made - at the bottom of the tree (where they are probably most - interesting), but some will be skipped closer to the root. */ + eassert (tree->root->parent == ITREE_NULL); struct interval_node *node = tree->root; struct check_subtree_result result = check_subtree (node, check_red_black_invariants, tree->otick, - max_height, node->offset, PTRDIFF_MIN, + node->offset, PTRDIFF_MIN, PTRDIFF_MAX); - eassert (result.complete); eassert (result.size == tree->size); /* The only way this function fails is eassert(). */ return true; } -/* Check the tree with all invariant checks enabled. */ -static bool -check_tree (struct interval_tree *tree) -{ - return check_tree_common (tree, true); -} - -/* Check the tree with all invariant checks enabled, except for the - red-black tree invariants. Useful for asserting the other - invariants while inserting or removing. */ -static bool -check_tree_no_rb (struct interval_tree *tree) -{ - return check_tree_common (tree, false); -} - /* +===================================================================================+ * | Stack * +===================================================================================+ */ @@ -613,7 +542,7 @@ static void interval_tree_insert (struct interval_tree *tree, struct interval_node *node) { eassert (node && node->begin <= node->end && node != ITREE_NULL); - eassert (check_tree (tree)); + eassert (check_tree (tree, true)); /* FIXME: Too expensive. */ struct interval_node *parent = ITREE_NULL; struct interval_node *child = tree->root; @@ -654,7 +583,7 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) /* Fix/update the tree */ ++tree->size; - eassert (check_tree_no_rb (tree)); + eassert (check_tree (tree, false)); /* FIXME: Too expensive. */ interval_tree_insert_fix (tree, node); } @@ -815,7 +744,7 @@ struct interval_node* interval_tree_remove (struct interval_tree *tree, struct interval_node *node) { eassert (interval_tree_contains (tree, node)); - eassert (check_tree (tree)); + eassert (check_tree (tree, true)); /* FIXME: Too expensive. */ /* `broken`, if non-NULL, holds a node that's being moved up to where a black node used to be, which may thus require further fixups in its parents @@ -884,15 +813,18 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) interval_tree_propagate_limit (min_parent); } interval_tree_propagate_limit (node->parent); + --tree->size; if (broken) - interval_tree_remove_fix (tree, broken, broken_parent); + { + eassert (check_tree (tree, false)); /* FIXME: Too expensive. */ + interval_tree_remove_fix (tree, broken, broken_parent); + } node->right = node->left = node->parent = NULL; - --tree->size; eassert ((tree->size == 0) == (tree->root == ITREE_NULL)); - eassert (check_tree (tree)); + eassert (check_tree (tree, true)); /* FIXME: Too expensive. */ return node; } @@ -1462,10 +1394,10 @@ interval_tree_insert_fix (struct interval_tree *tree, struct interval_node *node } } - /* The root may have been changed to red due to the algorithm. Set - it to black so that property #5 is satisfied. */ + /* The root may have been changed to red due to the algorithm. + Set it to black so that property #5 is satisfied. */ tree->root->red = false; - eassert (check_tree (tree)); + eassert (check_tree (tree, true)); /* FIXME: Too expensive. */ } /* Return accumulated offsets of NODE's parents. */ commit da0387f0fe79f577fae6d5453c758f600e1ae495 Author: Matt Armstrong Date: Mon Oct 10 10:45:05 2022 -0700 Stop reading and writing the itree_null.parent field entirely. With this change all fields in the itree_null sentinel are read only. This makes accessing itree_null thread safe in an obvious way. Because it took two commits from two peole to get this right, I think we can call this design fragile and difficult to reason about. Another benefit of this commit is as preparation for removing sentinel node completely, and just using NULL. * src/itree.c (itree_null): Statically initialize itree_null.parent to NULL. It is never accessed. (null_is_sane): Assert parent == NULL. (interval_tree_remove_fix): Remove unecessary assignments to parent from node->parent. These were the last places itree_null.parent were read. (interval_tree_remove): Avoid an assignment to itree_null.parent through min->right->parent. (interval_tree_transplant): Avoid an assignment to itree_null.parent through source->parent. diff --git a/src/itree.c b/src/itree.c index d9f9ec8cd6..9c5d8ce142 100644 --- a/src/itree.c +++ b/src/itree.c @@ -146,7 +146,7 @@ static void interval_tree_insert (struct interval_tree *, struct interval_node * /* The sentinel node, the null node. */ struct interval_node itree_null = { - .parent = &itree_null, + .parent = NULL, /* never accessed */ .left = &itree_null, .right = &itree_null, .begin = PTRDIFF_MIN, @@ -162,12 +162,8 @@ struct interval_node itree_null = { static bool null_is_sane (void) { - /* The sentinel node has most of its fields read-only. - - FIXME: PARENT is still read/write. It is written to - ininterval_tree_transplant, and later read. --matt - */ - /* eassert (itree_null.parent == &itree_null); */ + /* All sentinel node fields are read-only. */ + eassert (itree_null.parent == NULL); eassert (itree_null.left == &itree_null); eassert (itree_null.right == &itree_null); eassert (itree_null.begin == PTRDIFF_MIN); @@ -742,7 +738,6 @@ interval_tree_remove_fix (struct interval_tree *tree, other->red = false; parent->red = true; interval_tree_rotate_left (tree, parent); - parent = node->parent; other = parent->right; } @@ -761,7 +756,6 @@ interval_tree_remove_fix (struct interval_tree *tree, other->left->red = false; other->red = true; interval_tree_rotate_right (tree, other); - parent = node->parent; other = parent->right; } other->red = parent->red; /* 4.a */ @@ -781,7 +775,6 @@ interval_tree_remove_fix (struct interval_tree *tree, other->red = false; parent->red = true; interval_tree_rotate_right (tree, parent); - parent = node->parent; other = parent->left; } @@ -800,7 +793,6 @@ interval_tree_remove_fix (struct interval_tree *tree, other->right->red = false; other->red = true; interval_tree_rotate_left (tree, other); - parent = node->parent; other = parent->left; } @@ -881,7 +873,8 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) interval_tree_transplant (tree, min, node); /* We set min->right->parent after `interval_tree_transplant` so that calls to `itree_total_offset` don't get stuck in an inf-loop. */ - min->right->parent = min; + if (min->right != ITREE_NULL) + min->right->parent = min; interval_tree_update_limit (min); /* This call "belongs" with the first `interval_tree_transplant` (of `min_right`, done earlier in the `if`) but we prefer to do it @@ -1508,7 +1501,8 @@ interval_tree_transplant (struct interval_tree *tree, struct interval_node *sour else dest->parent->right = source; - source->parent = dest->parent; + if (source != ITREE_NULL) + source->parent = dest->parent; } commit a154259bfacf7f1406794a952e80a8197b9a83fb Author: Matt Armstrong Date: Mon Oct 10 08:32:56 2022 -0700 Simplify itree_null initialization * src/itree.c (null_is_sane): call eassert directly, check REAR_ADVANCE, FRONT_ADVANCE. Add FIXME that PARENT is still read/write. (itree_null): initialize statically (itree_init): remove initialization code, call eassert(null_is_sane()) (check_tree_common): call eassert (null_is_sane()) diff --git a/src/itree.c b/src/itree.c index 7ac400398b..d9f9ec8cd6 100644 --- a/src/itree.c +++ b/src/itree.c @@ -145,19 +145,42 @@ static struct interval_generator* interval_generator_create (struct interval_tre static void interval_tree_insert (struct interval_tree *, struct interval_node *); /* The sentinel node, the null node. */ -struct interval_node itree_null; +struct interval_node itree_null = { + .parent = &itree_null, + .left = &itree_null, + .right = &itree_null, + .begin = PTRDIFF_MIN, + .end = PTRDIFF_MIN, + .limit = PTRDIFF_MIN, /* => max(x, null.limit) = x */ + .offset = 0, + .otick = 0, + .red = false, + .rear_advance = false, + .front_advance = false, +}; static bool null_is_sane (void) { - /* The sentinel node has most of its fields read-only, except for `parent`, - `left`, `right` which are write only. */ - return itree_null.red == false - && itree_null.otick == 0 - && itree_null.offset == 0 - && itree_null.begin == PTRDIFF_MIN - && itree_null.end == PTRDIFF_MIN - && itree_null.limit == PTRDIFF_MIN; + /* The sentinel node has most of its fields read-only. + + FIXME: PARENT is still read/write. It is written to + ininterval_tree_transplant, and later read. --matt + */ + /* eassert (itree_null.parent == &itree_null); */ + eassert (itree_null.left == &itree_null); + eassert (itree_null.right == &itree_null); + eassert (itree_null.begin == PTRDIFF_MIN); + eassert (itree_null.end == PTRDIFF_MIN); + eassert (itree_null.limit == PTRDIFF_MIN); + eassert (itree_null.offset == 0); + eassert (itree_null.otick == 0); + eassert (itree_null.red == false); + eassert (itree_null.rear_advance == false); + eassert (itree_null.front_advance == false); + + /* if we get this far things must be good */ + return true; } /* +------------------------------------------------------------------------------------+ */ @@ -195,13 +218,8 @@ static struct interval_generator *iter; static void itree_init (void) { - struct interval_node *null = ITREE_NULL; - null->left = null->right = null->parent = null; - null->offset = null->otick = 0; - null->begin = PTRDIFF_MIN; - null->end = PTRDIFF_MIN; - null->limit = PTRDIFF_MIN; /* => max(x, null.limit) = x */ - null->red = false; + eassert (null_is_sane ()); + iter = interval_generator_create (NULL); } @@ -327,6 +345,8 @@ static bool check_tree_common (struct interval_tree *tree, bool check_red_black_invariants) { + eassert (null_is_sane ()); + eassert (tree != NULL); eassert (tree->size >= 0); eassert ((tree->size == 0) == (tree->root == ITREE_NULL)); commit 51a8e375ebea2e6e05eed623bddfb323b8e408f0 Author: Matt Armstrong Date: Mon Oct 10 09:07:42 2022 -0700 Check red-black invariants in most places Stefan recently disabled this but I happened to want it back soon after. * src/itree.c (check_subtree): new arg: allow_red_red (check_tree_common): renamed from check_tree, pass allow_red_red through. (check_tree): new function, pass allow_red_red=false (interval_tree_insert): check_tree -> check_tree_common with allow_red_red=true. diff --git a/src/itree.c b/src/itree.c index aa8a5f7f3b..7ac400398b 100644 --- a/src/itree.c +++ b/src/itree.c @@ -222,7 +222,8 @@ struct check_subtree_result }; static struct check_subtree_result -check_subtree (struct interval_node *node, uintmax_t tree_otick, +check_subtree (struct interval_node *node, + bool check_red_black_invariants, uintmax_t tree_otick, int max_depth, ptrdiff_t offset, ptrdiff_t min_begin, ptrdiff_t max_begin) { @@ -251,23 +252,9 @@ check_subtree (struct interval_node *node, uintmax_t tree_otick, eassert (node->left == ITREE_NULL || node->left->parent == node); eassert (node->right == ITREE_NULL || node->right->parent == node); - /* We don't normally check the RB invariants here (neither the - absence of red+red nor the equal-black-depth), so that we can use - this check even while the tree is temporarily breaking some of - those invarints. You can enable them if you want. */ - if (false) - { - /* If a node is red then both of its children are black. Red - nodes cannot have red parents. */ - if (node->red) - { - eassert (node->left == ITREE_NULL - || node->left->red == false); - eassert (node->right == ITREE_NULL - || node->right->red == false); - eassert (node->parent == ITREE_NULL || !node->parent->red); - } - } + /* No red nodes have red parents. */ + if (check_red_black_invariants && node->parent != ITREE_NULL) + eassert (!node->red || !node->parent->red); /* Validate otick. A node's otick must be <= to the tree's otick and <= to its parent's otick. @@ -292,11 +279,13 @@ check_subtree (struct interval_node *node, uintmax_t tree_otick, eassert (end <= limit); struct check_subtree_result left_result - = check_subtree (node->left, tree_otick, max_depth - 1, offset, - min_begin, begin); + = check_subtree (node->left, check_red_black_invariants, + tree_otick, max_depth - 1, offset, min_begin, + begin); struct check_subtree_result right_result - = check_subtree (node->right, tree_otick, max_depth - 1, offset, - begin, max_begin); + = check_subtree (node->right, check_red_black_invariants, + tree_otick, max_depth - 1, offset, begin, + max_begin); eassert (left_result.limit <= limit); eassert (right_result.limit <= limit); @@ -304,24 +293,29 @@ check_subtree (struct interval_node *node, uintmax_t tree_otick, result.complete = left_result.complete && right_result.complete; if (result.complete) { - /* Every path from a node to a descendent leaf contains the same - number of black nodes. Often said this way: all nodes have - the same "black height". */ - eassert (left_result.black_height == right_result.black_height); - result.black_height - = (node->red ? 0 : 1) + left_result.black_height; - result.size = 1 + left_result.size + right_result.size; result.limit = max (end, max (left_result.limit, right_result.limit)); eassert (limit == result.limit); + + if (check_red_black_invariants) + { + /* Every path from a node to a descendent leaf contains the + same number of black nodes. Often said this way: all + nodes have the same "black height". */ + eassert (left_result.black_height + == right_result.black_height); + result.black_height + = (node->red ? 0 : 1) + left_result.black_height; + } } return result; } -/* Validate invariants for TREE. +/* Validate invariants for TREE. If CHECK_RED_BLACK_INVARIANTS, red + nodes with red children are considered invalid. This runs in constant time when ENABLE_OVERLAY_CHECKING is 0 (i.e. Emacs is not configured with @@ -330,7 +324,8 @@ check_subtree (struct interval_node *node, uintmax_t tree_otick, entire tree and validates all invariants. */ static bool -check_tree (struct interval_tree *tree) +check_tree_common (struct interval_tree *tree, + bool check_red_black_invariants) { eassert (tree != NULL); eassert (tree->size >= 0); @@ -353,8 +348,9 @@ check_tree (struct interval_tree *tree) struct interval_node *node = tree->root; struct check_subtree_result result - = check_subtree (node, tree->otick, max_height, node->offset, - PTRDIFF_MIN, PTRDIFF_MAX); + = check_subtree (node, check_red_black_invariants, tree->otick, + max_height, node->offset, PTRDIFF_MIN, + PTRDIFF_MAX); eassert (result.complete); eassert (result.size == tree->size); @@ -362,6 +358,22 @@ check_tree (struct interval_tree *tree) return true; } +/* Check the tree with all invariant checks enabled. */ +static bool +check_tree (struct interval_tree *tree) +{ + return check_tree_common (tree, true); +} + +/* Check the tree with all invariant checks enabled, except for the + red-black tree invariants. Useful for asserting the other + invariants while inserting or removing. */ +static bool +check_tree_no_rb (struct interval_tree *tree) +{ + return check_tree_common (tree, false); +} + /* +===================================================================================+ * | Stack * +===================================================================================+ */ @@ -626,7 +638,7 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) /* Fix/update the tree */ ++tree->size; - eassert (check_tree (tree)); + eassert (check_tree_no_rb (tree)); interval_tree_insert_fix (tree, node); } commit 67b9e89a5aa0052b1abdcd8971a0fa83f52e13a5 Author: Matt Armstrong Date: Mon Oct 10 08:48:41 2022 -0700 Improve check_subtree * src/itree.c (struct check_subtree_result): new struct returned by check_subtree. (check_subtree): new function, renamed from recurse_check_tree. Add new black height assertions. (check_tree): assert that the tree has non-negative size, assert that limiting to interval_tree_max_height(tree) levels is enough to traverses the complete tree. diff --git a/src/itree.c b/src/itree.c index bbab70dac7..aa8a5f7f3b 100644 --- a/src/itree.c +++ b/src/itree.c @@ -205,14 +205,44 @@ itree_init (void) iter = interval_generator_create (NULL); } -static ptrdiff_t -recurse_check_tree (struct interval_node *node, uintmax_t tree_otick, - ptrdiff_t offset, ptrdiff_t min_begin, - ptrdiff_t max_begin, intmax_t *size) +struct check_subtree_result +{ + /* Were all nodes visited? */ + bool complete; + + /* Computed node count of the tree. */ + int size; + + /* Computed limit of the tree (max END). */ + ptrdiff_t limit; + + /* Computed black height of the tree (count of black nodes from the + bottom up to the root). */ + int black_height; +}; + +static struct check_subtree_result +check_subtree (struct interval_node *node, uintmax_t tree_otick, + int max_depth, ptrdiff_t offset, ptrdiff_t min_begin, + ptrdiff_t max_begin) { + struct check_subtree_result result = { .complete = false, + .size = 0, + .limit = PTRDIFF_MIN, + .black_height = 0 }; if (node == ITREE_NULL) - return PTRDIFF_MIN; - ++*size; + { + /* Every nil node of a Red-Black tree is black */ + result.black_height = 1; + result.complete = true; + return result; + }; + + if (max_depth == 0) + { + result.complete = false; + return result; + } /* Validate structure. */ eassert ( @@ -221,14 +251,35 @@ recurse_check_tree (struct interval_node *node, uintmax_t tree_otick, eassert (node->left == ITREE_NULL || node->left->parent == node); eassert (node->right == ITREE_NULL || node->right->parent == node); - /* We don't check the RB invariants here (neither the absence of - red+red nor the equal-black-depth), so that we can use this check - even while the tree is temporarily breaking some of those invarints. */ - /* Red nodes cannot have red parents. */ - /* eassert (node->parent == ITREE_NULL - || !(node->red && node->parent->red)); */ + /* We don't normally check the RB invariants here (neither the + absence of red+red nor the equal-black-depth), so that we can use + this check even while the tree is temporarily breaking some of + those invarints. You can enable them if you want. */ + if (false) + { + /* If a node is red then both of its children are black. Red + nodes cannot have red parents. */ + if (node->red) + { + eassert (node->left == ITREE_NULL + || node->left->red == false); + eassert (node->right == ITREE_NULL + || node->right->red == false); + eassert (node->parent == ITREE_NULL || !node->parent->red); + } + } - eassert (node->offset == 0 || node->otick < tree_otick); + /* Validate otick. A node's otick must be <= to the tree's otick + and <= to its parent's otick. + + Note: we cannot assert that (NODE.otick == NODE.parent.otick) + implies (NODE.offset == 0) because interval_tree_inherit_offset() + doesn't always update otick. It could, but it is not clear there + is a need. */ + eassert (node->otick <= tree_otick); + eassert (node->parent == ITREE_NULL + || node->otick <= node->parent->otick); + eassert (node->otick != tree_otick || node->offset == 0); offset += node->offset; ptrdiff_t begin = node->begin + offset; @@ -240,29 +291,74 @@ recurse_check_tree (struct interval_node *node, uintmax_t tree_otick, eassert (begin <= max_begin); eassert (end <= limit); - ptrdiff_t left_limit - = recurse_check_tree (node->left, tree_otick, offset, min_begin, - begin, size); - ptrdiff_t right_limit - = recurse_check_tree (node->right, tree_otick, offset, begin, - max_begin, size); - eassert (left_limit <= limit); - eassert (right_limit <= limit); - eassert (limit == max (end, max (left_limit, right_limit))); - return limit; + struct check_subtree_result left_result + = check_subtree (node->left, tree_otick, max_depth - 1, offset, + min_begin, begin); + struct check_subtree_result right_result + = check_subtree (node->right, tree_otick, max_depth - 1, offset, + begin, max_begin); + + eassert (left_result.limit <= limit); + eassert (right_result.limit <= limit); + + result.complete = left_result.complete && right_result.complete; + if (result.complete) + { + /* Every path from a node to a descendent leaf contains the same + number of black nodes. Often said this way: all nodes have + the same "black height". */ + eassert (left_result.black_height == right_result.black_height); + result.black_height + = (node->red ? 0 : 1) + left_result.black_height; + + result.size = 1 + left_result.size + right_result.size; + result.limit + = max (end, max (left_result.limit, right_result.limit)); + + eassert (limit == result.limit); + } + + return result; } +/* Validate invariants for TREE. + + This runs in constant time when ENABLE_OVERLAY_CHECKING is 0 + (i.e. Emacs is not configured with + "--enable_checking=yes,overlays"). In this mode it can't check all + the invariants. When ENABLE_OVERLAY_CHECKING is 1 it checks the + entire tree and validates all invariants. +*/ static bool check_tree (struct interval_tree *tree) { eassert (tree != NULL); + eassert (tree->size >= 0); eassert ((tree->size == 0) == (tree->root == ITREE_NULL)); if (tree->root == ITREE_NULL) return true; - intmax_t size = 0; - recurse_check_tree (tree->root, tree->otick, 0, - PTRDIFF_MIN, PTRDIFF_MAX, &size); + /* Limit the traversal depth to what 'interval_tree_max_height' + returns. Later, verify that this is enough height to traverse + the complete tree. */ + const int max_height = interval_tree_max_height (tree); + eassert (max_height >= 0); + eassert (max_height <= 120); + + /* NOTE: if this check is too expensive an easy fix is to reduce + max_height to for large trees, then relax the assertion on + result.complete. Assertions in check_subtree will still be made + at the bottom of the tree (where they are probably most + interesting), but some will be skipped closer to the root. */ + + struct interval_node *node = tree->root; + struct check_subtree_result result + = check_subtree (node, tree->otick, max_height, node->offset, + PTRDIFF_MIN, PTRDIFF_MAX); + eassert (result.complete); + eassert (result.size == tree->size); + + /* The only way this function fails is eassert(). */ return true; } @@ -1145,10 +1241,10 @@ interval_tree_inherit_offset (uintmax_t otick, struct interval_node *node) } /* Offsets can be inherited from dirty nodes (with out of date - otick) during insert and remove. Offsets aren't inherited - downward from the root for these operations so rotations are - performed on potentially "dirty" nodes, where we only make sure - the *local* offsets are all zero. */ + otick) during removal, since we do not travel down from the root + in that case. In this case rotations are performed on + potentially "dirty" nodes, where we only need to make sure the + *local* offsets are zero. */ if (node->offset) { commit 246acbddbeb3e9a390fe78242259182af0c2cc18 Author: Matt Armstrong Date: Sun Oct 9 10:12:32 2022 -0700 ; * test/src/buffer-tests.el: Remove unecessary `message' calls. diff --git a/test/src/buffer-tests.el b/test/src/buffer-tests.el index 01780a15cc..9bccbdf2e8 100644 --- a/test/src/buffer-tests.el +++ b/test/src/buffer-tests.el @@ -1554,10 +1554,8 @@ dicta sunt, explicabo. ")) ;; is to initially steadily increase the overlay count, then ;; steadily decrease it, then repeat. (when (and growing (= overlay-count overlay-count-limit)) - (message "now shrinking") (setq growing nil)) (when (and (not growing) (= overlay-count 0)) - (message "now growing") (setq growing t)) ;; Create or delete a random overlay according to a commit 7cbeeabc7e6271948e7bb93020c2e4e50c65f384 Author: Stefan Monnier Date: Sun Oct 9 19:45:26 2022 -0400 Tighten up handling of `otick` Move args between `build_overlay` and `add_buffer_overlay`, to try and keep buffer positions together with their buffer. Be more strict in the `otick` values passed to `interval_tree_insert`. Move a few things around to try and reduce dependencies through `.h` files. Fix a thinko bug in `check_tree`. * src/alloc.c (build_overlay): Remove `begin` and `end` args. * src/buffer.c (add_buffer_overlay): Move from `buffer.h`. Add `begin` and `end` args. (copy_overlays): Adjust accordingly. (Fmake_overlay): Use BUF_BEG and BUF_Z; adjust call to `build_overlay` and `add_buffer_overlay`. (Fmove_overlay): Use BUF_BEG and BUF_Z; Use the new `begin` and `end` args of `add_buffer_overlay` so we don't need to use `interval_node_set_region` when moving to a new buffer. (remove_buffer_overlay, set_overlay_region): Move from `buffer.h`. * src/buffer.h (set_overlay_region, add_buffer_overlay) (remove_buffer_overlay): Move to `buffer.c`. (build_overlay): Move from `lisp.h`. (maybe_alloc_buffer_overlays): Delete function (inline into its only caller). * src/itree.c (interval_tree_insert): Move declaration `from buffer.h`. (check_tree): Fix initial offset in call to `recurse_check_tree`. Remove redundant check of the `limit` value. (interval_node_init): Remove `begin` and `end` args. (interval_tree_insert): Mark it as static. Assert that the new node's `otick` should already be uptodate and its new parent as well. (itree_insert_node): New function. (interval_tree_insert_gap): Assert the otick of the removed+added nodes were uptodate and mark them as uptodate again after adjusting their positions. (interval_tree_inherit_offset): Check that the parent is at least as uptodate as the child. * src/lisp.h (build_overlay): Move to `buffer.h`. * src/itree.h (interval_node_init): Adjust accordingly. (interval_tree_insert): Remove declaration. (itree_insert_node): New declaration. diff --git a/src/alloc.c b/src/alloc.c index 50968b7e12..00f2991f25 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -3702,19 +3702,17 @@ build_symbol_with_pos (Lisp_Object symbol, Lisp_Object position) return val; } -/* Return a new overlay with specified START, END and PLIST. */ +/* Return a new (deleted) overlay with PLIST. */ Lisp_Object -build_overlay (ptrdiff_t begin, ptrdiff_t end, - bool front_advance, bool rear_advance, +build_overlay (bool front_advance, bool rear_advance, Lisp_Object plist) { struct Lisp_Overlay *p = ALLOCATE_PSEUDOVECTOR (struct Lisp_Overlay, plist, PVEC_OVERLAY); Lisp_Object overlay = make_lisp_ptr (p, Lisp_Vectorlike); struct interval_node *node = xmalloc (sizeof (*node)); - interval_node_init (node, begin, end, front_advance, - rear_advance, overlay); + interval_node_init (node, front_advance, rear_advance, overlay); p->interval = node; p->buffer = NULL; set_overlay_plist (overlay, plist); diff --git a/src/buffer.c b/src/buffer.c index f59fddcbde..e1303f2a88 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -638,6 +638,16 @@ even if it is dead. The return value is never nil. */) return buffer; } +static void +add_buffer_overlay (struct buffer *b, struct Lisp_Overlay *ov, + ptrdiff_t begin, ptrdiff_t end) +{ + eassert (! ov->buffer); + if (! b->overlays) + b->overlays = interval_tree_create (); + ov->buffer = b; + itree_insert_node (b->overlays, ov->interval, begin, end); +} /* Copy overlays of buffer FROM to buffer TO. */ @@ -650,11 +660,10 @@ copy_overlays (struct buffer *from, struct buffer *to) ITREE_FOREACH (node, from->overlays, PTRDIFF_MIN, PTRDIFF_MAX, ASCENDING) { Lisp_Object ov = node->data; - Lisp_Object copy = build_overlay (node->begin, node->end, - node->front_advance, + Lisp_Object copy = build_overlay (node->front_advance, node->rear_advance, Fcopy_sequence (OVERLAY_PLIST (ov))); - add_buffer_overlay (to, XOVERLAY (copy)); + add_buffer_overlay (to, XOVERLAY (copy), node->begin, node->end); } } @@ -897,6 +906,15 @@ does not run the hooks `kill-buffer-hook', return buf; } +static void +remove_buffer_overlay (struct buffer *b, struct Lisp_Overlay *ov) +{ + eassert (b->overlays); + eassert (ov->buffer == b); + interval_tree_remove (ov->buffer->overlays, ov->interval); + ov->buffer = NULL; +} + /* Mark OV as no longer associated with its buffer. */ static void @@ -3486,12 +3504,11 @@ for the rear of the overlay advance when text is inserted there temp = beg; beg = end; end = temp; } - ptrdiff_t obeg = clip_to_bounds (BEG, XFIXNUM (beg), b->text->z); - ptrdiff_t oend = clip_to_bounds (obeg, XFIXNUM (end), b->text->z); - ov = build_overlay (obeg, oend, - ! NILP (front_advance), + ptrdiff_t obeg = clip_to_bounds (BUF_BEG (b), XFIXNUM (beg), BUF_Z (b)); + ptrdiff_t oend = clip_to_bounds (obeg, XFIXNUM (end), BUF_Z (b)); + ov = build_overlay (! NILP (front_advance), ! NILP (rear_advance), Qnil); - add_buffer_overlay (b, XOVERLAY (ov)); + add_buffer_overlay (b, XOVERLAY (ov), obeg, oend); /* We don't need to redisplay the region covered by the overlay, because the overlay has no properties at the moment. */ @@ -3518,6 +3535,15 @@ modify_overlay (struct buffer *buf, ptrdiff_t start, ptrdiff_t end) modiff_incr (&BUF_OVERLAY_MODIFF (buf), 1); } +INLINE void +set_overlay_region (struct Lisp_Overlay *ov, ptrdiff_t begin, ptrdiff_t end) +{ + eassert (ov->buffer); + begin = clip_to_bounds (BEG, begin, ov->buffer->text->z); + end = clip_to_bounds (begin, end, ov->buffer->text->z); + interval_node_set_region (ov->buffer->overlays, ov->interval, begin, end); +} + DEFUN ("move-overlay", Fmove_overlay, Smove_overlay, 3, 4, 0, doc: /* Set the endpoints of OVERLAY to BEG and END in BUFFER. If BUFFER is omitted, leave OVERLAY in the same buffer it inhabits now. @@ -3528,7 +3554,6 @@ buffer. */) struct buffer *b, *ob = 0; Lisp_Object obuffer; specpdl_ref count = SPECPDL_INDEX (); - ptrdiff_t n_beg, n_end; ptrdiff_t o_beg UNINIT, o_end UNINIT; CHECK_OVERLAY (overlay); @@ -3555,11 +3580,14 @@ buffer. */) temp = beg; beg = end; end = temp; } - specbind (Qinhibit_quit, Qt); + specbind (Qinhibit_quit, Qt); /* FIXME: Why? */ obuffer = Foverlay_buffer (overlay); b = XBUFFER (buffer); + ptrdiff_t n_beg = clip_to_bounds (BUF_BEG (b), XFIXNUM (beg), BUF_Z (b)); + ptrdiff_t n_end = clip_to_bounds (n_beg, XFIXNUM (end), BUF_Z (b)); + if (!NILP (obuffer)) { ob = XBUFFER (obuffer); @@ -3572,13 +3600,11 @@ buffer. */) { if (! NILP (obuffer)) remove_buffer_overlay (XBUFFER (obuffer), XOVERLAY (overlay)); - add_buffer_overlay (XBUFFER (buffer), XOVERLAY (overlay)); + add_buffer_overlay (XBUFFER (buffer), XOVERLAY (overlay), n_beg, n_end); } - /* Set the overlay boundaries, which may clip them. */ - set_overlay_region (XOVERLAY (overlay), XFIXNUM (beg), XFIXNUM (end)); - - n_beg = OVERLAY_START (overlay); - n_end = OVERLAY_END (overlay); + else + interval_node_set_region (b->overlays, XOVERLAY (overlay)->interval, + n_beg, n_end); /* If the overlay has changed buffers, do a thorough redisplay. */ if (!BASE_EQ (buffer, obuffer)) diff --git a/src/buffer.h b/src/buffer.h index a4e3934cad..288acd4f5e 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -1189,6 +1189,7 @@ extern void fix_overlays_before (struct buffer *, ptrdiff_t, ptrdiff_t); extern void mmap_set_vars (bool); extern void restore_buffer (Lisp_Object); extern void set_buffer_if_live (Lisp_Object); +extern Lisp_Object build_overlay (bool, bool, Lisp_Object); /* Return B as a struct buffer pointer, defaulting to the current buffer. */ @@ -1408,40 +1409,6 @@ overlay_end (struct Lisp_Overlay *ov) return interval_node_end (ov->buffer->overlays, ov->interval); } -INLINE void -set_overlay_region (struct Lisp_Overlay *ov, ptrdiff_t begin, ptrdiff_t end) -{ - eassert (ov->buffer); - begin = clip_to_bounds (BEG, begin, ov->buffer->text->z); - end = clip_to_bounds (begin, end, ov->buffer->text->z); - interval_node_set_region (ov->buffer->overlays, ov->interval, begin, end); -} - -INLINE void -maybe_alloc_buffer_overlays (struct buffer *b) -{ - if (! b->overlays) - b->overlays = interval_tree_create (); -} - -INLINE void -add_buffer_overlay (struct buffer *b, struct Lisp_Overlay *ov) -{ - eassert (! ov->buffer); - maybe_alloc_buffer_overlays (b); - ov->buffer = b; - interval_tree_insert (b->overlays, ov->interval); -} - -INLINE void -remove_buffer_overlay (struct buffer *b, struct Lisp_Overlay *ov) -{ - eassert (b->overlays); - eassert (ov->buffer == b); - interval_tree_remove (ov->buffer->overlays, ov->interval); - ov->buffer = NULL; -} - /* Return the start of OV in its buffer, or -1 if OV is not associated with any buffer. */ diff --git a/src/itree.c b/src/itree.c index b57c3cc656..bbab70dac7 100644 --- a/src/itree.c +++ b/src/itree.c @@ -142,6 +142,7 @@ static void interval_tree_rotate_right (struct interval_tree *, struct interval_ static void interval_tree_insert_fix (struct interval_tree *, struct interval_node *); static void interval_tree_transplant (struct interval_tree *, struct interval_node *, struct interval_node *); static struct interval_generator* interval_generator_create (struct interval_tree *); +static void interval_tree_insert (struct interval_tree *, struct interval_node *); /* The sentinel node, the null node. */ struct interval_node itree_null; @@ -260,11 +261,8 @@ check_tree (struct interval_tree *tree) return true; intmax_t size = 0; - ptrdiff_t offset = tree->root->offset; - ptrdiff_t limit - = recurse_check_tree (tree->root, tree->otick, offset, - PTRDIFF_MIN, PTRDIFF_MAX, &size); - eassert (limit == tree->root->limit + offset); + recurse_check_tree (tree->root, tree->otick, 0, + PTRDIFF_MIN, PTRDIFF_MAX, &size); return true; } @@ -375,12 +373,11 @@ interval_stack_pop (struct interval_stack *stack) void interval_node_init (struct interval_node *node, - ptrdiff_t begin, ptrdiff_t end, bool front_advance, bool rear_advance, Lisp_Object data) { - node->begin = begin; - node->end = max (begin, end); + node->begin = -1; + node->end = -1; node->front_advance = front_advance; node->rear_advance = rear_advance; node->data = data; @@ -488,7 +485,7 @@ interval_tree_size (struct interval_tree *tree) Note, that inserting a node twice results in undefined behaviour. */ -void +static void interval_tree_insert (struct interval_tree *tree, struct interval_node *node) { eassert (node && node->begin <= node->end && node != ITREE_NULL); @@ -497,6 +494,9 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) struct interval_node *parent = ITREE_NULL; struct interval_node *child = tree->root; uintmax_t otick = tree->otick; + /* It's the responsability of the caller to set `otick` on the node, + to "confirm" that the begin/end fields are uptodate. */ + eassert (node->otick == otick); /* Find the insertion point, accumulate node's offset and update ancestors limit values. */ @@ -526,7 +526,7 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) node->red = true; node->offset = 0; node->limit = node->end; - node->otick = otick; + eassert (node->parent == ITREE_NULL || node->parent->otick >= node->otick); /* Fix/update the tree */ ++tree->size; @@ -534,6 +534,16 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) interval_tree_insert_fix (tree, node); } +void +itree_insert_node (struct interval_tree *tree, struct interval_node *node, + ptrdiff_t begin, ptrdiff_t end) +{ + node->begin = begin; + node->end = end; + node->otick = tree->otick; + interval_tree_insert (tree, node); +} + /* Return true, if NODE is a member of TREE. */ static bool @@ -849,6 +859,7 @@ interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l { if (length <= 0 || tree->root == ITREE_NULL) return; + uintmax_t ootick = tree->otick; /* FIXME: Don't allocate generator/stack anew every time. */ @@ -907,13 +918,16 @@ interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l } /* Reinsert nodes starting at POS having front-advance. */ + uintmax_t notick = tree->otick; nodeptr_and_flag nav; while ((nav = interval_stack_pop (saved), node = nav_nodeptr (nav))) { + eassert (node->otick == ootick); node->begin += length; if (node->end != pos || node->rear_advance) node->end += length; + node->otick = notick; interval_tree_insert (tree, node); } @@ -1123,12 +1137,19 @@ interval_tree_update_limit (struct interval_node *node) static void interval_tree_inherit_offset (uintmax_t otick, struct interval_node *node) { + eassert (node->parent == ITREE_NULL || node->parent->otick >= node->otick); if (node->otick == otick) { eassert (node->offset == 0); return; } + /* Offsets can be inherited from dirty nodes (with out of date + otick) during insert and remove. Offsets aren't inherited + downward from the root for these operations so rotations are + performed on potentially "dirty" nodes, where we only make sure + the *local* offsets are all zero. */ + if (node->offset) { node->begin += node->offset; @@ -1140,17 +1161,9 @@ interval_tree_inherit_offset (uintmax_t otick, struct interval_node *node) node->right->offset += node->offset; node->offset = 0; } - /* FIXME: I wonder when/why this condition can be false, and more - generally why we'd want to propagate offsets that may not be - fully up-to-date. --stef - - Offsets can be inherited from dirty nodes (with out of date - otick) during insert and remove. Offsets aren't inherited - downward from the root for these operations so rotations are - performed on potentially "dirty" nodes. We could fix this by - always inheriting offsets downward from the root for every insert - and remove. --matt - */ + /* The only thing that matters about `otick` is whether it's equal to + that of the tree. We could also "blindly" inherit from parent->otick, + but we need to tree's `otick` anyway for when there's no parent. */ if (node->parent == ITREE_NULL || node->parent->otick == otick) node->otick = otick; } diff --git a/src/itree.h b/src/itree.h index 7fdc9c07c9..5733ec1530 100644 --- a/src/itree.h +++ b/src/itree.h @@ -112,7 +112,7 @@ enum interval_tree_order { ITREE_PRE_ORDER, }; -void interval_node_init (struct interval_node *, ptrdiff_t, ptrdiff_t, bool, bool, Lisp_Object); +void interval_node_init (struct interval_node *, bool, bool, Lisp_Object); ptrdiff_t interval_node_begin (struct interval_tree *, struct interval_node *); ptrdiff_t interval_node_end (struct interval_tree *, struct interval_node *); void interval_node_set_region (struct interval_tree *, struct interval_node *, ptrdiff_t, ptrdiff_t); @@ -120,7 +120,8 @@ struct interval_tree *interval_tree_create (void); void interval_tree_destroy (struct interval_tree *); intmax_t interval_tree_size (struct interval_tree *); void interval_tree_clear (struct interval_tree *); -void interval_tree_insert (struct interval_tree *, struct interval_node *); +void itree_insert_node (struct interval_tree *tree, struct interval_node *node, + ptrdiff_t begin, ptrdiff_t end); struct interval_node *interval_tree_remove (struct interval_tree *, struct interval_node *); struct interval_generator *interval_tree_iter_start (struct interval_tree *, ptrdiff_t, ptrdiff_t, enum interval_tree_order, const char* file, int line); diff --git a/src/lisp.h b/src/lisp.h index 7d838afb5c..7cd7871281 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -4415,7 +4415,6 @@ extern Lisp_Object make_float (double); extern void display_malloc_warning (void); extern specpdl_ref inhibit_garbage_collection (void); extern Lisp_Object build_symbol_with_pos (Lisp_Object, Lisp_Object); -extern Lisp_Object build_overlay (ptrdiff_t, ptrdiff_t, bool, bool, Lisp_Object); extern void free_cons (struct Lisp_Cons *); extern void init_alloc_once (void); extern void init_alloc (void); commit 4f3f7aebc957732f4fbe5c799da5367f46607680 Author: Stefan Monnier Date: Sun Oct 9 00:56:24 2022 -0400 itree.c: Use `interval_tree_inherit_offset` The insertion code tried to manipulate the offset in its own way, and apparently there was a bug in it. Replace that with a call to `interval_tree_inherit_offset`, making the whole logic a bit simpler, and fixing a bug along the way (not sure where the bug was, to be honest). * src/itree.c (interval_tree_insert): Use `interval_tree_inherit_offset`. Check the tree before insert_fix. (recurse_check_tree): Don't check RB invariants. (itree_limits_are_stable): Delete function (subsumed by `check_tree`). diff --git a/src/itree.c b/src/itree.c index 4ad47b2e3f..b57c3cc656 100644 --- a/src/itree.c +++ b/src/itree.c @@ -220,9 +220,12 @@ recurse_check_tree (struct interval_node *node, uintmax_t tree_otick, eassert (node->left == ITREE_NULL || node->left->parent == node); eassert (node->right == ITREE_NULL || node->right->parent == node); + /* We don't check the RB invariants here (neither the absence of + red+red nor the equal-black-depth), so that we can use this check + even while the tree is temporarily breaking some of those invarints. */ /* Red nodes cannot have red parents. */ - eassert (node->parent == ITREE_NULL - || !(node->red && node->parent->red)); + /* eassert (node->parent == ITREE_NULL + || !(node->red && node->parent->red)); */ eassert (node->offset == 0 || node->otick < tree_otick); @@ -493,15 +496,16 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) struct interval_node *parent = ITREE_NULL; struct interval_node *child = tree->root; - ptrdiff_t offset = 0; + uintmax_t otick = tree->otick; /* Find the insertion point, accumulate node's offset and update ancestors limit values. */ while (child != ITREE_NULL) { + interval_tree_inherit_offset (otick, child); parent = child; - offset += child->offset; - child->limit = max (child->limit, node->end - offset); + eassert (child->offset == 0); + child->limit = max (child->limit, node->end); /* This suggests that nodes in the right subtree are strictly greater. But this is not true due to later rotations. */ child = node->begin <= child->begin ? child->left : child->right; @@ -521,15 +525,13 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) node->right = ITREE_NULL; node->red = true; node->offset = 0; - node->begin -= offset; - node->end -= offset; node->limit = node->end; - node->otick = tree->otick - 1; + node->otick = otick; /* Fix/update the tree */ ++tree->size; - interval_tree_insert_fix (tree, node); eassert (check_tree (tree)); + interval_tree_insert_fix (tree, node); } /* Return true, if NODE is a member of TREE. */ @@ -567,16 +569,6 @@ itree_limit_is_stable (struct interval_node *node) return (newlimit == node->limit); } -static inline bool -itree_limits_are_stable (struct interval_node *node) -{ - if (node == ITREE_NULL) - return true; - return itree_limit_is_stable (node) - && itree_limits_are_stable (node->right) - && itree_limits_are_stable (node->left); -} - static struct interval_node* interval_tree_subtree_min (uintmax_t otick, struct interval_node *node) { @@ -692,7 +684,6 @@ interval_tree_remove_fix (struct interval_tree *tree, struct interval_node* interval_tree_remove (struct interval_tree *tree, struct interval_node *node) { - /* eassert (itree_limits_are_stable (tree->root)); */ eassert (interval_tree_contains (tree, node)); eassert (check_tree (tree)); @@ -770,7 +761,6 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) --tree->size; eassert ((tree->size == 0) == (tree->root == ITREE_NULL)); - /* eassert (itree_limits_are_stable (tree->root)); */ eassert (check_tree (tree)); return node; commit fe14454101cfd9951b76549773645b2ffeed66bd Author: Matt Armstrong Date: Sat Oct 8 19:53:36 2022 -0700 Debug check overlay tree invariants * src/itree.c (check_tree): (recurse_check_tree): new functions. (interval_tree_insert): call them. (interval_tree_remove): ditto. (interval_tree_insert_fix): ditto. diff --git a/src/itree.c b/src/itree.c index 05851007f5..4ad47b2e3f 100644 --- a/src/itree.c +++ b/src/itree.c @@ -204,7 +204,67 @@ itree_init (void) iter = interval_generator_create (NULL); } - +static ptrdiff_t +recurse_check_tree (struct interval_node *node, uintmax_t tree_otick, + ptrdiff_t offset, ptrdiff_t min_begin, + ptrdiff_t max_begin, intmax_t *size) +{ + if (node == ITREE_NULL) + return PTRDIFF_MIN; + ++*size; + + /* Validate structure. */ + eassert ( + node->parent == ITREE_NULL + || (node->parent->left == node || node->parent->right == node)); + eassert (node->left == ITREE_NULL || node->left->parent == node); + eassert (node->right == ITREE_NULL || node->right->parent == node); + + /* Red nodes cannot have red parents. */ + eassert (node->parent == ITREE_NULL + || !(node->red && node->parent->red)); + + eassert (node->offset == 0 || node->otick < tree_otick); + + offset += node->offset; + ptrdiff_t begin = node->begin + offset; + ptrdiff_t end = node->end + offset; + ptrdiff_t limit = node->limit + offset; + + eassert (min_begin <= max_begin); + eassert (min_begin <= begin); + eassert (begin <= max_begin); + eassert (end <= limit); + + ptrdiff_t left_limit + = recurse_check_tree (node->left, tree_otick, offset, min_begin, + begin, size); + ptrdiff_t right_limit + = recurse_check_tree (node->right, tree_otick, offset, begin, + max_begin, size); + eassert (left_limit <= limit); + eassert (right_limit <= limit); + eassert (limit == max (end, max (left_limit, right_limit))); + return limit; +} + +static bool +check_tree (struct interval_tree *tree) +{ + eassert (tree != NULL); + eassert ((tree->size == 0) == (tree->root == ITREE_NULL)); + if (tree->root == ITREE_NULL) + return true; + + intmax_t size = 0; + ptrdiff_t offset = tree->root->offset; + ptrdiff_t limit + = recurse_check_tree (tree->root, tree->otick, offset, + PTRDIFF_MIN, PTRDIFF_MAX, &size); + eassert (limit == tree->root->limit + offset); + return true; +} + /* +===================================================================================+ * | Stack * +===================================================================================+ */ @@ -429,6 +489,7 @@ void interval_tree_insert (struct interval_tree *tree, struct interval_node *node) { eassert (node && node->begin <= node->end && node != ITREE_NULL); + eassert (check_tree (tree)); struct interval_node *parent = ITREE_NULL; struct interval_node *child = tree->root; @@ -468,6 +529,7 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) /* Fix/update the tree */ ++tree->size; interval_tree_insert_fix (tree, node); + eassert (check_tree (tree)); } /* Return true, if NODE is a member of TREE. */ @@ -632,6 +694,7 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) { /* eassert (itree_limits_are_stable (tree->root)); */ eassert (interval_tree_contains (tree, node)); + eassert (check_tree (tree)); /* `broken`, if non-NULL, holds a node that's being moved up to where a black node used to be, which may thus require further fixups in its parents @@ -708,6 +771,7 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) eassert ((tree->size == 0) == (tree->root == ITREE_NULL)); /* eassert (itree_limits_are_stable (tree->root)); */ + eassert (check_tree (tree)); return node; } @@ -1277,6 +1341,7 @@ interval_tree_insert_fix (struct interval_tree *tree, struct interval_node *node /* The root may have been changed to red due to the algorithm. Set it to black so that property #5 is satisfied. */ tree->root->red = false; + eassert (check_tree (tree)); } /* Return accumulated offsets of NODE's parents. */ commit 92a0bf6ce2d2afe909d8a075ad9760eb003a8e73 Author: Matt Armstrong Date: Sat Oct 8 09:28:29 2022 -0700 ; * test/src/buffer-tests.el (test-overlay-randomly): new test. diff --git a/test/src/buffer-tests.el b/test/src/buffer-tests.el index a12d15bc79..01780a15cc 100644 --- a/test/src/buffer-tests.el +++ b/test/src/buffer-tests.el @@ -1508,6 +1508,98 @@ with parameters from the *Messages* buffer modification." (ovshould nonempty-eob-end 4 5) (ovshould empty-eob 5 5))))) +(ert-deftest test-overlay-randomly () + "Exercise overlay code, but perform few assertions. + +This test works best when Emacs is configured with +--enable-checking=yes. This is a little bit like fuzz testing, +except this test has no way to reduce to a minimal failng test +case. Regardless, by exercising many corner cases bugs can be +found using Emacs' internal consistency assertions." + (let* ( + ;; The size and slack for the test buffer size. + (buffer-size-target 1000) + (buffer-size-slack 200) + + ;; Use up to 100 overlays. We need not use more to observe + ;; reasonable variation in the overlay data structures. + (overlay-count-limit 100) + + ;; This test maintains a vector of overlays. Each iteration + ;; may append or erase one overlay. + (overlays (make-vector overlay-count-limit nil)) + (overlay-count 0) + + ;; The test is either slowly growing or shrinking the overlay + ;; count. Deletions still occur while growing, and additions + ;; still occur while shrinking. The GROWING variable only + ;; controls the relative probability of doing one or the + ;; other. + (growing t) + + ;; Loop up to 1M times. + (iteration-count 0) + (iteration-target 100000)) + (with-temp-buffer + (while (< (buffer-size) buffer-size-target) + (insert "Sed ut perspiciatis, unde omnis iste natus error sit voluptatem +accusantium doloremque laudantium, totam rem aperiam eaque ipsa, +quae ab illo inventore veritatis et quasi architecto beatae vitae +dicta sunt, explicabo. ")) + + (while (< iteration-count iteration-target) + (cl-incf iteration-count) + + ;; Toggle GROWING if we've reached a size boundary. The idea + ;; is to initially steadily increase the overlay count, then + ;; steadily decrease it, then repeat. + (when (and growing (= overlay-count overlay-count-limit)) + (message "now shrinking") + (setq growing nil)) + (when (and (not growing) (= overlay-count 0)) + (message "now growing") + (setq growing t)) + + ;; Create or delete a random overlay according to a + ;; probability chosen by GROWING. + (let ((create-overlay (>= (random 100) (if growing 40 60)))) + (cond + ;; Possibly create a new overlay in a random place in the + ;; buffer. We have two easy choices. We can choose the + ;; overlay BEGIN randomly, then choose its END among the + ;; valid remaining buffer posiitions. Or we could choose + ;; the overlay width randomly, then choose a valid BEGIN. + ;; We take the former approach, because the overlay data + ;; structure is ordered primarily by BEGIN. + ((and create-overlay (< overlay-count overlay-count-limit)) + (let* ((begin (random (buffer-size))) + (end (+ begin (random (- (buffer-size) begin)))) + (ov (make-overlay begin end nil + (= 0 (random 2)) (= 0 (random 2))))) + (aset overlays overlay-count ov) + (cl-incf overlay-count))) + ((and (not create-overlay) (> overlay-count 0)) + + ;; Possibly delete a random overlay. + (let* ((last-index (1- overlay-count)) + (index (random overlay-count)) + (ov (aref overlays index))) + (when (< index last-index) + (aset overlays index (aref overlays last-index))) + (aset overlays last-index nil) + (cl-decf overlay-count) + (delete-overlay ov))))) + + ;; Modify the buffer on occasion, which exercises the + ;; insert/remove gap logic in the overlay implementation. + (when (and (< (buffer-size) (+ buffer-size-target buffer-size-slack)) + (zerop (random 10))) + (goto-char (1+ (random (buffer-size)))) + (insert (+ ?a (random 26)))) + (when (and (> (buffer-size) (- buffer-size-target buffer-size-slack)) + (zerop (random 10))) + (goto-char (1+ (random (buffer-size)))) + (delete-char 1)))))) commit 30f52202775155c1d301af3634d0122c3d7851f8 Author: Matt Armstrong Date: Sat Oct 8 09:15:26 2022 -0700 Comment change: explain inheriting "dirty" offsets ; * src/itree.c (interval_generator_next): explain why the code handles inheriting offsets from dirty nodes. diff --git a/src/itree.c b/src/itree.c index de16af5b0c..05851007f5 100644 --- a/src/itree.c +++ b/src/itree.c @@ -1086,8 +1086,17 @@ interval_tree_inherit_offset (uintmax_t otick, struct interval_node *node) node->right->offset += node->offset; node->offset = 0; } - /* FIXME: I wonder when/why this condition can be false, and more generally - why we'd want to propagate offsets that may not be fully up-to-date. */ + /* FIXME: I wonder when/why this condition can be false, and more + generally why we'd want to propagate offsets that may not be + fully up-to-date. --stef + + Offsets can be inherited from dirty nodes (with out of date + otick) during insert and remove. Offsets aren't inherited + downward from the root for these operations so rotations are + performed on potentially "dirty" nodes. We could fix this by + always inheriting offsets downward from the root for every insert + and remove. --matt + */ if (node->parent == ITREE_NULL || node->parent->otick == otick) node->otick = otick; } commit 208de5de59ae85887d637ec93690e17c418a7ab6 Author: Stefan Monnier Date: Fri Oct 7 19:15:07 2022 -0400 itree: Try and detect non-local exits during itree iterations * src/itree.c (itree_busy_p): New function. * src/eval.c (signal_or_quit): Use it. * src/itree.h (itree_busy_p): Declare it. diff --git a/src/eval.c b/src/eval.c index 7da1d8fb98..37c25c7914 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1711,6 +1711,7 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object data, bool keyboard_quit) Lisp_Object clause = Qnil; struct handler *h; + eassert (!itree_busy_p ()); if (gc_in_progress || waiting_for_input) emacs_abort (); diff --git a/src/itree.c b/src/itree.c index d955c57539..de16af5b0c 100644 --- a/src/itree.c +++ b/src/itree.c @@ -725,6 +725,12 @@ interval_tree_validate (struct interval_tree *tree, struct interval_node *node) return node; } +bool +itree_busy_p (void) +{ + return (iter && iter->running); +} + /* Start a generator iterating all intervals in [BEGIN,END) in the given ORDER. Only one iterator per tree can be running at any time. diff --git a/src/itree.h b/src/itree.h index 9e40b87cc4..7fdc9c07c9 100644 --- a/src/itree.h +++ b/src/itree.h @@ -129,6 +129,7 @@ void interval_tree_iter_finish (struct interval_generator *); struct interval_node *interval_generator_next (struct interval_generator *); void interval_tree_insert_gap (struct interval_tree *, ptrdiff_t, ptrdiff_t); void interval_tree_delete_gap (struct interval_tree *, ptrdiff_t, ptrdiff_t); +bool itree_busy_p (void); /* Iterate over the intervals between BEG and END in the tree T. N will hold successive nodes. ORDER can be one of : `ASCENDING`, @@ -148,9 +149,7 @@ void interval_tree_delete_gap (struct interval_tree *, ptrdiff_t, ptrdiff_t); be able to run ELisp code (or GC for that matter). - If you need to exit the loop early, you *have* to call `ITREE_ABORT` just before exiting (e.g. with `break` or `return`). - - Non-local exits are not supported within the body of the loop, - unless the caller makes sure `ITREE_ABORT` is called via some - kind of unwind_protect. + - Non-local exits are not supported within the body of the loop. - Don't modify the tree during the iteration. */ #define ITREE_FOREACH(n, t, beg, end, order) \ commit 780d3d8df2e9222a4643f0d0e9caf7628085d7bf Author: Matt Armstrong Date: Thu Oct 6 15:47:20 2022 -0700 ; * src/itree.c: Add comment describing when noverlay is O(N) diff --git a/src/itree.c b/src/itree.c index 79e39d6e2a..d955c57539 100644 --- a/src/itree.c +++ b/src/itree.c @@ -62,6 +62,40 @@ along with GNU Emacs. If not, see . */ complexity of O(K*log(N)) for this operation, where K is the size of the result set and N the size of the tree. + ==== FIXME: bug#58342 some important operations remain slow === + + The amortized costs of Emacs' previous-overlay-change and + next-overlay-change functions are O(N) with this data structure. + The root problem is that we only have an order for the BEG field, + but not the END. The previous/next overlay change operations need + to find the nearest point where there is *either* an interval BEG + or END point, but there is no efficient way to narrow the search + space over END postions. + + Consider the case where next-overlay-change is called at POS, all + interval BEG positions are less than pos POS and all interval END + posistions are after. These END positions have no order, and so + *every* interval must be examined. This is at least O(N). The + previous-overlay-change case is similar. The root issue is that + the iterative "narrowing" approach is not guaranteed to reduce the + search space in logarithmic time, since END is not ordered in the + tree. + + One might argue that the LIMIT value will do this narrowing, but + this narrowing is O(K*log(N)) where K is the size of the result + set. If we are interested in finding the node in a range with the + smallest END, we might have to examine all K nodes in that range. + In the case of the *-overlay-channge functions, K may well be equal + to N. + + Ideally, a tree based data structure for overlays would have + O(log(N)) performance for previous-overlay-change and + next-overlay-change, as these are called in performance sensitive + situations such as redisplay. The only way I can think of + achieving this is by keeping one ordering by BEG and a separate + ordering by END, and then performing logic quite similar to the + current Emacs overlays-before and overlays-after lists. + ==== Adjusting intervals ==== Since this data-structure will be used for overlays in an Emacs commit c0d5026321e5f5c5cfbf012f06d91fdead01eae4 Author: Matt Armstrong Date: Thu Oct 6 13:18:46 2022 -0700 Use a bool instead of a bitfield * src/itree.c (struct interval_generator): use a bool instead of a bitfield, since space is not an issue. diff --git a/src/itree.c b/src/itree.c index 3098fe1cf4..79e39d6e2a 100644 --- a/src/itree.c +++ b/src/itree.c @@ -145,7 +145,7 @@ struct interval_generator ptrdiff_t end; uintmax_t otick; /* A copy of the tree's `otick`. */ enum interval_tree_order order; - bool_bf running : 1; + bool running; const char* file; int line; }; commit cab3b67652da1d82a854e7d2007d080e2a3c6605 Author: Matt Armstrong Date: Thu Oct 6 13:12:54 2022 -0700 ; * src/itree.c: change comments for clarity. diff --git a/src/itree.c b/src/itree.c index a782410860..3098fe1cf4 100644 --- a/src/itree.c +++ b/src/itree.c @@ -24,7 +24,7 @@ along with GNU Emacs. If not, see . */ /* Intervals of the form [BEGIN, END), are stored as nodes inside a RB - tree, sorted by BEGIN . The core operation of this tree (besides + tree, ordered by BEGIN. The core operation of this tree (besides insert, remove, etc.) is finding all intervals intersecting with some given interval. In order to perform this operation efficiently, every node stores a third value called LIMIT. (See @@ -65,10 +65,10 @@ along with GNU Emacs. If not, see . */ ==== Adjusting intervals ==== Since this data-structure will be used for overlays in an Emacs - buffer, a second core operation implements the ability to insert or - delete gaps in resp. from the tree. This models the insertion - resp. deletion of text in a buffer and the effects it may have on - the positions of overlays. + buffer, a second core operation is the ability to insert and delete + gaps in the tree. This models the insertion and deletion of text + in a buffer and the effects it may have on the positions of + overlays. Consider this: Something gets inserted at position P into a buffer and assume that all overlays occur strictly after P. Ordinarily, @@ -79,10 +79,10 @@ along with GNU Emacs. If not, see . */ The OFFSET of some some subtree, represented by its root, is the amount of shift that needs to be applied to its BEGIN, END and - LIMIT values, in order to get to the real values. Coming back to - the example, all we would need to do in this case, is to increment - the OFFSET of the tree's root, without any traversal of the tree - itself. + LIMIT values, in order to get to the actual buffer positions. + Coming back to the example, all we would need to do in this case, + is to increment the OFFSET of the tree's root, without any + traversal of the tree itself. As a consequence, the real values of BEGIN, END and LIMIT of some NODE need to be computed by incrementing them by the sum of NODE's commit 0fcd6de93b998a03f7e7c086522e803602974150 Author: Matt Armstrong Date: Thu Oct 6 13:05:19 2022 -0700 ; * src/itree.h (struct interval_node): document field invariants. diff --git a/src/itree.h b/src/itree.h index 9b79551f77..9e40b87cc4 100644 --- a/src/itree.h +++ b/src/itree.h @@ -25,7 +25,8 @@ along with GNU Emacs. If not, see . */ #include "lisp.h" -/* The tree and node structs are mainly here, so they can be allocated. +/* The tree and node structs are mainly here, so they can be + allocated. NOTE: The only time where it is safe to modify node.begin and node.end directly, is while the node is not part of any tree. @@ -33,14 +34,56 @@ along with GNU Emacs. If not, see . */ NOTE: It is safe to read node.begin and node.end directly, if the node came from a generator, because it validates the nodes it returns as a side-effect. -*/ + */ struct interval_node; struct interval_node { + /* The normal parent, left and right links found in binary trees. + See also `red`, below, which completes the Red-Black tree + representation. */ struct interval_node *parent; struct interval_node *left; struct interval_node *right; + + /* The following five fields comprise the interval abstraction. + + BEGIN, END are buffer positions describing the range. When a + node is in a tree these fields are read only, written only by + itree functions. + + The LIMIT, OFFSET and OTICK fields should be considered internal + to itree.c and used only by itree functions. + + LIMIT is a buffer position, the maximum of END of this node and + its children. See itree.c for its use. + + OFFSET is in buffer position units, and will be non-zero only + when the node is dirty. + + OTICK determines whether BEGIN, END, LIMIT and OFFSET are + considered dirty. A node is clean when its OTICK is equal to the + OTICK of its tree (see struct interval_tree). Otherwise, it is + dirty. + + In a clean node, BEGIN, END and LIMIT are correct buffer + positions, and OFFSET is zero. The parent of a clean node is + also clean, recursively. + + In a dirty node, the node's OTICK won't equal its tree's OTICK, + and its OFFSET may be non-zero. At all times the descendents of + a dirty node are also dirty. BEGIN, END and LIMIT require + adjustment before use as buffer positions. + + NOTE: BEGIN and END must not be modified while the node is part + of a tree. Use interval_tree_insert_gap and + interval_tree_delete_gap instead. + + NOTE: The interval generators ensure nodes are clean before + yielding them, so BEGIN and END may be safely used as buffer + positions then. + */ + ptrdiff_t begin; /* The beginning of this interval. */ ptrdiff_t end; /* The end of the interval. */ ptrdiff_t limit; /* The maximum end in this subtree. */ commit 6dff825a9943434cfccd64916c506ab10977acf8 Author: Matt Armstrong Date: Thu Oct 6 09:36:24 2022 -0700 ; * src/itree.h: include "lisp.h" for Lisp_Object diff --git a/src/itree.c b/src/itree.c index ed31ef1156..a782410860 100644 --- a/src/itree.c +++ b/src/itree.c @@ -19,7 +19,7 @@ along with GNU Emacs. If not, see . */ #include #include -#include "lisp.h" + #include "itree.h" /* diff --git a/src/itree.h b/src/itree.h index 8f6bb667d6..9b79551f77 100644 --- a/src/itree.h +++ b/src/itree.h @@ -23,6 +23,8 @@ along with GNU Emacs. If not, see . */ #include #include +#include "lisp.h" + /* The tree and node structs are mainly here, so they can be allocated. NOTE: The only time where it is safe to modify node.begin and commit 8bd114b98a31a31c1091e937891b369a165add3a Author: Stefan Monnier Date: Wed Oct 5 23:52:01 2022 -0400 itree.c: Get rid of the trick using null->parent * src/itree.c (interval_tree_remove_fix): Add a `parent` argument. Change the loop so it always keeps both `node` and `parent` in sync, thus avoiding the use of `node->parent` on the initial node (since that one can be null). (interval_tree_remove): Manually keep track of the `broken` node's parent to pass it to `interval_tree_remove_fix`. diff --git a/src/itree.c b/src/itree.c index a0c3d6ab5e..ed31ef1156 100644 --- a/src/itree.c +++ b/src/itree.c @@ -116,9 +116,7 @@ static bool null_is_sane (void) { /* The sentinel node has most of its fields read-only, except for `parent`, - `left`, `right` which are write only. - BEWARE: The `parent` field is actually used both for read&write - in the code of `interval_tree_remove`. */ + `left`, `right` which are write only. */ return itree_null.red == false && itree_null.otick == 0 && itree_null.offset == 0 @@ -499,30 +497,36 @@ interval_tree_subtree_min (uintmax_t otick, struct interval_node *node) so re-balance the parents to re-establish the RB invariants. */ static void -interval_tree_remove_fix (struct interval_tree *tree, struct interval_node *node) +interval_tree_remove_fix (struct interval_tree *tree, + struct interval_node *node, + struct interval_node *parent) { - /* BEWARE: null->parent is usually write-only, *BUT* - NODE here can be the NULL node, in which case its `parent` field - has to be properly set to point to the intended "parent" node. */ - while (node != tree->root && !node->red) + eassert (node == ITREE_NULL || node->parent == parent); + eassert (parent == ITREE_NULL + || node == parent->left || node == parent->right); + + while (parent != ITREE_NULL && !node->red) { - if (node == node->parent->left) + if (node == parent->left) { - struct interval_node *other = node->parent->right; + struct interval_node *other = parent->right; if (other->red) /* case 1.a */ { other->red = false; - node->parent->red = true; - interval_tree_rotate_left (tree, node->parent); - other = node->parent->right; + parent->red = true; + interval_tree_rotate_left (tree, parent); + parent = node->parent; + other = parent->right; } if (!other->left->red /* 2.a */ && !other->right->red) { other->red = true; - node = node->parent; + node = parent; + eassert (node != ITREE_NULL); + parent = node->parent; } else { @@ -531,32 +535,37 @@ interval_tree_remove_fix (struct interval_tree *tree, struct interval_node *node other->left->red = false; other->red = true; interval_tree_rotate_right (tree, other); - other = node->parent->right; + parent = node->parent; + other = parent->right; } - other->red = node->parent->red; /* 4.a */ - node->parent->red = false; + other->red = parent->red; /* 4.a */ + parent->red = false; other->right->red = false; - interval_tree_rotate_left (tree, node->parent); + interval_tree_rotate_left (tree, parent); node = tree->root; + parent = ITREE_NULL; } } else { - struct interval_node *other = node->parent->left; + struct interval_node *other = parent->left; if (other->red) /* 1.b */ { other->red = false; - node->parent->red = true; - interval_tree_rotate_right (tree, node->parent); - other = node->parent->left; + parent->red = true; + interval_tree_rotate_right (tree, parent); + parent = node->parent; + other = parent->left; } if (!other->right->red /* 2.b */ && !other->left->red) { other->red = true; - node = node->parent; + node = parent; + eassert (node != ITREE_NULL); + parent = node->parent; } else { @@ -565,14 +574,16 @@ interval_tree_remove_fix (struct interval_tree *tree, struct interval_node *node other->right->red = false; other->red = true; interval_tree_rotate_left (tree, other); - other = node->parent->left; + parent = node->parent; + other = parent->left; } - other->red = node->parent->red; /* 4.b */ - node->parent->red = false; + other->red = parent->red; /* 4.b */ + parent->red = false; other->left->red = false; - interval_tree_rotate_right (tree, node->parent); + interval_tree_rotate_right (tree, parent); node = tree->root; + parent = ITREE_NULL; } } } @@ -590,15 +601,18 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) /* `broken`, if non-NULL, holds a node that's being moved up to where a black node used to be, which may thus require further fixups in its parents - (done in `interval_tree_remove_fix`). - BEWARE: `null->parent` is usually write-only, *BUT* in this function, - we use `null->parent` to simplify the code for the case where the - "broken" node is null: `broken->parent` is set typically in - `interval_tree_transplant` and then used in - `interval_tree_remove_fix`. - This trick is described in Cormen et al.'s Introduction to Algorithms. */ - + (done in `interval_tree_remove_fix`). */ struct interval_node *broken = NULL; + /* `broken` may be null but `interval_tree_remove_fix` still + needs to know its "parent". + Cormen et al.'s Introduction to Algorithms uses a trick where + they rely on the null sentinel node's `parent` field to hold + the right value. While this works, it breaks the rule that + the `parent` field is write-only making correctness much more tricky + and introducing a dependency on a global state (which is incompatible + with concurrency among other things), so instead we keep track of + `broken`'s parent manually. */ + struct interval_node *broken_parent = NULL; interval_tree_inherit_offset (tree->otick, node); if (node->left == ITREE_NULL || node->right == ITREE_NULL) @@ -606,8 +620,10 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) struct interval_node *subst = node->right == ITREE_NULL ? node->left : node->right; if (!node->red) - broken = subst; - /* BEWARE: Here is one place we may set `null->parent`. */ + { + broken = subst; + broken_parent = node->parent; /* The future parent. */ + } interval_tree_transplant (tree, subst, node); } else @@ -623,10 +639,12 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) /* `min` should not have any offsets any more so we can move nodes underneath it without risking changing their begin/end. */ eassert (min->offset == 0); - if (min->parent != node) + if (min->parent == node) + broken_parent = min; /* The future parent. */ + else { - /* BEWARE: Here is one place we may set `null->parent`. */ interval_tree_transplant (tree, min_right, min); + broken_parent = min->parent; /* The parent. */ min->right = node->right; } min->left = node->left; @@ -637,7 +655,6 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) interval_tree_transplant (tree, min, node); /* We set min->right->parent after `interval_tree_transplant` so that calls to `itree_total_offset` don't get stuck in an inf-loop. */ - /* BEWARE: Here is one place we may set `null->parent`. */ min->right->parent = min; interval_tree_update_limit (min); /* This call "belongs" with the first `interval_tree_transplant` @@ -650,9 +667,7 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) interval_tree_propagate_limit (node->parent); if (broken) - /* BEWARE: Here is where we may end up relying on the `null->parent` - set earlier. */ - interval_tree_remove_fix (tree, broken); + interval_tree_remove_fix (tree, broken, broken_parent); node->right = node->left = node->parent = NULL; --tree->size; commit a1f1fdd291a708684262c2494eae5bba32857cd7 Author: Stefan Monnier Date: Wed Oct 5 23:48:47 2022 -0400 * src/itree.c (interval_tree_remove_fix): Move before first use diff --git a/src/itree.c b/src/itree.c index 545eb55b0d..a0c3d6ab5e 100644 --- a/src/itree.c +++ b/src/itree.c @@ -106,7 +106,6 @@ static void interval_tree_propagate_limit (struct interval_node *); static void interval_tree_rotate_left (struct interval_tree *, struct interval_node *); static void interval_tree_rotate_right (struct interval_tree *, struct interval_node *); static void interval_tree_insert_fix (struct interval_tree *, struct interval_node *); -static void interval_tree_remove_fix (struct interval_tree *, struct interval_node *); static void interval_tree_transplant (struct interval_tree *, struct interval_node *, struct interval_node *); static struct interval_generator* interval_generator_create (struct interval_tree *); @@ -495,6 +494,92 @@ interval_tree_subtree_min (uintmax_t otick, struct interval_node *node) return node; } +/* Repair the tree after a deletion. + The black-depth of NODE is one less than that of its sibling, + so re-balance the parents to re-establish the RB invariants. */ + +static void +interval_tree_remove_fix (struct interval_tree *tree, struct interval_node *node) +{ + /* BEWARE: null->parent is usually write-only, *BUT* + NODE here can be the NULL node, in which case its `parent` field + has to be properly set to point to the intended "parent" node. */ + while (node != tree->root && !node->red) + { + if (node == node->parent->left) + { + struct interval_node *other = node->parent->right; + + if (other->red) /* case 1.a */ + { + other->red = false; + node->parent->red = true; + interval_tree_rotate_left (tree, node->parent); + other = node->parent->right; + } + + if (!other->left->red /* 2.a */ + && !other->right->red) + { + other->red = true; + node = node->parent; + } + else + { + if (!other->right->red) /* 3.a */ + { + other->left->red = false; + other->red = true; + interval_tree_rotate_right (tree, other); + other = node->parent->right; + } + other->red = node->parent->red; /* 4.a */ + node->parent->red = false; + other->right->red = false; + interval_tree_rotate_left (tree, node->parent); + node = tree->root; + } + } + else + { + struct interval_node *other = node->parent->left; + + if (other->red) /* 1.b */ + { + other->red = false; + node->parent->red = true; + interval_tree_rotate_right (tree, node->parent); + other = node->parent->left; + } + + if (!other->right->red /* 2.b */ + && !other->left->red) + { + other->red = true; + node = node->parent; + } + else + { + if (!other->left->red) /* 3.b */ + { + other->right->red = false; + other->red = true; + interval_tree_rotate_left (tree, other); + other = node->parent->left; + } + + other->red = node->parent->red; /* 4.b */ + node->parent->red = false; + other->left->red = false; + interval_tree_rotate_right (tree, node->parent); + node = tree->root; + } + } + } + + node->red = false; +} + /* Remove NODE from TREE and return it. NODE must exist in TREE. */ struct interval_node* @@ -1130,92 +1215,6 @@ interval_tree_insert_fix (struct interval_tree *tree, struct interval_node *node tree->root->red = false; } -/* Repair the tree after a deletion. - The black-depth of NODE is one less than that of its sibling, - so re-balance the parents to re-establish the RB invariants. */ - -static void -interval_tree_remove_fix (struct interval_tree *tree, struct interval_node *node) -{ - /* BEWARE: null->parent is usually write-only, *BUT* - NODE here can be the NULL node, in which case its `parent` field - has to be properly set to point to the intended "parent" node. */ - while (node != tree->root && !node->red) - { - if (node == node->parent->left) - { - struct interval_node *other = node->parent->right; - - if (other->red) /* case 1.a */ - { - other->red = false; - node->parent->red = true; - interval_tree_rotate_left (tree, node->parent); - other = node->parent->right; - } - - if (!other->left->red /* 2.a */ - && !other->right->red) - { - other->red = true; - node = node->parent; - } - else - { - if (!other->right->red) /* 3.a */ - { - other->left->red = false; - other->red = true; - interval_tree_rotate_right (tree, other); - other = node->parent->right; - } - other->red = node->parent->red; /* 4.a */ - node->parent->red = false; - other->right->red = false; - interval_tree_rotate_left (tree, node->parent); - node = tree->root; - } - } - else - { - struct interval_node *other = node->parent->left; - - if (other->red) /* 1.b */ - { - other->red = false; - node->parent->red = true; - interval_tree_rotate_right (tree, node->parent); - other = node->parent->left; - } - - if (!other->right->red /* 2.b */ - && !other->left->red) - { - other->red = true; - node = node->parent; - } - else - { - if (!other->left->red) /* 3.b */ - { - other->right->red = false; - other->red = true; - interval_tree_rotate_left (tree, other); - other = node->parent->left; - } - - other->red = node->parent->red; /* 4.b */ - node->parent->red = false; - other->left->red = false; - interval_tree_rotate_right (tree, node->parent); - node = tree->root; - } - } - } - - node->red = false; -} - /* Return accumulated offsets of NODE's parents. */ static ptrdiff_t itree_total_offset (struct interval_node *node) commit 1f31534f510fdd9ed3166f761d736c0dda322db5 Author: Stefan Monnier Date: Wed Oct 5 22:55:54 2022 -0400 itree.c: Fix corner case errors in offsets In some cases, `interval_tree_remove` could cause some nodes to inherit fewer (or additional) offsets than the should because nodes were transplanted between two parts of the tree where offsets had not been propagated "equally". So we remove/apply all offsets along the path between the two points of a transplant before doing the transplant. * src/itree.c (interval_tree_subtree_min): Move before first use; delete the declaration; add an `otick` argument, and use it to update offsets along the way. (interval_tree_remove): Update all offsets on the way from `node` to `min`. Reorder some of the operations so that when we transplant `min` to `node` those nodes are in the proper state where `interval_tree_transplant` can do its sanity checks. (itree_total_offset): New function. (interval_tree_transplant): Use it to sanity check that improper offsets aren't accidentally inherited/lost because of the transplant. (itree_newlimit): New function. (itree_limit_is_stable, interval_tree_update_limit) (interval_tree_propagate_limit): Use it. (null_is_sane): Remove `inline` annotation; it's not needed. (interval_tree_inherit_offset): Sanity check that `offset` is 0 when `otick` is uptodate. Skip the unneeded increments when the offset is 0. (interval_tree_insert_fix): Add sanity check that we indeed have 2 reds. diff --git a/src/itree.c b/src/itree.c index d6c2dd8e30..545eb55b0d 100644 --- a/src/itree.c +++ b/src/itree.c @@ -29,7 +29,7 @@ along with GNU Emacs. If not, see . */ some given interval. In order to perform this operation efficiently, every node stores a third value called LIMIT. (See https://en.wikipedia.org/wiki/Interval_tree#Augmented_tree and its - source Introduction to Algorithms (Section 14.3), Cormen et al. .) + source Introduction to Algorithms, Cormen et al. .) ==== Finding intervals ==== @@ -108,17 +108,18 @@ static void interval_tree_rotate_right (struct interval_tree *, struct interval_ static void interval_tree_insert_fix (struct interval_tree *, struct interval_node *); static void interval_tree_remove_fix (struct interval_tree *, struct interval_node *); static void interval_tree_transplant (struct interval_tree *, struct interval_node *, struct interval_node *); -static struct interval_node *interval_tree_subtree_min (struct interval_node *); static struct interval_generator* interval_generator_create (struct interval_tree *); /* The sentinel node, the null node. */ struct interval_node itree_null; -static inline bool +static bool null_is_sane (void) { /* The sentinel node has most of its fields read-only, except for `parent`, - `left`, `right` which are write only. */ + `left`, `right` which are write only. + BEWARE: The `parent` field is actually used both for read&write + in the code of `interval_tree_remove`. */ return itree_null.red == false && itree_null.otick == 0 && itree_null.offset == 0 @@ -455,14 +456,21 @@ interval_tree_contains (struct interval_tree *tree, struct interval_node *node) return false; } -static inline bool +static inline ptrdiff_t +itree_newlimit (struct interval_node *node) +{ + eassert (node != ITREE_NULL); + return max (node->end, + max (node->left->limit + node->left->offset, + node->right->limit + node->right->offset)); +} + +static bool itree_limit_is_stable (struct interval_node *node) { if (node == ITREE_NULL) return true; - ptrdiff_t newlimit = max (node->end, - max (node->left->limit + node->left->offset, - node->right->limit + node->right->offset)); + ptrdiff_t newlimit = itree_newlimit (node); return (newlimit == node->limit); } @@ -476,6 +484,17 @@ itree_limits_are_stable (struct interval_node *node) && itree_limits_are_stable (node->left); } +static struct interval_node* +interval_tree_subtree_min (uintmax_t otick, struct interval_node *node) +{ + if (node == ITREE_NULL) + return node; + while ((interval_tree_inherit_offset (otick, node), + node->left != ITREE_NULL)) + node = node->left; + return node; +} + /* Remove NODE from TREE and return it. NODE must exist in TREE. */ struct interval_node* @@ -508,33 +527,33 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) } else { - struct interval_node *min = interval_tree_subtree_min (node->right); + struct interval_node *min + = interval_tree_subtree_min (tree->otick, node->right); struct interval_node *min_right = min->right; struct interval_node *min_parent = min->parent; if (!min->red) broken = min_right; eassert (min != ITREE_NULL); - if (min->parent == node) - { - if (min_right == ITREE_NULL) - /* BEWARE: Here is one place we set `null->parent`. */ - min_right->parent = min; - else - eassert (min_right->parent == min); - } - else + /* `min` should not have any offsets any more so we can move nodes + underneath it without risking changing their begin/end. */ + eassert (min->offset == 0); + if (min->parent != node) { /* BEWARE: Here is one place we may set `null->parent`. */ interval_tree_transplant (tree, min_right, min); min->right = node->right; - min->right->parent = min; } - interval_tree_inherit_offset (tree->otick, min); - interval_tree_transplant (tree, min, node); min->left = node->left; min->left->parent = min; min->red = node->red; + /* FIXME: At this point node->right->parent = min but node->right + is a parent of `min` so total_offsets gets stuck in an inf-loop! */ + interval_tree_transplant (tree, min, node); + /* We set min->right->parent after `interval_tree_transplant` so + that calls to `itree_total_offset` don't get stuck in an inf-loop. */ + /* BEWARE: Here is one place we may set `null->parent`. */ + min->right->parent = min; interval_tree_update_limit (min); /* This call "belongs" with the first `interval_tree_transplant` (of `min_right`, done earlier in the `if`) but we prefer to do it @@ -544,7 +563,6 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) interval_tree_propagate_limit (min_parent); } interval_tree_propagate_limit (node->parent); - /* eassert (itree_limits_are_stable (tree->root)); */ if (broken) /* BEWARE: Here is where we may end up relying on the `null->parent` @@ -899,8 +917,7 @@ interval_tree_update_limit (struct interval_node *node) if (node == ITREE_NULL) return; - node->limit = max (node->end, max (node->left->limit + node->left->offset, - node->right->limit + node->right->offset)); + node->limit = itree_newlimit (node); } /* Apply NODE's offset to its begin, end and limit values and @@ -913,16 +930,22 @@ static void interval_tree_inherit_offset (uintmax_t otick, struct interval_node *node) { if (node->otick == otick) - return; + { + eassert (node->offset == 0); + return; + } - node->begin += node->offset; - node->end += node->offset; - node->limit += node->offset; - if (node->left != ITREE_NULL) - node->left->offset += node->offset; - if (node->right != ITREE_NULL) - node->right->offset += node->offset; - node->offset = 0; + if (node->offset) + { + node->begin += node->offset; + node->end += node->offset; + node->limit += node->offset; + if (node->left != ITREE_NULL) + node->left->offset += node->offset; + if (node->right != ITREE_NULL) + node->right->offset += node->offset; + node->offset = 0; + } /* FIXME: I wonder when/why this condition can be false, and more generally why we'd want to propagate offsets that may not be fully up-to-date. */ if (node->parent == ITREE_NULL || node->parent->otick == otick) @@ -943,9 +966,7 @@ interval_tree_propagate_limit (struct interval_node *node) return; while (1) { - ptrdiff_t newlimit = max (node->end, - max (node->left->limit + node->left->offset, - node->right->limit + node->right->offset)); + ptrdiff_t newlimit = itree_newlimit (node); if (newlimit == node->limit) break; node->limit = newlimit; @@ -1044,6 +1065,7 @@ interval_tree_insert_fix (struct interval_tree *tree, struct interval_node *node { /* NODE is red and its parent is red. This is a violation of red-black tree property #3. */ + eassert (node->red); if (node->parent == node->parent->parent->left) { @@ -1194,6 +1216,20 @@ interval_tree_remove_fix (struct interval_tree *tree, struct interval_node *node node->red = false; } +/* Return accumulated offsets of NODE's parents. */ +static ptrdiff_t +itree_total_offset (struct interval_node *node) +{ + eassert (node != ITREE_NULL); + ptrdiff_t offset = 0; + while (node->parent != ITREE_NULL) + { + node = node->parent; + offset += node->offset; + } + return offset; +} + /* Link node SOURCE in DEST's place. It's the caller's responsability to refresh the `limit`s of DEST->parents afterwards. */ @@ -1203,6 +1239,8 @@ interval_tree_transplant (struct interval_tree *tree, struct interval_node *sour struct interval_node *dest) { eassert (tree && source && dest && dest != ITREE_NULL); + eassert (source == ITREE_NULL + || itree_total_offset (source) == itree_total_offset (dest)); if (dest == tree->root) tree->root = source; @@ -1214,17 +1252,6 @@ interval_tree_transplant (struct interval_tree *tree, struct interval_node *sour source->parent = dest->parent; } - -static struct interval_node* -interval_tree_subtree_min (struct interval_node *node) -{ - if (node == ITREE_NULL) - return node; - while (node->left != ITREE_NULL) - node = node->left; - return node; -} - /* +===================================================================================+ * | Debugging commit 5642b4a255171f5593ae56ae76c98daf7f4cd6ad Author: Stefan Monnier Date: Wed Oct 5 16:35:31 2022 -0400 itree.c: Fix incomplete update of `limit`s in corner cases `interval_tree_remove` called `interval_tree_propagate_limit (subst)` and `interval_tree_propagate_limit (min_right)` but both of those nodes are moved without touching their subtrees, so their `limit`s are "stable" causing `interval_tree_propagate_limit` to do nothing. Indeed we don't need to update those nodes's `limit`s but we *do* need to update their parents since those nodes have been moved. Incidentally, this removes some uses of `null->parent` :-) There are more uses of `null->parent`, tho, so I added more comments explaining them (with the help of the matching section of the book from which the algorithm was taken). * src/itree.c (interval_tree_update_limit): Remove unused arg `tree`. (interval_tree_rotate_left, interval_tree_rotate_right): Adjust callers. (interval_tree_contains): Mark as static. (itree_limit_is_stable, itree_limits_are_stable): New functions. (interval_tree_remove): Fix incomplete update of `limit`s in corner cases. (interval_generator_next): Add sanity check to make sure the `limit`s were properly updated. * src/itree.h (interval_tree_contains): Remove declaration. diff --git a/src/itree.c b/src/itree.c index dcad848c21..d6c2dd8e30 100644 --- a/src/itree.c +++ b/src/itree.c @@ -100,7 +100,7 @@ along with GNU Emacs. If not, see . */ static struct interval_node *interval_tree_validate (struct interval_tree *, struct interval_node *); static bool interval_node_intersects (const struct interval_node *, ptrdiff_t, ptrdiff_t); static int interval_tree_max_height (const struct interval_tree *); -static void interval_tree_update_limit (const struct interval_tree *, struct interval_node *); +static void interval_tree_update_limit (struct interval_node *); static void interval_tree_inherit_offset (uintmax_t otick, struct interval_node *); static void interval_tree_propagate_limit (struct interval_node *); static void interval_tree_rotate_left (struct interval_tree *, struct interval_node *); @@ -440,7 +440,7 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) /* Return true, if NODE is a member of TREE. */ -bool +static bool interval_tree_contains (struct interval_tree *tree, struct interval_node *node) { eassert (node); @@ -455,13 +455,45 @@ interval_tree_contains (struct interval_tree *tree, struct interval_node *node) return false; } +static inline bool +itree_limit_is_stable (struct interval_node *node) +{ + if (node == ITREE_NULL) + return true; + ptrdiff_t newlimit = max (node->end, + max (node->left->limit + node->left->offset, + node->right->limit + node->right->offset)); + return (newlimit == node->limit); +} + +static inline bool +itree_limits_are_stable (struct interval_node *node) +{ + if (node == ITREE_NULL) + return true; + return itree_limit_is_stable (node) + && itree_limits_are_stable (node->right) + && itree_limits_are_stable (node->left); +} + /* Remove NODE from TREE and return it. NODE must exist in TREE. */ struct interval_node* interval_tree_remove (struct interval_tree *tree, struct interval_node *node) { + /* eassert (itree_limits_are_stable (tree->root)); */ eassert (interval_tree_contains (tree, node)); + /* `broken`, if non-NULL, holds a node that's being moved up to where a black + node used to be, which may thus require further fixups in its parents + (done in `interval_tree_remove_fix`). + BEWARE: `null->parent` is usually write-only, *BUT* in this function, + we use `null->parent` to simplify the code for the case where the + "broken" node is null: `broken->parent` is set typically in + `interval_tree_transplant` and then used in + `interval_tree_remove_fix`. + This trick is described in Cormen et al.'s Introduction to Algorithms. */ + struct interval_node *broken = NULL; interval_tree_inherit_offset (tree->otick, node); @@ -471,29 +503,30 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) = node->right == ITREE_NULL ? node->left : node->right; if (!node->red) broken = subst; + /* BEWARE: Here is one place we may set `null->parent`. */ interval_tree_transplant (tree, subst, node); - interval_tree_propagate_limit - /* FIXME: null->parent is supposed to be write only! */ - (subst == ITREE_NULL ? ITREE_NULL->parent : subst); } else { struct interval_node *min = interval_tree_subtree_min (node->right); struct interval_node *min_right = min->right; + struct interval_node *min_parent = min->parent; if (!min->red) - broken = min->right; + broken = min_right; eassert (min != ITREE_NULL); if (min->parent == node) { if (min_right == ITREE_NULL) - ITREE_NULL->parent = min; /* set parent, if min_right = null */ + /* BEWARE: Here is one place we set `null->parent`. */ + min_right->parent = min; else eassert (min_right->parent == min); } else { - interval_tree_transplant (tree, min->right, min); + /* BEWARE: Here is one place we may set `null->parent`. */ + interval_tree_transplant (tree, min_right, min); min->right = node->right; min->right->parent = min; } @@ -502,19 +535,27 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) min->left = node->left; min->left->parent = min; min->red = node->red; - interval_tree_propagate_limit - /* FIXME: null->parent is supposed to be write only! */ - (min_right == ITREE_NULL ? ITREE_NULL->parent : min_right); - interval_tree_propagate_limit (min); + interval_tree_update_limit (min); + /* This call "belongs" with the first `interval_tree_transplant` + (of `min_right`, done earlier in the `if`) but we prefer to do it + here ("late") because otherwise it would sometimes update part of + the tree with values that would be invalidated by the second + `interval_tree_transplant`. */ + interval_tree_propagate_limit (min_parent); } + interval_tree_propagate_limit (node->parent); + /* eassert (itree_limits_are_stable (tree->root)); */ if (broken) + /* BEWARE: Here is where we may end up relying on the `null->parent` + set earlier. */ interval_tree_remove_fix (tree, broken); node->right = node->left = node->parent = NULL; --tree->size; eassert ((tree->size == 0) == (tree->root == ITREE_NULL)); + /* eassert (itree_limits_are_stable (tree->root)); */ return node; } @@ -794,6 +835,7 @@ interval_generator_next (struct interval_generator *g) struct interval_node * const right = node->right; interval_tree_inherit_offset (g->otick, node); + eassert (itree_limit_is_stable (node)); switch (g->order) { case ITREE_ASCENDING: @@ -852,8 +894,7 @@ interval_generator_narrow (struct interval_generator *g, /* Update NODE's limit attribute according to its children. */ static void -interval_tree_update_limit (const struct interval_tree *tree, - struct interval_node *node) +interval_tree_update_limit (struct interval_node *node) { if (node == ITREE_NULL) return; @@ -952,8 +993,8 @@ interval_tree_rotate_left (struct interval_tree *tree, struct interval_node *nod node->parent = right; /* Order matters here. */ - interval_tree_update_limit (tree, node); - interval_tree_update_limit (tree, right); + interval_tree_update_limit (node); + interval_tree_update_limit (right); } /* Perform the familiar right-rotation on node NODE. */ @@ -988,12 +1029,13 @@ interval_tree_rotate_right (struct interval_tree *tree, struct interval_node *no if (node != ITREE_NULL) node->parent = left; - interval_tree_update_limit (tree, left); - interval_tree_update_limit (tree, node); + interval_tree_update_limit (left); + interval_tree_update_limit (node); } -/* Repair the tree after an insertion. Part of the RB-Tree - algorithm. */ +/* Repair the tree after an insertion. + The new NODE was added as red, so we may have 2 reds in a row. + Rebalance the parents as needed to re-establish the RB invariants. */ static void interval_tree_insert_fix (struct interval_tree *tree, struct interval_node *node) @@ -1066,12 +1108,16 @@ interval_tree_insert_fix (struct interval_tree *tree, struct interval_node *node tree->root->red = false; } -/* Repair the tree after a deletion. Part of the RB-Tree - algorithm. */ +/* Repair the tree after a deletion. + The black-depth of NODE is one less than that of its sibling, + so re-balance the parents to re-establish the RB invariants. */ static void interval_tree_remove_fix (struct interval_tree *tree, struct interval_node *node) { + /* BEWARE: null->parent is usually write-only, *BUT* + NODE here can be the NULL node, in which case its `parent` field + has to be properly set to point to the intended "parent" node. */ while (node != tree->root && !node->red) { if (node == node->parent->left) @@ -1148,7 +1194,9 @@ interval_tree_remove_fix (struct interval_tree *tree, struct interval_node *node node->red = false; } -/* Link node SOURCE in DEST's place. */ +/* Link node SOURCE in DEST's place. + It's the caller's responsability to refresh the `limit`s + of DEST->parents afterwards. */ static void interval_tree_transplant (struct interval_tree *tree, struct interval_node *source, diff --git a/src/itree.h b/src/itree.h index a04ff6827c..8f6bb667d6 100644 --- a/src/itree.h +++ b/src/itree.h @@ -76,7 +76,6 @@ void interval_tree_destroy (struct interval_tree *); intmax_t interval_tree_size (struct interval_tree *); void interval_tree_clear (struct interval_tree *); void interval_tree_insert (struct interval_tree *, struct interval_node *); -bool interval_tree_contains (struct interval_tree *, struct interval_node *); struct interval_node *interval_tree_remove (struct interval_tree *, struct interval_node *); struct interval_generator *interval_tree_iter_start (struct interval_tree *, ptrdiff_t, ptrdiff_t, enum interval_tree_order, const char* file, int line); commit aa5a32ca2c07691f3f10b7ec0c423ade11479220 Author: Stefan Monnier Date: Wed Oct 5 12:12:01 2022 -0400 itree.c: Clarify how the sentinel is used * src/itree.c (null_is_sane): New function. (interval_tree_iter_start): Use it to make sure we haven't garbled null. (itree_init): Fully initialize `itree_null` here... (interval_tree_clear): ...instead of here. (interval_tree_propagate_limit): Remove special code that read NULL->parent. (interval_tree_remove): Do it explicitly before the call in those two places where it can happen. diff --git a/src/itree.c b/src/itree.c index 58456fbc6e..dcad848c21 100644 --- a/src/itree.c +++ b/src/itree.c @@ -114,6 +114,19 @@ static struct interval_generator* interval_generator_create (struct interval_tre /* The sentinel node, the null node. */ struct interval_node itree_null; +static inline bool +null_is_sane (void) +{ + /* The sentinel node has most of its fields read-only, except for `parent`, + `left`, `right` which are write only. */ + return itree_null.red == false + && itree_null.otick == 0 + && itree_null.offset == 0 + && itree_null.begin == PTRDIFF_MIN + && itree_null.end == PTRDIFF_MIN + && itree_null.limit == PTRDIFF_MIN; +} + /* +------------------------------------------------------------------------------------+ */ typedef uintptr_t nodeptr_and_flag; @@ -149,7 +162,13 @@ static struct interval_generator *iter; static void itree_init (void) { - itree_null.parent = itree_null.left = itree_null.right = ITREE_NULL; + struct interval_node *null = ITREE_NULL; + null->left = null->right = null->parent = null; + null->offset = null->otick = 0; + null->begin = PTRDIFF_MIN; + null->end = PTRDIFF_MIN; + null->limit = PTRDIFF_MIN; /* => max(x, null.limit) = x */ + null->red = false; iter = interval_generator_create (NULL); } @@ -310,6 +329,7 @@ interval_node_set_region (struct interval_tree *tree, else if (end != node->end) { node->end = max (node->begin, end); + eassert (node != ITREE_NULL); interval_tree_propagate_limit (node); } } @@ -334,16 +354,7 @@ interval_tree_create (void) void interval_tree_clear (struct interval_tree *tree) { - /* FIXME: Why is this done? */ - struct interval_node *null = ITREE_NULL; - null->left = null->right = null->parent = null; - null->offset = null->otick = 0; - null->begin = PTRDIFF_MIN; - null->end = PTRDIFF_MIN; - null->limit = PTRDIFF_MIN; /* => max(x, null.limit) = x */ - null->red = false; - - tree->root = null; + tree->root = ITREE_NULL; tree->otick = 1; tree->size = 0; } @@ -461,7 +472,9 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) if (!node->red) broken = subst; interval_tree_transplant (tree, subst, node); - interval_tree_propagate_limit (subst); + interval_tree_propagate_limit + /* FIXME: null->parent is supposed to be write only! */ + (subst == ITREE_NULL ? ITREE_NULL->parent : subst); } else { @@ -472,7 +485,12 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) broken = min->right; eassert (min != ITREE_NULL); if (min->parent == node) - min_right->parent = min; /* set parent, if min_right = null */ + { + if (min_right == ITREE_NULL) + ITREE_NULL->parent = min; /* set parent, if min_right = null */ + else + eassert (min_right->parent == min); + } else { interval_tree_transplant (tree, min->right, min); @@ -484,7 +502,9 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) min->left = node->left; min->left->parent = min; min->red = node->red; - interval_tree_propagate_limit (min_right); + interval_tree_propagate_limit + /* FIXME: null->parent is supposed to be write only! */ + (min_right == ITREE_NULL ? ITREE_NULL->parent : min_right); interval_tree_propagate_limit (min); } @@ -523,6 +543,7 @@ interval_tree_iter_start (struct interval_tree *tree, enum interval_tree_order order, const char* file, int line) { + eassert (null_is_sane ()); /* struct interval_generator *iter = tree->iter; */ if (iter->running) { @@ -625,6 +646,7 @@ interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l if (node->end > pos || (node->end == pos && node->rear_advance)) { node->end += length; + eassert (node != ITREE_NULL); interval_tree_propagate_limit (node); } } @@ -689,6 +711,7 @@ interval_tree_delete_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l if (node->end > pos) { node->end = max (pos , node->end - length); + eassert (node != ITREE_NULL); interval_tree_propagate_limit (node); } } @@ -875,8 +898,6 @@ interval_tree_inherit_offset (uintmax_t otick, struct interval_node *node) static void interval_tree_propagate_limit (struct interval_node *node) { - if (node == ITREE_NULL) - node = node->parent; if (node == ITREE_NULL) return; commit 4f4327c0b0db7f908ae7153e5ddc802b9808dc2e Author: Stefan Monnier Date: Tue Oct 4 22:29:27 2022 -0400 itree.c: Remove some unnecessary `tree` arguments * src/itree.c (interval_tree_propagate_limit) (interval_tree_subtree_min): Remove `tree` argument. (interval_node_set_region, interval_tree_remove) (interval_tree_insert_gap, interval_tree_delete_gap): Adjust callers. diff --git a/src/itree.c b/src/itree.c index 046ad2fa8f..58456fbc6e 100644 --- a/src/itree.c +++ b/src/itree.c @@ -102,13 +102,13 @@ static bool interval_node_intersects (const struct interval_node *, ptrdiff_t, p static int interval_tree_max_height (const struct interval_tree *); static void interval_tree_update_limit (const struct interval_tree *, struct interval_node *); static void interval_tree_inherit_offset (uintmax_t otick, struct interval_node *); -static void interval_tree_propagate_limit (const struct interval_tree *, struct interval_node *); +static void interval_tree_propagate_limit (struct interval_node *); static void interval_tree_rotate_left (struct interval_tree *, struct interval_node *); static void interval_tree_rotate_right (struct interval_tree *, struct interval_node *); static void interval_tree_insert_fix (struct interval_tree *, struct interval_node *); static void interval_tree_remove_fix (struct interval_tree *, struct interval_node *); static void interval_tree_transplant (struct interval_tree *, struct interval_node *, struct interval_node *); -static struct interval_node *interval_tree_subtree_min (const struct interval_tree *, struct interval_node *); +static struct interval_node *interval_tree_subtree_min (struct interval_node *); static struct interval_generator* interval_generator_create (struct interval_tree *); /* The sentinel node, the null node. */ @@ -310,7 +310,7 @@ interval_node_set_region (struct interval_tree *tree, else if (end != node->end) { node->end = max (node->begin, end); - interval_tree_propagate_limit (tree, node); + interval_tree_propagate_limit (node); } } @@ -461,15 +461,16 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) if (!node->red) broken = subst; interval_tree_transplant (tree, subst, node); - interval_tree_propagate_limit (tree, subst); + interval_tree_propagate_limit (subst); } else { - struct interval_node *min = interval_tree_subtree_min (tree, node->right); + struct interval_node *min = interval_tree_subtree_min (node->right); struct interval_node *min_right = min->right; if (!min->red) broken = min->right; + eassert (min != ITREE_NULL); if (min->parent == node) min_right->parent = min; /* set parent, if min_right = null */ else @@ -483,8 +484,8 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) min->left = node->left; min->left->parent = min; min->red = node->red; - interval_tree_propagate_limit (tree, min_right); - interval_tree_propagate_limit (tree, min); + interval_tree_propagate_limit (min_right); + interval_tree_propagate_limit (min); } if (broken) @@ -624,7 +625,7 @@ interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l if (node->end > pos || (node->end == pos && node->rear_advance)) { node->end += length; - interval_tree_propagate_limit (tree, node); + interval_tree_propagate_limit (node); } } interval_stack_destroy (stack); @@ -688,7 +689,7 @@ interval_tree_delete_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l if (node->end > pos) { node->end = max (pos , node->end - length); - interval_tree_propagate_limit (tree, node); + interval_tree_propagate_limit (node); } } interval_stack_destroy (stack); @@ -872,8 +873,7 @@ interval_tree_inherit_offset (uintmax_t otick, struct interval_node *node) */ static void -interval_tree_propagate_limit (const struct interval_tree *tree, - struct interval_node *node) +interval_tree_propagate_limit (struct interval_node *node) { if (node == ITREE_NULL) node = node->parent; @@ -887,7 +887,7 @@ interval_tree_propagate_limit (const struct interval_tree *tree, if (newlimit == node->limit) break; node->limit = newlimit; - if (node == tree->root) + if (node->parent == ITREE_NULL) break; node = node->parent; } @@ -1147,7 +1147,7 @@ interval_tree_transplant (struct interval_tree *tree, struct interval_node *sour static struct interval_node* -interval_tree_subtree_min (const struct interval_tree *tree, struct interval_node *node) +interval_tree_subtree_min (struct interval_node *node) { if (node == ITREE_NULL) return node; commit 2c4a3910b384a1f5b14d282818b04e25785e25b0 Author: Stefan Monnier Date: Sun Oct 2 12:27:37 2022 -0400 itree: Use a single iterator object Instead of having one iterator object per buffer, use just a single global one. There is virtually no benefit to having per-buffer iterators anyway: if two iterations can be active at the same time, then there can be cases where those two iterations happen to operate on the same buffer :-( * src/itree.h (struct interval_tree): Remove `iter` field. * src/itree.c (interval_generator_destroy) (interval_tree_iter_ensure_space): Delete functions. (iter): New global variable. (init_itree_null): Rename to `itree_init` and adjust all callers. Initialize `iter` as well. (interval_tree_create, interval_tree_init): Don't initialize `iter` field any more. (interval_tree_destroy): Don't destroy `iter` field any more. (interval_tree_insert): Don't bother growing the iterator (it's grown in `interval_stack_push_flagged` if needed anyway, and in any case there's no `iter` here to grow any more). (interval_tree_remove): Tweak assertion to be more precise and self-evident. (interval_tree_iter_start): Use the global `iter`. (interval_generator_create): Make it work with a NULL argument. diff --git a/src/itree.c b/src/itree.c index 1ce45a981e..046ad2fa8f 100644 --- a/src/itree.c +++ b/src/itree.c @@ -110,19 +110,10 @@ static void interval_tree_remove_fix (struct interval_tree *, struct interval_no static void interval_tree_transplant (struct interval_tree *, struct interval_node *, struct interval_node *); static struct interval_node *interval_tree_subtree_min (const struct interval_tree *, struct interval_node *); static struct interval_generator* interval_generator_create (struct interval_tree *); -static void interval_generator_destroy (struct interval_generator *); -static inline void interval_tree_iter_ensure_space (struct interval_tree *); /* The sentinel node, the null node. */ struct interval_node itree_null; -static void -init_itree_null (void) -{ - itree_null.parent = itree_null.left = itree_null.right = ITREE_NULL; -} - - /* +------------------------------------------------------------------------------------+ */ typedef uintptr_t nodeptr_and_flag; @@ -148,6 +139,20 @@ struct interval_generator int line; }; +/* Ideally, every iteration would use its own `iter` object, so we could + have several iterations active at the same time. In practice, iterations + are limited by the fact we don't allow modifying the tree at the same + time, making the use of nested iterations quite rare anyway. + So we just use a single global iterator instead for now. */ +static struct interval_generator *iter; + +static void +itree_init (void) +{ + itree_null.parent = itree_null.left = itree_null.right = ITREE_NULL; + iter = interval_generator_create (NULL); +} + /* +===================================================================================+ * | Stack @@ -208,7 +213,8 @@ interval_stack_ensure_space (struct interval_stack *stack, intmax_t nelements) if (nelements > stack->size) { stack->size = (nelements + 1) * 2; - stack->nodes = xrealloc (stack->nodes, stack->size * sizeof (*stack->nodes)); + stack->nodes = xrealloc (stack->nodes, + stack->size * sizeof (*stack->nodes)); } } @@ -220,11 +226,12 @@ interval_stack_push_flagged (struct interval_stack *stack, { eassert (node && node != ITREE_NULL); - /* FIXME: This call to `interval_stack_ensure_space` is a bit redundant - with the work of `interval_generator_ensure_space` but it's still needed - here because `interval_generator_next` can push up to 3 elements per - node it visits, so for a tree of depth N it can use up a stack - space up to 3 times larger than what we computed. :-( */ + /* FIXME: While the stack used in the iterator is bounded by the tree + depth and could be easily pre-allocated to a large enough size to avoid + this "ensure" check, `interval_stack_push` is also used elsewhere to + simply collect some subset of the overlays, where it's only bounded by + the total number of overlays in the buffer (which can be large and thus + preferably not pre-allocated needlessly). */ interval_stack_ensure_space (stack, stack->length + 1); stack->nodes[stack->length] = make_nav (node, flag); @@ -315,11 +322,10 @@ interval_tree_create (void) /* FIXME? Maybe avoid the initialization of itree_null in the same way that is used to call mem_init in alloc.c? It's not really important though. */ - init_itree_null (); + itree_init (); struct interval_tree *tree = xmalloc (sizeof (*tree)); interval_tree_clear (tree); - tree->iter = interval_generator_create (tree); return tree; } @@ -349,7 +355,7 @@ static void interval_tree_init (struct interval_tree *tree) { interval_tree_clear (tree); - tree->iter = interval_generator_create (tree); + /* tree->iter = interval_generator_create (tree); */ } #endif @@ -358,8 +364,8 @@ void interval_tree_destroy (struct interval_tree *tree) { eassert (tree->root == ITREE_NULL); - if (tree->iter) - interval_generator_destroy (tree->iter); + /* if (tree->iter) + * interval_generator_destroy (tree->iter); */ xfree (tree); } @@ -419,7 +425,6 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) /* Fix/update the tree */ ++tree->size; interval_tree_insert_fix (tree, node); - interval_tree_iter_ensure_space (tree); } /* Return true, if NODE is a member of TREE. */ @@ -488,7 +493,7 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) node->right = node->left = node->parent = NULL; --tree->size; - eassert (tree->size == 0 || (tree->size > 0 && tree->root != ITREE_NULL)); + eassert ((tree->size == 0) == (tree->root == ITREE_NULL)); return node; } @@ -517,7 +522,7 @@ interval_tree_iter_start (struct interval_tree *tree, enum interval_tree_order order, const char* file, int line) { - struct interval_generator *iter = tree->iter; + /* struct interval_generator *iter = tree->iter; */ if (iter->running) { fprintf (stderr, @@ -535,6 +540,8 @@ interval_tree_iter_start (struct interval_tree *tree, iter->file = file; iter->line = line; iter->running = true; + /* interval_stack_ensure_space (iter->stack, + 2 * interval_tree_max_height (tree)); */ return iter; } @@ -547,16 +554,6 @@ interval_tree_iter_finish (struct interval_generator *iter) iter->running = false; } -/* Ensure that the tree's iterator does not need to allocate space - until the tree grows in size. */ - -static inline void -interval_tree_iter_ensure_space (struct interval_tree *tree) -{ - interval_stack_ensure_space (tree->iter->stack, - interval_tree_max_height (tree) + 1); -} - static int interval_tree_max_height (const struct interval_tree *tree) { @@ -709,8 +706,11 @@ static struct interval_generator * interval_generator_create (struct interval_tree *tree) { struct interval_generator *g = xmalloc (sizeof *g); - /* FIXME: Is tree ever non-empty here? */ - const int size = interval_tree_max_height (tree) + 1; + /* 19 here just avoids starting with a silly-small stack. + FIXME: Since this stack only needs to be about 2*max_depth + in the worst case, we could completely pre-allocate it to something + like word-bit-size * 2 and then never worry about growing it. */ + const int size = (tree ? interval_tree_max_height (tree) : 19) + 1; g->stack = interval_stack_create (size); g->running = false; @@ -820,17 +820,6 @@ interval_generator_narrow (struct interval_generator *g, g->end = min (end, g->end); } -/* Free the memory allocated for G. */ - -void -interval_generator_destroy (struct interval_generator *g) -{ - if (! g) return; - if (g->stack) - interval_stack_destroy (g->stack); - xfree (g); -} - /* +===================================================================================+ * | Internal Functions diff --git a/src/itree.h b/src/itree.h index 29bc8dd1b2..a04ff6827c 100644 --- a/src/itree.h +++ b/src/itree.h @@ -59,11 +59,6 @@ struct interval_tree struct interval_node *root; uintmax_t otick; /* offset tick, compared with node's otick. */ intmax_t size; /* Number of nodes in the tree. */ - /* FIXME: We can only have one iteration active per tree, which is very - restrictive. Actually, in practice this is no better than limiting - to a single active iteration *globally*, so we could move this `iter` - to a global variable! */ - struct interval_generator *iter; }; enum interval_tree_order { commit b8fbd42f0a7caa4cd9e2d50dd4e4b2101ac78acd Author: Stefan Monnier Date: Sun Oct 2 12:21:13 2022 -0400 mark_overlays: Use the normal ITREE_FOREACH This commit basically reverts commit 5b954f8f9. The problem of nested iterations hasn't been fixed in the mean time, but since the GC can run arbitrary ELisp code (via `post-gc-hook`), running the GC from within an itree iteration is already unsafe anyway :-( * src/alloc.c (mark_overlays): Delete function. (mark_buffer): Use ITREE_FOREACH. diff --git a/src/alloc.c b/src/alloc.c index db8f39a60e..50968b7e12 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -6512,21 +6512,6 @@ mark_overlay (struct Lisp_Overlay *ov) mark_object (ov->plist); } -static void -mark_overlays (struct interval_tree *it, struct interval_node *in) -{ - /* `left/right` are set to NULL when the overlay is deleted, but - they use the `null` node instead when the overlay is not deleted - (i.e. is within an overlay tree). */ - eassert (in); - if (in == ITREE_NULL) - return; - - mark_object (in->data); - mark_overlays (it, in->left); - mark_overlays (it, in->right); -} - /* Mark Lisp_Objects and special pointers in BUFFER. */ static void @@ -6548,8 +6533,9 @@ mark_buffer (struct buffer *buffer) if (!BUFFER_LIVE_P (buffer)) mark_object (BVAR (buffer, undo_list)); - if (buffer->overlays) - mark_overlays (buffer->overlays, buffer->overlays->root); + struct interval_node *node; + ITREE_FOREACH (node, buffer->overlays, PTRDIFF_MIN, PTRDIFF_MAX, ASCENDING) + mark_object (node->data); /* If this is an indirect buffer, mark its base buffer. */ if (buffer->base_buffer && diff --git a/src/itree.h b/src/itree.h index 1f019a2607..29bc8dd1b2 100644 --- a/src/itree.h +++ b/src/itree.h @@ -105,7 +105,8 @@ void interval_tree_delete_gap (struct interval_tree *, ptrdiff_t, ptrdiff_t); - The expression T may be evaluated more than once, so make sure it is cheap a pure. - Only a single iteration can happen at a time, so make sure none of the - code within the loop can start another tree_itertion. + code within the loop can start another tree iteration, i.e. it shouldn't + be able to run ELisp code (or GC for that matter). - If you need to exit the loop early, you *have* to call `ITREE_ABORT` just before exiting (e.g. with `break` or `return`). - Non-local exits are not supported within the body of the loop, commit ba5fe8e7895a2cbfd2d666ca88c0ed96a73fbe29 Author: Stefan Monnier Date: Sun Oct 2 11:11:57 2022 -0400 itree.c: Remove `tree` field from iterator * src/itree.c (interval_generator_ensure_space, interval_generator_reset): Inline and then delete functions. (interval_tree_inherit_offset): Only take the tree's `otick` as arg. Update all callers. (struct interval_generator): Remove `tree` field, replace with a copy of the tree's `otick`. (interval_stack_push_flagged): The arg should be a real node. (interval_tree_insert_gap): Prefer checking root==NULL rather than size==0. Skip loop when tree is empty to avoid pushing&processing the NULL node. (interval_tree_inherit_offset): Prefer parent==NULL rather than node==root to avoid accessing the tree object. diff --git a/src/itree.c b/src/itree.c index eeecaf1839..1ce45a981e 100644 --- a/src/itree.c +++ b/src/itree.c @@ -98,11 +98,10 @@ along with GNU Emacs. If not, see . */ inconsistently/interchangeably. We should fix this naming. */ static struct interval_node *interval_tree_validate (struct interval_tree *, struct interval_node *); -static void interval_generator_ensure_space (struct interval_generator *); static bool interval_node_intersects (const struct interval_node *, ptrdiff_t, ptrdiff_t); static int interval_tree_max_height (const struct interval_tree *); static void interval_tree_update_limit (const struct interval_tree *, struct interval_node *); -static void interval_tree_inherit_offset (const struct interval_tree *, struct interval_node *); +static void interval_tree_inherit_offset (uintmax_t otick, struct interval_node *); static void interval_tree_propagate_limit (const struct interval_tree *, struct interval_node *); static void interval_tree_rotate_left (struct interval_tree *, struct interval_node *); static void interval_tree_rotate_right (struct interval_tree *, struct interval_node *); @@ -112,9 +111,6 @@ static void interval_tree_transplant (struct interval_tree *, struct interval_no static struct interval_node *interval_tree_subtree_min (const struct interval_tree *, struct interval_node *); static struct interval_generator* interval_generator_create (struct interval_tree *); static void interval_generator_destroy (struct interval_generator *); -static void interval_generator_reset (struct interval_generator *, - ptrdiff_t, ptrdiff_t, - enum interval_tree_order); static inline void interval_tree_iter_ensure_space (struct interval_tree *); /* The sentinel node, the null node. */ @@ -142,10 +138,10 @@ struct interval_stack /* State used when iterating interval. */ struct interval_generator { - struct interval_tree *tree; struct interval_stack *stack; ptrdiff_t begin; ptrdiff_t end; + uintmax_t otick; /* A copy of the tree's `otick`. */ enum interval_tree_order order; bool_bf running : 1; const char* file; @@ -222,6 +218,8 @@ static inline void interval_stack_push_flagged (struct interval_stack *stack, struct interval_node *node, bool flag) { + eassert (node && node != ITREE_NULL); + /* FIXME: This call to `interval_stack_ensure_space` is a bit redundant with the work of `interval_generator_ensure_space` but it's still needed here because `interval_generator_next` can push up to 3 elements per @@ -450,7 +448,7 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) struct interval_node *broken = NULL; - interval_tree_inherit_offset (tree, node); + interval_tree_inherit_offset (tree->otick, node); if (node->left == ITREE_NULL || node->right == ITREE_NULL) { struct interval_node *subst @@ -475,7 +473,7 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) min->right = node->right; min->right->parent = min; } - interval_tree_inherit_offset (tree, min); + interval_tree_inherit_offset (tree->otick, min); interval_tree_transplant (tree, min, node); min->left = node->left; min->left->parent = min; @@ -504,7 +502,7 @@ interval_tree_validate (struct interval_tree *tree, struct interval_node *node) if (node != tree->root) interval_tree_validate (tree, node->parent); - interval_tree_inherit_offset (tree, node); + interval_tree_inherit_offset (tree->otick, node); return node; } @@ -527,10 +525,16 @@ interval_tree_iter_start (struct interval_tree *tree, iter->file, iter->line, file, line); emacs_abort (); } - interval_generator_reset (iter, begin, end, order); - iter->running = true; + iter->begin = begin; + iter->end = end; + iter->otick = tree->otick; + iter->order = order; + interval_stack_clear (iter->stack); + if (begin <= end && tree->root != ITREE_NULL) + interval_stack_push_flagged (iter->stack, tree->root, false); iter->file = file; iter->line = line; + iter->running = true; return iter; } @@ -549,7 +553,8 @@ interval_tree_iter_finish (struct interval_generator *iter) static inline void interval_tree_iter_ensure_space (struct interval_tree *tree) { - interval_generator_ensure_space (tree->iter); + interval_stack_ensure_space (tree->iter->stack, + interval_tree_max_height (tree) + 1); } static int @@ -570,7 +575,7 @@ interval_tree_max_height (const struct interval_tree *tree) void interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t length) { - if (length <= 0 || tree->size == 0) + if (length <= 0 || tree->root == ITREE_NULL) return; /* FIXME: Don't allocate generator/stack anew every time. */ @@ -588,45 +593,48 @@ interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l for (int i = 0; i < saved->length; ++i) interval_tree_remove (tree, nav_nodeptr (saved->nodes[i])); - /* We can't use a generator here, because we can't effectively narrow AND shift some subtree at the same time. */ - const int size = interval_tree_max_height (tree) + 1; - struct interval_stack *stack = interval_stack_create (size); - interval_stack_push (stack, tree->root); - nodeptr_and_flag nav; - while ((nav = interval_stack_pop (stack), - node = nav_nodeptr (nav))) + if (tree->root != ITREE_NULL) { - /* Process in pre-order. */ - interval_tree_inherit_offset (tree, node); - if (node->right != ITREE_NULL) + const int size = interval_tree_max_height (tree) + 1; + struct interval_stack *stack = interval_stack_create (size); + interval_stack_push (stack, tree->root); + nodeptr_and_flag nav; + while ((nav = interval_stack_pop (stack), + node = nav_nodeptr (nav))) { - if (node->begin > pos) + /* Process in pre-order. */ + interval_tree_inherit_offset (tree->otick, node); + if (node->right != ITREE_NULL) { - /* All nodes in this subtree are shifted by length. */ - node->right->offset += length; - ++tree->otick; + if (node->begin > pos) + { + /* All nodes in this subtree are shifted by length. */ + node->right->offset += length; + ++tree->otick; + } + else + interval_stack_push (stack, node->right); } - else - interval_stack_push (stack, node->right); - } - if (node->left != ITREE_NULL - && pos <= node->left->limit + node->left->offset) - interval_stack_push (stack, node->left); + if (node->left != ITREE_NULL + && pos <= node->left->limit + node->left->offset) + interval_stack_push (stack, node->left); - /* node->begin == pos implies no front-advance. */ - if (node->begin > pos) - node->begin += length; - if (node->end > pos || (node->end == pos && node->rear_advance)) - { - node->end += length; - interval_tree_propagate_limit (tree, node); + /* node->begin == pos implies no front-advance. */ + if (node->begin > pos) + node->begin += length; + if (node->end > pos || (node->end == pos && node->rear_advance)) + { + node->end += length; + interval_tree_propagate_limit (tree, node); + } } + interval_stack_destroy (stack); } - interval_stack_destroy (stack); /* Reinsert nodes starting at POS having front-advance. */ + nodeptr_and_flag nav; while ((nav = interval_stack_pop (saved), node = nav_nodeptr (nav))) { @@ -645,7 +653,7 @@ interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l void interval_tree_delete_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t length) { - if (length <= 0 || tree->size == 0) + if (length <= 0 || tree->root == ITREE_NULL) return; /* FIXME: Don't allocate stack anew every time. */ @@ -662,7 +670,7 @@ interval_tree_delete_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l while ((nav = interval_stack_pop (stack))) { node = nav_nodeptr (nav); - interval_tree_inherit_offset (tree, node); + interval_tree_inherit_offset (tree->otick, node); if (node->right != ITREE_NULL) { if (node->begin > pos + length) @@ -705,38 +713,14 @@ interval_generator_create (struct interval_tree *tree) const int size = interval_tree_max_height (tree) + 1; g->stack = interval_stack_create (size); - g->tree = tree; g->running = false; - interval_generator_reset (g, 1, 0, ITREE_ASCENDING); + g->begin = 0; + g->end = 0; + g->file = NULL; + g->line = 0; return g; } -/* Reset generator G such that it iterates over intervals intersecting - with [BEGIN, END) in the given ORDER. */ - -void -interval_generator_reset (struct interval_generator *g, - ptrdiff_t begin, ptrdiff_t end, - enum interval_tree_order order) -{ - if (! g) return; - - g->begin = begin; - g->end = end; - g->order = order; - interval_stack_clear (g->stack); - if (begin <= end && g->tree->size > 0) - interval_stack_push_flagged (g->stack, g->tree->root, false); -} - -/* Allocate enough space for the tree of G in its current shape. */ - -static inline void -interval_generator_ensure_space (struct interval_generator *g) -{ - interval_stack_ensure_space (g->stack, interval_tree_max_height (g->tree) + 1); -} - /* Return true, if NODE's interval intersects with [BEGIN, END). Note: We always include empty nodes at BEGIN (and not at END), but if BEGIN==END, then we don't include non-empty nodes starting @@ -785,7 +769,7 @@ interval_generator_next (struct interval_generator *g) struct interval_node * const left = node->left; struct interval_node * const right = node->right; - interval_tree_inherit_offset (g->tree, node); + interval_tree_inherit_offset (g->otick, node); switch (g->order) { case ITREE_ASCENDING: @@ -872,11 +856,9 @@ interval_tree_update_limit (const struct interval_tree *tree, */ static void -interval_tree_inherit_offset (const struct interval_tree *tree, - struct interval_node *node) +interval_tree_inherit_offset (uintmax_t otick, struct interval_node *node) { - - if (node->otick == tree->otick) + if (node->otick == otick) return; node->begin += node->offset; @@ -889,8 +871,8 @@ interval_tree_inherit_offset (const struct interval_tree *tree, node->offset = 0; /* FIXME: I wonder when/why this condition can be false, and more generally why we'd want to propagate offsets that may not be fully up-to-date. */ - if (node == tree->root || node->parent->otick == tree->otick) - node->otick = tree->otick; + if (node->parent == ITREE_NULL || node->parent->otick == otick) + node->otick = otick; } /* Update limit of NODE and its ancestors. Stop when it becomes @@ -910,8 +892,9 @@ interval_tree_propagate_limit (const struct interval_tree *tree, return; while (1) { - ptrdiff_t newlimit = max (node->end, max (node->left->limit + node->left->offset, - node->right->limit + node->right->offset)); + ptrdiff_t newlimit = max (node->end, + max (node->left->limit + node->left->offset, + node->right->limit + node->right->offset)); if (newlimit == node->limit) break; node->limit = newlimit; @@ -930,8 +913,8 @@ interval_tree_rotate_left (struct interval_tree *tree, struct interval_node *nod struct interval_node *right = node->right; - interval_tree_inherit_offset (tree, node); - interval_tree_inherit_offset (tree, right); + interval_tree_inherit_offset (tree->otick, node); + interval_tree_inherit_offset (tree->otick, right); /* Turn right's left subtree into node's right subtree. */ node->right = right->left; @@ -972,8 +955,8 @@ interval_tree_rotate_right (struct interval_tree *tree, struct interval_node *no struct interval_node *left = node->left; - interval_tree_inherit_offset (tree, node); - interval_tree_inherit_offset (tree, left); + interval_tree_inherit_offset (tree->otick, node); + interval_tree_inherit_offset (tree->otick, left); node->left = left->right; if (left->right != ITREE_NULL) commit c3eb6c0563cc95b2134af9fe0ee6f304ddbb0480 Merge: 1303f55161 c55254dccc Author: Stefan Monnier Date: Sun Oct 2 01:31:59 2022 -0400 Merge remote-tracking branch 'origin/feature/noverlay' into noverlay commit 1303f55161ae40cc98ccddc37755b58b68840798 Author: Stefan Monnier Date: Sun Oct 2 01:30:44 2022 -0400 New ITREE_FOREACH macro * src/itree.h (interval_tree_iter_start): Adjust type. (interval_tree_nodes): Delete declaration. (ITREE_FOREACH, ITREE_FOREACH_ABORT, ITREE_FOREACH_NARROW): New macros. * src/itree.c (interval_tree_contains, interval_tree_insert_gap): Use the new ITREE_FOREACH macro. (interval_tree_nodes): Delete function. (interval_tree_iter_start): Return the iterator. (interval_generator_next, interval_tree_destroy): Don't accept a NULL arg any more. * src/xdisp.c (load_overlay_strings, strings_with_newlines): * src/textprop.c (get_char_property_and_overlay): * src/buffer.c (copy_overlays, delete_all_overlays) (set_overlays_multibyte, swap_buffer_overlays, overlays_in) (next_overlay_change, previous_overlay_change, overlay_touches_p) (overlay_strings, Foverlay_lists, report_overlay_modification) (evaporate_overlays): Use the new ITREE_FOREACH macro. * src/buffer.h (buffer_overlay_iter_start1) (buffer_overlay_iter_start, buffer_overlay_iter_next) (buffer_overlay_iter_finish, buffer_overlay_iter_narrow): Delete declarations. diff --git a/src/buffer.c b/src/buffer.c index 19937216ed..8d02d705b5 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -647,8 +647,7 @@ copy_overlays (struct buffer *from, struct buffer *to) eassert (to && ! to->overlays); struct interval_node *node; - buffer_overlay_iter_start (from, PTRDIFF_MIN, PTRDIFF_MAX, ITREE_ASCENDING); - while ((node = buffer_overlay_iter_next (from))) + ITREE_FOREACH (node, from->overlays, PTRDIFF_MIN, PTRDIFF_MAX, ASCENDING) { Lisp_Object ov = node->data; Lisp_Object copy = build_overlay (node->begin, node->end, @@ -657,7 +656,6 @@ copy_overlays (struct buffer *from, struct buffer *to) Fcopy_sequence (OVERLAY_PLIST (ov))); add_buffer_overlay (to, XOVERLAY (copy)); } - buffer_overlay_iter_finish (from); } bool @@ -929,14 +927,12 @@ delete_all_overlays (struct buffer *b) Of course, we can't set them to NULL from within the iteration because the iterator may need them (tho we could if we added an ITREE_POST_ORDER iteration order). */ - buffer_overlay_iter_start (b, PTRDIFF_MIN, PTRDIFF_MAX, ITREE_ASCENDING); - while ((node = buffer_overlay_iter_next (b))) + ITREE_FOREACH (node, b->overlays, PTRDIFF_MIN, PTRDIFF_MAX, ASCENDING) { modify_overlay (b, node->begin, node->end); /* Where are the nodes freed ? --ap */ XOVERLAY (node->data)->buffer = NULL; } - buffer_overlay_iter_finish (b); interval_tree_clear (b->overlays); } @@ -944,7 +940,6 @@ static void free_buffer_overlays (struct buffer *b) { /* Actually this does not free any overlay, but the tree only. --ap */ - eassert (! b->overlays || 0 == interval_tree_size (b->overlays)); if (b->overlays) { interval_tree_destroy (b->overlays); @@ -969,9 +964,16 @@ set_overlays_multibyte (bool multibyte) struct interval_tree *tree = current_buffer->overlays; const intmax_t size = interval_tree_size (tree); + /* We can't use `interval_node_set_region` at the same time + as we iterate over the itree, so we need an auxiliary storage + to keep the list of nodes. */ USE_SAFE_ALLOCA; SAFE_NALLOCA (nodes, 1, size); - interval_tree_nodes (tree, nodes, ITREE_ASCENDING); + { + struct interval_node *node, **cursor = nodes; + ITREE_FOREACH (node, tree, PTRDIFF_MIN, PTRDIFF_MAX, ASCENDING) + *(cursor++) = node; + } for (int i = 0; i < size; ++i, ++nodes) { @@ -2418,15 +2420,11 @@ swap_buffer_overlays (struct buffer *buffer, struct buffer *other) { struct interval_node *node; - buffer_overlay_iter_start (buffer, PTRDIFF_MIN, PTRDIFF_MAX, ITREE_ASCENDING); - while ((node = buffer_overlay_iter_next (buffer))) + ITREE_FOREACH (node, buffer->overlays, PTRDIFF_MIN, PTRDIFF_MAX, ASCENDING) XOVERLAY (node->data)->buffer = other; - buffer_overlay_iter_finish (buffer); - buffer_overlay_iter_start (other, PTRDIFF_MIN, PTRDIFF_MAX, ITREE_ASCENDING); - while ((node = buffer_overlay_iter_next (other))) + ITREE_FOREACH (node, other->overlays, PTRDIFF_MIN, PTRDIFF_MAX, ASCENDING) XOVERLAY (node->data)->buffer = buffer; - buffer_overlay_iter_finish (other); /* Swap the interval trees. */ void *tmp = buffer->overlays; @@ -2951,23 +2949,24 @@ overlays_in (ptrdiff_t beg, ptrdiff_t end, bool extend, Lisp_Object *vec = *vec_ptr; struct interval_node *node; - buffer_overlay_iter_start (current_buffer, beg, - /* Find empty OV at Z ? */ - (end >= ZV && empty) ? ZV + 1 : ZV, - ITREE_ASCENDING); - - while ((node = buffer_overlay_iter_next (current_buffer))) + ITREE_FOREACH (node, current_buffer->overlays, beg, + /* Find empty OV at Z ? */ + (end >= ZV && empty) ? ZV + 1 : ZV, ASCENDING) { if (node->begin > end) { next = min (next, node->begin); + ITREE_FOREACH_ABORT (); break; } else if (node->begin == end) { next = node->begin; if ((! empty || end < ZV) && beg < end) - break; + { + ITREE_FOREACH_ABORT (); + break; + } } if (! empty && node->begin == node->end) @@ -2985,7 +2984,6 @@ overlays_in (ptrdiff_t beg, ptrdiff_t end, bool extend, /* Keep counting overlays even if we can't return them all. */ idx++; } - buffer_overlay_iter_finish (current_buffer); if (next_ptr) *next_ptr = next ? next : ZV; @@ -3012,8 +3010,7 @@ next_overlay_change (ptrdiff_t pos) ptrdiff_t next = ZV; struct interval_node *node; - buffer_overlay_iter_start (current_buffer, pos, next, ITREE_ASCENDING); - while ((node = buffer_overlay_iter_next (current_buffer))) + ITREE_FOREACH (node, current_buffer->overlays, pos, next, ASCENDING) { if (node->begin > pos) { @@ -3021,15 +3018,15 @@ next_overlay_change (ptrdiff_t pos) of pos, because the search is limited to [pos,next) . */ eassert (node->begin < next); next = node->begin; + ITREE_FOREACH_ABORT (); break; } else if (node->begin < node->end && node->end < next) { next = node->end; - buffer_overlay_iter_narrow (current_buffer, pos, next); + ITREE_FOREACH_NARROW (pos, next); } } - buffer_overlay_iter_finish (current_buffer); return next; } @@ -3040,16 +3037,14 @@ previous_overlay_change (ptrdiff_t pos) struct interval_node *node; ptrdiff_t prev = BEGV; - buffer_overlay_iter_start (current_buffer, prev, pos, ITREE_DESCENDING); - while ((node = buffer_overlay_iter_next (current_buffer))) + ITREE_FOREACH (node, current_buffer->overlays, prev, pos, DESCENDING) { if (node->end < pos) prev = node->end; else prev = max (prev, node->begin); - buffer_overlay_iter_narrow (current_buffer, prev, pos); + ITREE_FOREACH_NARROW (prev, pos); } - buffer_overlay_iter_finish (current_buffer); return prev; } @@ -3123,19 +3118,16 @@ bool overlay_touches_p (ptrdiff_t pos) { struct interval_node *node; - bool result = false; /* We need to find overlays ending in pos, as well as empty ones at pos. */ - buffer_overlay_iter_start (current_buffer, - pos - 1, pos + 1, ITREE_DESCENDING); - - while (! result && (node = buffer_overlay_iter_next (current_buffer))) - result = (node->begin == pos || node->end == pos); - - buffer_overlay_iter_finish (current_buffer); - - return result; + ITREE_FOREACH (node, current_buffer->overlays, pos - 1, pos + 1, DESCENDING) + if (node->begin == pos || node->end == pos) + { + ITREE_FOREACH_ABORT (); + return true; + } + return false; } @@ -3342,9 +3334,7 @@ overlay_strings (ptrdiff_t pos, struct window *w, unsigned char **pstr) overlay_heads.used = overlay_heads.bytes = 0; overlay_tails.used = overlay_tails.bytes = 0; - buffer_overlay_iter_start (current_buffer, - pos - 1, pos + 1, ITREE_DESCENDING); - while ((node = buffer_overlay_iter_next (current_buffer))) + ITREE_FOREACH (node, current_buffer->overlays, pos - 1, pos + 1, DESCENDING) { Lisp_Object overlay = node->data; eassert (OVERLAYP (overlay)); @@ -3358,6 +3348,8 @@ overlay_strings (ptrdiff_t pos, struct window *w, unsigned char **pstr) if (WINDOWP (window) && XWINDOW (window) != w) continue; Lisp_Object str; + /* FIXME: Are we really sure that `record_overlay_string` can + never cause a non-local exit? */ if (startpos == pos && (str = Foverlay_get (overlay, Qbefore_string), STRINGP (str))) record_overlay_string (&overlay_heads, str, @@ -3372,7 +3364,6 @@ overlay_strings (ptrdiff_t pos, struct window *w, unsigned char **pstr) Foverlay_get (overlay, Qpriority), endpos - startpos); } - buffer_overlay_iter_finish (current_buffer); if (overlay_tails.used > 1) qsort (overlay_tails.buf, overlay_tails.used, sizeof (struct sortstr), @@ -3842,10 +3833,8 @@ However, the overlays you get are the real objects that the buffer uses. */) Lisp_Object overlays = Qnil; struct interval_node *node; - buffer_overlay_iter_start (current_buffer, BEG, Z, ITREE_DESCENDING); - while ((node = buffer_overlay_iter_next (current_buffer))) + ITREE_FOREACH (node, current_buffer->overlays, BEG, Z, DESCENDING) overlays = Fcons (node->data, overlays); - buffer_overlay_iter_finish (current_buffer); return Fcons (overlays, Qnil); } @@ -3983,11 +3972,10 @@ report_overlay_modification (Lisp_Object start, Lisp_Object end, bool after, if (! current_buffer->overlays) return; - buffer_overlay_iter_start (current_buffer, - begin_arg - (insertion ? 1 : 0), - end_arg + (insertion ? 1 : 0), - ITREE_ASCENDING); - while ((node = buffer_overlay_iter_next (current_buffer))) + ITREE_FOREACH (node, current_buffer->overlays, + begin_arg - (insertion ? 1 : 0), + end_arg + (insertion ? 1 : 0), + ASCENDING) { Lisp_Object overlay = node->data; ptrdiff_t obegin = OVERLAY_START (overlay); @@ -4016,7 +4004,6 @@ report_overlay_modification (Lisp_Object start, Lisp_Object end, bool after, add_overlay_mod_hooklist (prop, overlay); } } - buffer_overlay_iter_finish (current_buffer); } { /* Call the functions recorded in last_overlay_modification_hooks. @@ -4070,14 +4057,12 @@ evaporate_overlays (ptrdiff_t pos) Lisp_Object hit_list = Qnil; struct interval_node *node; - buffer_overlay_iter_start (current_buffer, pos, pos, ITREE_ASCENDING); - while ((node = buffer_overlay_iter_next (current_buffer))) + ITREE_FOREACH (node, current_buffer->overlays, pos, pos, ASCENDING) { if (node->end == pos && ! NILP (Foverlay_get (node->data, Qevaporate))) hit_list = Fcons (node->data, hit_list); } - buffer_overlay_iter_finish (current_buffer); for (; CONSP (hit_list); hit_list = XCDR (hit_list)) Fdelete_overlay (XCAR (hit_list)); diff --git a/src/buffer.h b/src/buffer.h index ad3b2ad6df..a4e3934cad 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -1442,39 +1442,6 @@ remove_buffer_overlay (struct buffer *b, struct Lisp_Overlay *ov) ov->buffer = NULL; } -INLINE void -buffer_overlay_iter_start1 (struct buffer *b, ptrdiff_t begin, ptrdiff_t end, - enum interval_tree_order order, const char* file, int line) -{ - if (b->overlays) - interval_tree_iter_start (b->overlays, begin, end, order, file, line); -} - -#define buffer_overlay_iter_start(b, begin, end, order) \ - buffer_overlay_iter_start1 ((b), (begin), (end), (order), __FILE__, __LINE__) - -INLINE struct interval_node* -buffer_overlay_iter_next (struct buffer *b) -{ - if (! b->overlays) - return NULL; - return interval_generator_next (b->overlays->iter); -} - -INLINE void -buffer_overlay_iter_finish (struct buffer *b) -{ - if (b->overlays) - interval_tree_iter_finish (b->overlays->iter); -} - -INLINE void -buffer_overlay_iter_narrow (struct buffer *b, ptrdiff_t begin, ptrdiff_t end) -{ - if (b->overlays) - interval_generator_narrow (b->overlays->iter, begin, end); -} - /* Return the start of OV in its buffer, or -1 if OV is not associated with any buffer. */ diff --git a/src/itree.c b/src/itree.c index 4f8aea924a..eeecaf1839 100644 --- a/src/itree.c +++ b/src/itree.c @@ -325,7 +325,7 @@ interval_tree_create (void) return tree; } -/* Reset the tree TREE to its empty state. */ +/* Reset the tree TREE to its empty state. */ void interval_tree_clear (struct interval_tree *tree) @@ -345,7 +345,7 @@ interval_tree_clear (struct interval_tree *tree) } #ifdef ITREE_TESTING -/* Initialize a pre-allocated tree (presumably on the stack). */ +/* Initialize a pre-allocated tree (presumably on the stack). */ static void interval_tree_init (struct interval_tree *tree) @@ -355,12 +355,11 @@ interval_tree_init (struct interval_tree *tree) } #endif -/* Release a tree, freeing its allocated memory. */ +/* Release a tree, freeing its allocated memory. */ void interval_tree_destroy (struct interval_tree *tree) { - if (! tree) - return; + eassert (tree->root == ITREE_NULL); if (tree->iter) interval_generator_destroy (tree->iter); xfree (tree); @@ -389,14 +388,14 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) ptrdiff_t offset = 0; /* Find the insertion point, accumulate node's offset and update - ancestors limit values. */ + ancestors limit values. */ while (child != ITREE_NULL) { parent = child; offset += child->offset; child->limit = max (child->limit, node->end - offset); /* This suggests that nodes in the right subtree are strictly - greater. But this is not true due to later rotations. */ + greater. But this is not true due to later rotations. */ child = node->begin <= child->begin ? child->left : child->right; } @@ -430,15 +429,16 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) bool interval_tree_contains (struct interval_tree *tree, struct interval_node *node) { + eassert (node); struct interval_node *other; - - interval_tree_iter_start (tree, node->begin, PTRDIFF_MAX, ITREE_ASCENDING, __FILE__, __LINE__); - while ((other = interval_generator_next (tree->iter))) + ITREE_FOREACH (other, tree, node->begin, PTRDIFF_MAX, ASCENDING) if (other == node) - break; + { + ITREE_FOREACH_ABORT (); + return true; + } - interval_tree_iter_finish (tree->iter); - return other == node; + return false; } /* Remove NODE from TREE and return it. NODE must exist in TREE. */ @@ -508,34 +508,12 @@ interval_tree_validate (struct interval_tree *tree, struct interval_node *node) return node; } -/* Fill memory pointed at via NODES with all nodes of TREE in the - given ORDER. - - The size of NODES must be sufficiently large. - */ - -void -interval_tree_nodes (struct interval_tree *tree, - struct interval_node **nodes, - enum interval_tree_order order) -{ - struct interval_node *node; - - interval_tree_iter_start (tree, PTRDIFF_MIN, PTRDIFF_MAX, order, __FILE__, __LINE__); - while ((node = interval_generator_next (tree->iter))) - { - *nodes = node; - ++nodes; - } - interval_tree_iter_finish (tree->iter); -} - /* Start a generator iterating all intervals in [BEGIN,END) in the given ORDER. Only one iterator per tree can be running at any time. */ -void +struct interval_generator * interval_tree_iter_start (struct interval_tree *tree, ptrdiff_t begin, ptrdiff_t end, enum interval_tree_order order, @@ -553,6 +531,7 @@ interval_tree_iter_start (struct interval_tree *tree, iter->running = true; iter->file = file; iter->line = line; + return iter; } /* Stop using the iterator. */ @@ -600,15 +579,12 @@ interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l order, so we need to remove them first. */ struct interval_stack *saved = interval_stack_create (0); struct interval_node *node = NULL; - interval_tree_iter_start (tree, pos, pos + 1, - ITREE_PRE_ORDER, __FILE__, __LINE__); - while ((node = interval_generator_next (tree->iter))) + ITREE_FOREACH (node, tree, pos, pos + 1, PRE_ORDER) { if (node->begin == pos && node->front_advance && (node->begin != node->end || node->rear_advance)) interval_stack_push (saved, node); } - interval_tree_iter_finish (tree->iter); for (int i = 0; i < saved->length; ++i) interval_tree_remove (tree, nav_nodeptr (saved->nodes[i])); @@ -784,7 +760,6 @@ inline struct interval_node* interval_generator_next (struct interval_generator *g) { eassert (g->running); - if (! g) return NULL; struct interval_node * const null = ITREE_NULL; struct interval_node *node; diff --git a/src/itree.h b/src/itree.h index b9294c5662..1f019a2607 100644 --- a/src/itree.h +++ b/src/itree.h @@ -59,6 +59,10 @@ struct interval_tree struct interval_node *root; uintmax_t otick; /* offset tick, compared with node's otick. */ intmax_t size; /* Number of nodes in the tree. */ + /* FIXME: We can only have one iteration active per tree, which is very + restrictive. Actually, in practice this is no better than limiting + to a single active iteration *globally*, so we could move this `iter` + to a global variable! */ struct interval_generator *iter; }; @@ -79,12 +83,56 @@ void interval_tree_clear (struct interval_tree *); void interval_tree_insert (struct interval_tree *, struct interval_node *); bool interval_tree_contains (struct interval_tree *, struct interval_node *); struct interval_node *interval_tree_remove (struct interval_tree *, struct interval_node *); -void interval_tree_iter_start (struct interval_tree *, ptrdiff_t, ptrdiff_t, enum interval_tree_order, +struct interval_generator *interval_tree_iter_start (struct interval_tree *, ptrdiff_t, ptrdiff_t, enum interval_tree_order, const char* file, int line); void interval_generator_narrow (struct interval_generator *, ptrdiff_t, ptrdiff_t); void interval_tree_iter_finish (struct interval_generator *); struct interval_node *interval_generator_next (struct interval_generator *); void interval_tree_insert_gap (struct interval_tree *, ptrdiff_t, ptrdiff_t); void interval_tree_delete_gap (struct interval_tree *, ptrdiff_t, ptrdiff_t); -void interval_tree_nodes (struct interval_tree *tree, struct interval_node **nodes, enum interval_tree_order order); + +/* Iterate over the intervals between BEG and END in the tree T. + N will hold successive nodes. ORDER can be one of : `ASCENDING`, + `DESCENDING`, or `PRE_ORDER`. + It should be used as: + + ITREE_FOREACH (n, t, beg, end, order) + { + .. do the thing with n .. + } + + BEWARE: + - The expression T may be evaluated more than once, so make sure + it is cheap a pure. + - Only a single iteration can happen at a time, so make sure none of the + code within the loop can start another tree_itertion. + - If you need to exit the loop early, you *have* to call `ITREE_ABORT` + just before exiting (e.g. with `break` or `return`). + - Non-local exits are not supported within the body of the loop, + unless the caller makes sure `ITREE_ABORT` is called via some + kind of unwind_protect. + - Don't modify the tree during the iteration. + */ +#define ITREE_FOREACH(n, t, beg, end, order) \ + /* FIXME: We'd want to declare `x` right here, but I can't figure out + how to make that work here: the `for` syntax only allows a single + clause for the var declarations where we need 2 different types. + We could use the `struct {foo x; bar y; } p;` trick to declare two + vars `p.x` and `p.y` of unrelated types, but then none of the names + of the vars matches the `n` we receive :-(. */ \ + if (!t) \ + { } \ + else \ + for (struct interval_generator *itree_iter_ \ + = interval_tree_iter_start (t, beg, end, ITREE_##order, \ + __FILE__, __LINE__); \ + ((n = interval_generator_next (itree_iter_)) \ + || (interval_tree_iter_finish (itree_iter_), false));) + +#define ITREE_FOREACH_ABORT() \ + interval_tree_iter_finish (itree_iter_) + +#define ITREE_FOREACH_NARROW(beg, end) \ + interval_generator_narrow (itree_iter_, beg, end) + #endif diff --git a/src/textprop.c b/src/textprop.c index c2c3622d05..04fae52809 100644 --- a/src/textprop.c +++ b/src/textprop.c @@ -643,10 +643,8 @@ get_char_property_and_overlay (Lisp_Object position, register Lisp_Object prop, && pos <= BUF_ZV (b))) xsignal1 (Qargs_out_of_range, position); - buffer_overlay_iter_start (b, pos, pos + 1, ITREE_ASCENDING); - /* Now check the overlays in order of decreasing priority. */ - while ((node = buffer_overlay_iter_next (b))) + ITREE_FOREACH (node, b->overlays, pos, pos + 1, ASCENDING) { Lisp_Object tem = Foverlay_get (node->data, prop); struct sortvec *this; @@ -662,7 +660,6 @@ get_char_property_and_overlay (Lisp_Object position, register Lisp_Object prop, result_tem = tem; } } - buffer_overlay_iter_finish (b); if (result) { if (overlay) diff --git a/src/xdisp.c b/src/xdisp.c index 6ecd3bdf24..d585d57fd0 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -6568,10 +6568,8 @@ load_overlay_strings (struct it *it, ptrdiff_t charpos) while (false) - buffer_overlay_iter_start (current_buffer, - charpos - 1, charpos + 1, ITREE_DESCENDING); /* Process overlays. */ - while ((node = buffer_overlay_iter_next (current_buffer))) + ITREE_FOREACH (node, current_buffer->overlays, charpos - 1, charpos + 1, DESCENDING) { Lisp_Object overlay = node->data; eassert (OVERLAYP (overlay)); @@ -6607,7 +6605,6 @@ load_overlay_strings (struct it *it, ptrdiff_t charpos) && SCHARS (str)) RECORD_OVERLAY_STRING (overlay, str, true); } - buffer_overlay_iter_finish (current_buffer); #undef RECORD_OVERLAY_STRING @@ -7005,10 +7002,8 @@ static bool strings_with_newlines (ptrdiff_t startpos, ptrdiff_t endpos, struct window *w) { struct interval_node *node; - /* Process overlays before the overlay center. */ - buffer_overlay_iter_start (current_buffer, - startpos, endpos, ITREE_DESCENDING); - while ((node = buffer_overlay_iter_next (current_buffer))) + /* Process overlays. */ + ITREE_FOREACH (node, current_buffer->overlays, startpos, endpos, DESCENDING) { Lisp_Object overlay = node->data; eassert (OVERLAYP (overlay)); @@ -7032,20 +7027,18 @@ strings_with_newlines (ptrdiff_t startpos, ptrdiff_t endpos, struct window *w) if (STRINGP (str) && SCHARS (str) && memchr (SDATA (str), '\n', SBYTES (str))) { - buffer_overlay_iter_finish (current_buffer); + ITREE_FOREACH_ABORT (); return true; } str = Foverlay_get (overlay, Qafter_string); if (STRINGP (str) && SCHARS (str) && memchr (SDATA (str), '\n', SBYTES (str))) { - buffer_overlay_iter_finish (current_buffer); + ITREE_FOREACH_ABORT (); return true; } } - buffer_overlay_iter_finish (current_buffer); - /* Check for 'display' properties whose values include strings. */ Lisp_Object cpos = make_fixnum (startpos); Lisp_Object limpos = make_fixnum (endpos); commit c55254dcccf53df3e619737bb6670f0a573f27a1 Author: Gerd Möllmann Date: Sat Oct 1 08:28:14 2022 +0200 Make it compile with ITREE_DEBUG defined * src/buffer.c (make_lispy_interval_node): Use make_fixnum. (overlay_tree): Use ITREE_NULL. diff --git a/src/buffer.c b/src/buffer.c index 19937216ed..1e01750e16 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -4913,15 +4913,15 @@ defvar_per_buffer (struct Lisp_Buffer_Objfwd *bo_fwd, const char *namestring, static Lisp_Object make_lispy_interval_node (const struct interval_node *node) { - return listn (CONSTYPE_HEAP, 12, + return listn (12, intern (":begin"), - make_number (node->begin), + make_fixnum (node->begin), intern (":end"), - make_number (node->end), + make_fixnum (node->end), intern (":limit"), - make_number (node->limit), + make_fixnum (node->limit), intern (":offset"), - make_number (node->offset), + make_fixnum (node->offset), intern (":rear-advance"), node->rear_advance ? Qt : Qnil, intern (":front-advance"), @@ -4932,7 +4932,7 @@ static Lisp_Object overlay_tree (const struct interval_tree *tree, const struct interval_node *node) { - if (node == &tree->nil) + if (node == ITREE_NULL) return Qnil; return list3 (make_lispy_interval_node (node), overlay_tree (tree, node->left), commit ab2926aad3e15c6cfa0e4b31ae9274c47a58baf2 Author: Stefan Monnier Date: Fri Sep 30 20:37:15 2022 -0400 itree.c: Improve division between tree and iterator * src/buffer.c (delete_all_overlays): Add comment. * src/itree.c (struct interval_generator): New fields `running`, `file`, and `line` moved from `interval_tree`. (interval_stack_push_flagged): Adjust comment to resolve a FIXME. (interval_tree_clear): Replace assignment with an a (interval_tree_iter_next): Delete function. (interval_tree_clear): Don't set `iter_running` here any more. (interval_generator_create): Set it here instead. (interval_tree_iter_start): Fetch `iter` once and for all. (interval_generator_narrow): Mark it as non-static. (interval_tree_iter_next, interval_tree_iter_narrow): Delete functions. Inline their old bodies in the callers. (interval_tree_iter_finish): Take the iter rather than the whole tree. Adjust all callers. (interval_generator_next): Move `running `assertion here from `interval_tree_iter_next`. * src/buffer.h: Adjust accordingly. * src/itree.h (struct interval_tree): Remove fields `iter_running`, `file`, and `line`, moved to `interval_generator`. (interval_generator_narrow): Replace `interval_tree_iter_narrow`. diff --git a/src/buffer.c b/src/buffer.c index 2f026584bb..19937216ed 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -921,6 +921,14 @@ delete_all_overlays (struct buffer *b) if (! b->overlays) return; + /* FIXME: This loop sets the overlays' `buffer` field to NULL but + doesn't set the interval_nodes' `parent`, `left` and `right` + fields accordingly. I believe it's harmless, but a bit untidy since + other parts of the code are careful to set those fields to NULL when + the overlay is deleted. + Of course, we can't set them to NULL from within the iteration + because the iterator may need them (tho we could if we added + an ITREE_POST_ORDER iteration order). */ buffer_overlay_iter_start (b, PTRDIFF_MIN, PTRDIFF_MAX, ITREE_ASCENDING); while ((node = buffer_overlay_iter_next (b))) { diff --git a/src/buffer.h b/src/buffer.h index 447be06594..ad3b2ad6df 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -1458,21 +1458,21 @@ buffer_overlay_iter_next (struct buffer *b) { if (! b->overlays) return NULL; - return interval_tree_iter_next (b->overlays); + return interval_generator_next (b->overlays->iter); } INLINE void buffer_overlay_iter_finish (struct buffer *b) { if (b->overlays) - interval_tree_iter_finish (b->overlays); + interval_tree_iter_finish (b->overlays->iter); } INLINE void buffer_overlay_iter_narrow (struct buffer *b, ptrdiff_t begin, ptrdiff_t end) { if (b->overlays) - interval_tree_iter_narrow (b->overlays, begin, end); + interval_generator_narrow (b->overlays->iter, begin, end); } /* Return the start of OV in its buffer, or -1 if OV is not associated diff --git a/src/itree.c b/src/itree.c index 6d97dd2a71..4f8aea924a 100644 --- a/src/itree.c +++ b/src/itree.c @@ -94,6 +94,9 @@ along with GNU Emacs. If not, see . */ incremented whenever some node's offset has changed. */ +/* FIXME: The code seems to use "generator" and "iterator" + inconsistently/interchangeably. We should fix this naming. */ + static struct interval_node *interval_tree_validate (struct interval_tree *, struct interval_node *); static void interval_generator_ensure_space (struct interval_generator *); static bool interval_node_intersects (const struct interval_node *, ptrdiff_t, ptrdiff_t); @@ -110,13 +113,8 @@ static struct interval_node *interval_tree_subtree_min (const struct interval_tr static struct interval_generator* interval_generator_create (struct interval_tree *); static void interval_generator_destroy (struct interval_generator *); static void interval_generator_reset (struct interval_generator *, - ptrdiff_t, ptrdiff_t, - enum interval_tree_order); -static void -interval_generator_narrow (struct interval_generator *g, - ptrdiff_t begin, ptrdiff_t end); -static inline struct interval_node* -interval_generator_next (struct interval_generator *g); + ptrdiff_t, ptrdiff_t, + enum interval_tree_order); static inline void interval_tree_iter_ensure_space (struct interval_tree *); /* The sentinel node, the null node. */ @@ -149,6 +147,9 @@ struct interval_generator ptrdiff_t begin; ptrdiff_t end; enum interval_tree_order order; + bool_bf running : 1; + const char* file; + int line; }; @@ -221,8 +222,11 @@ static inline void interval_stack_push_flagged (struct interval_stack *stack, struct interval_node *node, bool flag) { - /* FIXME: Isn't this redundant with the calls that are passed - `interval_tree_max_height` before the iteration? */ + /* FIXME: This call to `interval_stack_ensure_space` is a bit redundant + with the work of `interval_generator_ensure_space` but it's still needed + here because `interval_generator_next` can push up to 3 elements per + node it visits, so for a tree of depth N it can use up a stack + space up to 3 times larger than what we computed. :-( */ interval_stack_ensure_space (stack, stack->length + 1); stack->nodes[stack->length] = make_nav (node, flag); @@ -338,7 +342,6 @@ interval_tree_clear (struct interval_tree *tree) tree->root = null; tree->otick = 1; tree->size = 0; - tree->iter_running = false; } #ifdef ITREE_TESTING @@ -430,11 +433,11 @@ interval_tree_contains (struct interval_tree *tree, struct interval_node *node) struct interval_node *other; interval_tree_iter_start (tree, node->begin, PTRDIFF_MAX, ITREE_ASCENDING, __FILE__, __LINE__); - while ((other = interval_tree_iter_next (tree))) + while ((other = interval_generator_next (tree->iter))) if (other == node) break; - interval_tree_iter_finish (tree); + interval_tree_iter_finish (tree->iter); return other == node; } @@ -519,12 +522,12 @@ interval_tree_nodes (struct interval_tree *tree, struct interval_node *node; interval_tree_iter_start (tree, PTRDIFF_MIN, PTRDIFF_MAX, order, __FILE__, __LINE__); - while ((node = interval_tree_iter_next (tree))) + while ((node = interval_generator_next (tree->iter))) { *nodes = node; ++nodes; } - interval_tree_iter_finish (tree); + interval_tree_iter_finish (tree->iter); } /* Start a generator iterating all intervals in [BEGIN,END) in the @@ -538,47 +541,27 @@ interval_tree_iter_start (struct interval_tree *tree, enum interval_tree_order order, const char* file, int line) { - if (tree->iter_running) + struct interval_generator *iter = tree->iter; + if (iter->running) { fprintf (stderr, "Detected nested iteration!\nOuter: %s:%d\nInner: %s:%d\n", - tree->file, tree->line, file, line); + iter->file, iter->line, file, line); emacs_abort (); } - interval_generator_reset (tree->iter, begin, end, order); - tree->iter_running = true; - tree->file = file; - tree->line = line; -} - -/* Limit the search interval of the iterator to the given values. The - interval can only shrink, but never grow.*/ - -inline void -interval_tree_iter_narrow (struct interval_tree *tree, - ptrdiff_t begin, ptrdiff_t end) -{ - eassert (tree->iter_running); - interval_generator_narrow (tree->iter, begin, end); + interval_generator_reset (iter, begin, end, order); + iter->running = true; + iter->file = file; + iter->line = line; } /* Stop using the iterator. */ void -interval_tree_iter_finish (struct interval_tree *tree) -{ - eassert (tree->iter_running); - tree->iter_running = false; -} - -/* Return the next node of the iterator in the order given when it was - started; or NULL if there are no more nodes. */ - -inline struct interval_node* -interval_tree_iter_next (struct interval_tree *tree) +interval_tree_iter_finish (struct interval_generator *iter) { - eassert (tree->iter_running); - return interval_generator_next (tree->iter); + eassert (iter->running); + iter->running = false; } /* Ensure that the tree's iterator does not need to allocate space @@ -617,14 +600,15 @@ interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l order, so we need to remove them first. */ struct interval_stack *saved = interval_stack_create (0); struct interval_node *node = NULL; - interval_tree_iter_start (tree, pos, pos + 1, ITREE_PRE_ORDER, __FILE__, __LINE__); - while ((node = interval_tree_iter_next (tree))) + interval_tree_iter_start (tree, pos, pos + 1, + ITREE_PRE_ORDER, __FILE__, __LINE__); + while ((node = interval_generator_next (tree->iter))) { if (node->begin == pos && node->front_advance && (node->begin != node->end || node->rear_advance)) interval_stack_push (saved, node); } - interval_tree_iter_finish (tree); + interval_tree_iter_finish (tree->iter); for (int i = 0; i < saved->length; ++i) interval_tree_remove (tree, nav_nodeptr (saved->nodes[i])); @@ -737,14 +721,16 @@ interval_tree_delete_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l /* Allocate a new generator for TREE. */ -static struct interval_generator* -interval_generator_create (struct interval_tree *tree) +static struct interval_generator * +interval_generator_create (struct interval_tree *tree) { struct interval_generator *g = xmalloc (sizeof *g); + /* FIXME: Is tree ever non-empty here? */ const int size = interval_tree_max_height (tree) + 1; g->stack = interval_stack_create (size); g->tree = tree; + g->running = false; interval_generator_reset (g, 1, 0, ITREE_ASCENDING); return g; } @@ -791,11 +777,13 @@ interval_node_intersects (const struct interval_node *node, || (node->begin == node->end && begin == node->begin); } -/* Return the next node of G, or NULL if there is none. */ +/* Return the next node of the iterator in the order given when it was + started; or NULL if there are no more nodes. */ inline struct interval_node* interval_generator_next (struct interval_generator *g) { + eassert (g->running); if (! g) return NULL; struct interval_node * const null = ITREE_NULL; @@ -862,10 +850,11 @@ interval_generator_next (struct interval_generator *g) /* Limit G to the new interval [BEGIN, END), which must be a subset of the current one. I.E. it can't grow on either side. */ -static inline void +inline void interval_generator_narrow (struct interval_generator *g, ptrdiff_t begin, ptrdiff_t end) { + eassert (g->running); eassert (begin >= g->begin); eassert (end <= g->end); g->begin = max (begin, g->begin); @@ -923,6 +912,8 @@ interval_tree_inherit_offset (const struct interval_tree *tree, if (node->right != ITREE_NULL) node->right->offset += node->offset; node->offset = 0; + /* FIXME: I wonder when/why this condition can be false, and more generally + why we'd want to propagate offsets that may not be fully up-to-date. */ if (node == tree->root || node->parent->otick == tree->otick) node->otick = tree->otick; } diff --git a/src/itree.h b/src/itree.h index f1ef7f9946..b9294c5662 100644 --- a/src/itree.h +++ b/src/itree.h @@ -60,9 +60,6 @@ struct interval_tree uintmax_t otick; /* offset tick, compared with node's otick. */ intmax_t size; /* Number of nodes in the tree. */ struct interval_generator *iter; - bool_bf iter_running : 1; - const char* file; - int line; }; enum interval_tree_order { @@ -84,9 +81,9 @@ bool interval_tree_contains (struct interval_tree *, struct interval_node *); struct interval_node *interval_tree_remove (struct interval_tree *, struct interval_node *); void interval_tree_iter_start (struct interval_tree *, ptrdiff_t, ptrdiff_t, enum interval_tree_order, const char* file, int line); -void interval_tree_iter_narrow (struct interval_tree *, ptrdiff_t, ptrdiff_t); -void interval_tree_iter_finish (struct interval_tree *); -struct interval_node *interval_tree_iter_next (struct interval_tree *); +void interval_generator_narrow (struct interval_generator *, ptrdiff_t, ptrdiff_t); +void interval_tree_iter_finish (struct interval_generator *); +struct interval_node *interval_generator_next (struct interval_generator *); void interval_tree_insert_gap (struct interval_tree *, ptrdiff_t, ptrdiff_t); void interval_tree_delete_gap (struct interval_tree *, ptrdiff_t, ptrdiff_t); void interval_tree_nodes (struct interval_tree *tree, struct interval_node **nodes, enum interval_tree_order order); commit 009249e0c6d3bb6c4a3714a279ae91807d133c77 Author: Gerd Möllmann Date: Fri Sep 30 13:25:15 2022 +0200 Remove the per-tree null node "make check" shows 0 unexpcted. * src/itree.h (itree_null): Declare extern. (ITREE_NULL): New macro (struct interval_tree): Remove null member. * src/alloc.c (mark_overlays): Use ITREE_NULL. * src/itree.c: Use ITREE_NULL insteads of a tree's null. * src/pdumper.c (dump_buffer): Use ITREE_NULL. diff --git a/src/alloc.c b/src/alloc.c index 8dc45659b5..db8f39a60e 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -6519,7 +6519,7 @@ mark_overlays (struct interval_tree *it, struct interval_node *in) they use the `null` node instead when the overlay is not deleted (i.e. is within an overlay tree). */ eassert (in); - if (in == &it->null) + if (in == ITREE_NULL) return; mark_object (in->data); diff --git a/src/itree.c b/src/itree.c index 3b354b5640..6d97dd2a71 100644 --- a/src/itree.c +++ b/src/itree.c @@ -119,6 +119,14 @@ static inline struct interval_node* interval_generator_next (struct interval_generator *g); static inline void interval_tree_iter_ensure_space (struct interval_tree *); +/* The sentinel node, the null node. */ +struct interval_node itree_null; + +static void +init_itree_null (void) +{ + itree_null.parent = itree_null.left = itree_null.right = ITREE_NULL; +} /* +------------------------------------------------------------------------------------+ */ @@ -302,6 +310,11 @@ interval_node_set_region (struct interval_tree *tree, struct interval_tree* interval_tree_create (void) { + /* FIXME? Maybe avoid the initialization of itree_null in the same + way that is used to call mem_init in alloc.c? It's not really + important though. */ + init_itree_null (); + struct interval_tree *tree = xmalloc (sizeof (*tree)); interval_tree_clear (tree); tree->iter = interval_generator_create (tree); @@ -313,13 +326,15 @@ interval_tree_create (void) void interval_tree_clear (struct interval_tree *tree) { - struct interval_node *null = &tree->null; + /* FIXME: Why is this done? */ + struct interval_node *null = ITREE_NULL; null->left = null->right = null->parent = null; null->offset = null->otick = 0; null->begin = PTRDIFF_MIN; null->end = PTRDIFF_MIN; null->limit = PTRDIFF_MIN; /* => max(x, null.limit) = x */ null->red = false; + tree->root = null; tree->otick = 1; tree->size = 0; @@ -364,15 +379,15 @@ interval_tree_size (struct interval_tree *tree) void interval_tree_insert (struct interval_tree *tree, struct interval_node *node) { - eassert (node && node->begin <= node->end && node != &tree->null); + eassert (node && node->begin <= node->end && node != ITREE_NULL); - struct interval_node *parent = &tree->null; + struct interval_node *parent = ITREE_NULL; struct interval_node *child = tree->root; ptrdiff_t offset = 0; /* Find the insertion point, accumulate node's offset and update ancestors limit values. */ - while (child != &tree->null) + while (child != ITREE_NULL) { parent = child; offset += child->offset; @@ -383,7 +398,7 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) } /* Insert the node */ - if (parent == &tree->null) + if (parent == ITREE_NULL) tree->root = node; else if (node->begin <= parent->begin) parent->left = node; @@ -392,8 +407,8 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) /* Init the node */ node->parent = parent; - node->left = &tree->null; - node->right = &tree->null; + node->left = ITREE_NULL; + node->right = ITREE_NULL; node->red = true; node->offset = 0; node->begin -= offset; @@ -433,10 +448,10 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) struct interval_node *broken = NULL; interval_tree_inherit_offset (tree, node); - if (node->left == &tree->null || node->right == &tree->null) + if (node->left == ITREE_NULL || node->right == ITREE_NULL) { - struct interval_node *subst = - (node->right == &tree->null) ? node->left : node->right; + struct interval_node *subst + = node->right == ITREE_NULL ? node->left : node->right; if (!node->red) broken = subst; interval_tree_transplant (tree, subst, node); @@ -472,7 +487,7 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) node->right = node->left = node->parent = NULL; --tree->size; - eassert (tree->size == 0 || (tree->size > 0 && tree->root != &tree->null)); + eassert (tree->size == 0 || (tree->size > 0 && tree->root != ITREE_NULL)); return node; } @@ -481,7 +496,7 @@ static struct interval_node* interval_tree_validate (struct interval_tree *tree, struct interval_node *node) { - if (tree->otick == node->otick || node == &tree->null) + if (tree->otick == node->otick || node == ITREE_NULL) return node; if (node != tree->root) interval_tree_validate (tree, node->parent); @@ -625,7 +640,7 @@ interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l { /* Process in pre-order. */ interval_tree_inherit_offset (tree, node); - if (node->right != &tree->null) + if (node->right != ITREE_NULL) { if (node->begin > pos) { @@ -636,7 +651,7 @@ interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l else interval_stack_push (stack, node->right); } - if (node->left != &tree->null + if (node->left != ITREE_NULL && pos <= node->left->limit + node->left->offset) interval_stack_push (stack, node->left); @@ -688,7 +703,7 @@ interval_tree_delete_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l { node = nav_nodeptr (nav); interval_tree_inherit_offset (tree, node); - if (node->right != &tree->null) + if (node->right != ITREE_NULL) { if (node->begin > pos + length) { @@ -699,7 +714,7 @@ interval_tree_delete_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l else interval_stack_push (stack, node->right); } - if (node->left != &tree->null + if (node->left != ITREE_NULL && pos <= node->left->limit + node->left->offset) interval_stack_push (stack, node->left); @@ -783,7 +798,7 @@ interval_generator_next (struct interval_generator *g) { if (! g) return NULL; - struct interval_node * const null = &g->tree->null; + struct interval_node * const null = ITREE_NULL; struct interval_node *node; /* The `visited` flag stored in each node is used here (and only here): @@ -879,7 +894,7 @@ static void interval_tree_update_limit (const struct interval_tree *tree, struct interval_node *node) { - if (node == &tree->null) + if (node == ITREE_NULL) return; node->limit = max (node->end, max (node->left->limit + node->left->offset, @@ -903,9 +918,9 @@ interval_tree_inherit_offset (const struct interval_tree *tree, node->begin += node->offset; node->end += node->offset; node->limit += node->offset; - if (node->left != &tree->null) + if (node->left != ITREE_NULL) node->left->offset += node->offset; - if (node->right != &tree->null) + if (node->right != ITREE_NULL) node->right->offset += node->offset; node->offset = 0; if (node == tree->root || node->parent->otick == tree->otick) @@ -923,9 +938,9 @@ static void interval_tree_propagate_limit (const struct interval_tree *tree, struct interval_node *node) { - if (node == &tree->null) + if (node == ITREE_NULL) node = node->parent; - if (node == &tree->null) + if (node == ITREE_NULL) return; while (1) { @@ -945,7 +960,7 @@ interval_tree_propagate_limit (const struct interval_tree *tree, static void interval_tree_rotate_left (struct interval_tree *tree, struct interval_node *node) { - eassert (node->right != &tree->null); + eassert (node->right != ITREE_NULL); struct interval_node *right = node->right; @@ -954,11 +969,11 @@ interval_tree_rotate_left (struct interval_tree *tree, struct interval_node *nod /* Turn right's left subtree into node's right subtree. */ node->right = right->left; - if (right->left != &tree->null) + if (right->left != ITREE_NULL) right->left->parent = node; /* right's parent was node's parent. */ - if (right != &tree->null) + if (right != ITREE_NULL) right->parent = node->parent; /* Get the parent to point to right instead of node. */ @@ -974,7 +989,7 @@ interval_tree_rotate_left (struct interval_tree *tree, struct interval_node *nod /* Put node on right's left. */ right->left = node; - if (node != &tree->null) + if (node != ITREE_NULL) node->parent = right; /* Order matters here. */ @@ -987,7 +1002,7 @@ interval_tree_rotate_left (struct interval_tree *tree, struct interval_node *nod static void interval_tree_rotate_right (struct interval_tree *tree, struct interval_node *node) { - eassert (tree && node && node->left != &tree->null); + eassert (tree && node && node->left != ITREE_NULL); struct interval_node *left = node->left; @@ -995,10 +1010,10 @@ interval_tree_rotate_right (struct interval_tree *tree, struct interval_node *no interval_tree_inherit_offset (tree, left); node->left = left->right; - if (left->right != &tree->null) + if (left->right != ITREE_NULL) left->right->parent = node; - if (left != &tree->null) + if (left != ITREE_NULL) left->parent = node->parent; if (node != tree->root) { @@ -1011,7 +1026,7 @@ interval_tree_rotate_right (struct interval_tree *tree, struct interval_node *no tree->root = left; left->right = node; - if (node != &tree->null) + if (node != ITREE_NULL) node->parent = left; interval_tree_update_limit (tree, left); @@ -1180,7 +1195,7 @@ static void interval_tree_transplant (struct interval_tree *tree, struct interval_node *source, struct interval_node *dest) { - eassert (tree && source && dest && dest != &tree->null); + eassert (tree && source && dest && dest != ITREE_NULL); if (dest == tree->root) tree->root = source; @@ -1196,9 +1211,9 @@ interval_tree_transplant (struct interval_tree *tree, struct interval_node *sour static struct interval_node* interval_tree_subtree_min (const struct interval_tree *tree, struct interval_node *node) { - if (node == &tree->null) + if (node == ITREE_NULL) return node; - while (node->left != &tree->null) + while (node->left != ITREE_NULL) node = node->left; return node; } diff --git a/src/itree.h b/src/itree.h index f24b12fcf6..f1ef7f9946 100644 --- a/src/itree.h +++ b/src/itree.h @@ -50,10 +50,13 @@ struct interval_node bool_bf front_advance : 1; /* Same as for marker and overlays. */ }; +/* The sentinel node, the null node. */ +extern struct interval_node itree_null; +#define ITREE_NULL (&itree_null) + struct interval_tree { struct interval_node *root; - struct interval_node null; /* The tree's version of NULL. */ uintmax_t otick; /* offset tick, compared with node's otick. */ intmax_t size; /* Number of nodes in the tree. */ struct interval_generator *iter; diff --git a/src/pdumper.c b/src/pdumper.c index 4c057117b4..e39f5f1109 100644 --- a/src/pdumper.c +++ b/src/pdumper.c @@ -2863,8 +2863,7 @@ dump_buffer (struct dump_context *ctx, const struct buffer *in_buffer) DUMP_FIELD_COPY (out, buffer, inhibit_buffer_hooks); DUMP_FIELD_COPY (out, buffer, long_line_optimizations_p); - if (buffer->overlays - && (buffer->overlays->root != &buffer->overlays->null)) + if (buffer->overlays && buffer->overlays->root != ITREE_NULL) /* We haven't implemented the code to dump overlays. */ emacs_abort (); else commit a7ad0f806c1ed82f4d0710111aa92417e04a1110 Author: Stefan Monnier Date: Thu Sep 29 17:12:21 2022 -0400 itree: Remove the `visited` flag from the tree nodes These bits really belong in the "workstack" used within `interval_generator_next`, so move them there. * src/itree.c (nodeptr_and_flag): New type; (struct interval_stack): Use it. (make_nav, nav_nodeptr, nav_flag): New functions. (interval_tree_insert_gap, interval_tree_delete_gap): Adjust accordingly. (interval_generator_next): Stash the `visited` bit in the work stack rather than inside the tree nodes. (interval_stack_create, interval_stack_destroy, interval_stack_clear) (interval_stack_ensure_space, interval_stack_push_flagged) (interval_stack_push, interval_stack_pop): Move before first use. * src/itree.h (struct interval_node): Remove `visited` field. * src/pdumper.c (dump_interval_node): Adjust accordingly. diff --git a/src/itree.c b/src/itree.c index 7c2602683c..3b354b5640 100644 --- a/src/itree.c +++ b/src/itree.c @@ -98,13 +98,6 @@ static struct interval_node *interval_tree_validate (struct interval_tree *, str static void interval_generator_ensure_space (struct interval_generator *); static bool interval_node_intersects (const struct interval_node *, ptrdiff_t, ptrdiff_t); static int interval_tree_max_height (const struct interval_tree *); -static struct interval_stack *interval_stack_create (intmax_t); -static void interval_stack_destroy (struct interval_stack *); -static void interval_stack_clear (struct interval_stack *); -static void interval_stack_ensure_space (struct interval_stack *, intmax_t); -static void interval_stack_push (struct interval_stack *, struct interval_node *); -static void interval_stack_push_flagged (struct interval_stack *, struct interval_node *, bool); -static struct interval_node *interval_stack_pop (struct interval_stack *); static void interval_tree_update_limit (const struct interval_tree *, struct interval_node *); static void interval_tree_inherit_offset (const struct interval_tree *, struct interval_node *); static void interval_tree_propagate_limit (const struct interval_tree *, struct interval_node *); @@ -130,10 +123,12 @@ static inline void interval_tree_iter_ensure_space (struct interval_tree *); /* +------------------------------------------------------------------------------------+ */ +typedef uintptr_t nodeptr_and_flag; + /* Simple dynamic array. */ struct interval_stack { - struct interval_node **nodes; + nodeptr_and_flag *nodes; size_t size; size_t length; }; @@ -148,6 +143,97 @@ struct interval_generator enum interval_tree_order order; }; + +/* +===================================================================================+ + * | Stack + * +===================================================================================+ */ + +static inline nodeptr_and_flag +make_nav (struct interval_node *ptr, bool flag) +{ + uintptr_t v = (uintptr_t) ptr; + /* We assume alignment imposes the LSB is clear for us to use it. */ + eassert (!(v & 1)); + return v | !!flag; +} + +static inline struct interval_node * +nav_nodeptr (nodeptr_and_flag nav) +{ + return (struct interval_node *) (nav & (~(uintptr_t)1)); +} + +static inline bool +nav_flag (nodeptr_and_flag nav) +{ + return (bool) (nav & 1); +} + +/* This is just a simple dynamic array with stack semantics. */ + +static struct interval_stack* +interval_stack_create (intmax_t initial_size) +{ + struct interval_stack *stack = xmalloc (sizeof (struct interval_stack)); + stack->size = max (0, initial_size); + stack->nodes = xmalloc (stack->size * sizeof (struct interval_node*)); + stack->length = 0; + return stack; +} + +static void +interval_stack_destroy (struct interval_stack *stack) +{ + if (! stack) + return; + if (stack->nodes) + xfree (stack->nodes); + xfree (stack); +} + +static void +interval_stack_clear (struct interval_stack *stack) +{ + stack->length = 0; +} + +static inline void +interval_stack_ensure_space (struct interval_stack *stack, intmax_t nelements) +{ + if (nelements > stack->size) + { + stack->size = (nelements + 1) * 2; + stack->nodes = xrealloc (stack->nodes, stack->size * sizeof (*stack->nodes)); + } +} + +/* Push NODE on the STACK, while settings its visited flag to FLAG. */ + +static inline void +interval_stack_push_flagged (struct interval_stack *stack, + struct interval_node *node, bool flag) +{ + /* FIXME: Isn't this redundant with the calls that are passed + `interval_tree_max_height` before the iteration? */ + interval_stack_ensure_space (stack, stack->length + 1); + + stack->nodes[stack->length] = make_nav (node, flag); + stack->length++; +} + +static inline void +interval_stack_push (struct interval_stack *stack, struct interval_node *node) +{ + interval_stack_push_flagged (stack, node, false); +} + +static inline nodeptr_and_flag +interval_stack_pop (struct interval_stack *stack) +{ + if (stack->length == 0) + return make_nav (NULL, false); + return stack->nodes[--stack->length]; +} /* +===================================================================================+ @@ -525,7 +611,7 @@ interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l } interval_tree_iter_finish (tree); for (int i = 0; i < saved->length; ++i) - interval_tree_remove (tree, saved->nodes[i]); + interval_tree_remove (tree, nav_nodeptr (saved->nodes[i])); /* We can't use a generator here, because we can't effectively @@ -533,7 +619,9 @@ interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l const int size = interval_tree_max_height (tree) + 1; struct interval_stack *stack = interval_stack_create (size); interval_stack_push (stack, tree->root); - while ((node = interval_stack_pop (stack))) + nodeptr_and_flag nav; + while ((nav = interval_stack_pop (stack), + node = nav_nodeptr (nav))) { /* Process in pre-order. */ interval_tree_inherit_offset (tree, node); @@ -564,7 +652,8 @@ interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l interval_stack_destroy (stack); /* Reinsert nodes starting at POS having front-advance. */ - while ((node = interval_stack_pop (saved))) + while ((nav = interval_stack_pop (saved), + node = nav_nodeptr (nav))) { node->begin += length; if (node->end != pos || node->rear_advance) @@ -594,8 +683,10 @@ interval_tree_delete_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l struct interval_node *node; interval_stack_push (stack, tree->root); - while ((node = interval_stack_pop (stack))) + nodeptr_and_flag nav; + while ((nav = interval_stack_pop (stack))) { + node = nav_nodeptr (nav); interval_tree_inherit_offset (tree, node); if (node->right != &tree->null) { @@ -705,19 +796,13 @@ interval_generator_next (struct interval_generator *g) needs to be considered but we haven't yet decided whether it's included in the generator's output. */ - /* FIXME: We should move the `visited` flag to the stack: each entry - there should simply consist of a node and a bool (the `visited` status) - so this internal implementation detail doesn't leak into the - `interval_node` structure. - [ In theory it would also allow multiple iterations to be active - at the same time, tho that does not seem particularly useful at - this time and would require further changes anyway. ] - To save space on the stack, we could hijack the LSB bit of the `node*` - word to hold the `visited` bit. */ - do { - while ((node = interval_stack_pop (g->stack)) - && ! node->visited) + nodeptr_and_flag nav; + bool visited; + while ((nav = interval_stack_pop (g->stack), + node = nav_nodeptr (nav), + visited = nav_flag (nav), + node && !visited)) { struct interval_node * const left = node->left; struct interval_node * const right = node->right; @@ -783,75 +868,6 @@ interval_generator_destroy (struct interval_generator *g) xfree (g); } - -/* +===================================================================================+ - * | Stack - * +===================================================================================+ */ - -/* This is just a simple dynamic array with stack semantics. */ - -static struct interval_stack* -interval_stack_create (intmax_t initial_size) -{ - struct interval_stack *stack = xmalloc (sizeof (struct interval_stack)); - stack->size = max (0, initial_size); - stack->nodes = xmalloc (stack->size * sizeof (struct interval_node*)); - stack->length = 0; - return stack; -} - -static void -interval_stack_destroy (struct interval_stack *stack) -{ - if (! stack) - return; - if (stack->nodes) - xfree (stack->nodes); - xfree (stack); -} - -static void -interval_stack_clear (struct interval_stack *stack) -{ - stack->length = 0; -} - -static inline void -interval_stack_ensure_space (struct interval_stack *stack, intmax_t nelements) -{ - if (nelements > stack->size) - { - stack->size = (nelements + 1) * 2; - stack->nodes = xrealloc (stack->nodes, stack->size * sizeof (*stack->nodes)); - } -} - -static inline void -interval_stack_push (struct interval_stack *stack, struct interval_node *node) -{ - interval_stack_ensure_space (stack, stack->length + 1); - stack->nodes[stack->length] = node; - stack->length++; -} - -/* Push NODE on the STACK, while settings its visited flag to FLAG. */ - -static inline void -interval_stack_push_flagged (struct interval_stack *stack, - struct interval_node *node, bool flag) -{ - interval_stack_push (stack, node); - node->visited = flag; -} - -static inline struct interval_node* -interval_stack_pop (struct interval_stack *stack) -{ - if (stack->length == 0) - return NULL; - return stack->nodes[--stack->length]; -} - /* +===================================================================================+ * | Internal Functions diff --git a/src/itree.h b/src/itree.h index 8f0454dc7a..f24b12fcf6 100644 --- a/src/itree.h +++ b/src/itree.h @@ -46,7 +46,6 @@ struct interval_node uintmax_t otick; /* offset modified tick */ Lisp_Object data; /* Exclusively used by the client. */ bool_bf red : 1; - bool_bf visited : 1; /* Internal to `interval_generator_next`. */ bool_bf rear_advance : 1; /* Same as for marker and overlays. */ bool_bf front_advance : 1; /* Same as for marker and overlays. */ }; diff --git a/src/pdumper.c b/src/pdumper.c index 44486015f0..4c057117b4 100644 --- a/src/pdumper.c +++ b/src/pdumper.c @@ -2155,7 +2155,6 @@ dump_interval_node (struct dump_context *ctx, struct interval_node *node, DUMP_FIELD_COPY (&out, node, otick); dump_field_lv (ctx, &out, node, &node->data, WEIGHT_STRONG); DUMP_FIELD_COPY (&out, node, red); - DUMP_FIELD_COPY (&out, node, visited); DUMP_FIELD_COPY (&out, node, rear_advance); DUMP_FIELD_COPY (&out, node, front_advance); dump_off offset = dump_object_finish (ctx, &out, sizeof (out)); commit 757c116f6b0bc2d8e81aef18f8eada27ca8745a1 Author: Stefan Monnier Date: Thu Sep 29 16:15:01 2022 -0400 free_buffer_overlays: Move nearer to its sole caller * src/buffer.c (free_buffer_overlays): Move from `buffer.h`. * src/buffer.h (free_buffer_overlays): Move to `buffer.c`. * src/itree.c (interval_tree_iter_narrow, interval_tree_iter_finish) (interval_tree_iter_next): Prefer `eassert`. diff --git a/src/buffer.c b/src/buffer.c index 879e14be96..2f026584bb 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -932,6 +932,18 @@ delete_all_overlays (struct buffer *b) interval_tree_clear (b->overlays); } +static void +free_buffer_overlays (struct buffer *b) +{ + /* Actually this does not free any overlay, but the tree only. --ap */ + eassert (! b->overlays || 0 == interval_tree_size (b->overlays)); + if (b->overlays) + { + interval_tree_destroy (b->overlays); + b->overlays = NULL; + } +} + /* Adjust the position of overlays in the current buffer according to MULTIBYTE. diff --git a/src/buffer.h b/src/buffer.h index 097283be67..447be06594 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -1424,20 +1424,6 @@ maybe_alloc_buffer_overlays (struct buffer *b) b->overlays = interval_tree_create (); } -/* FIXME: Actually this does not free any overlay, but the tree - only. --ap */ - -INLINE void -free_buffer_overlays (struct buffer *b) -{ - eassert (! b->overlays || 0 == interval_tree_size (b->overlays)); - if (b->overlays) - { - interval_tree_destroy (b->overlays); - b->overlays = NULL; - } -} - INLINE void add_buffer_overlay (struct buffer *b, struct Lisp_Overlay *ov) { diff --git a/src/itree.c b/src/itree.c index bdaa3fe09b..7c2602683c 100644 --- a/src/itree.c +++ b/src/itree.c @@ -94,26 +94,26 @@ along with GNU Emacs. If not, see . */ incremented whenever some node's offset has changed. */ -static struct interval_node *interval_tree_validate(struct interval_tree *, struct interval_node *); -static void interval_generator_ensure_space(struct interval_generator *); -static bool interval_node_intersects(const struct interval_node *, ptrdiff_t, ptrdiff_t); -static int interval_tree_max_height(const struct interval_tree *); -static struct interval_stack *interval_stack_create(intmax_t); -static void interval_stack_destroy(struct interval_stack *); -static void interval_stack_clear(struct interval_stack *); -static void interval_stack_ensure_space(struct interval_stack *, intmax_t); -static void interval_stack_push(struct interval_stack *, struct interval_node *); -static void interval_stack_push_flagged(struct interval_stack *, struct interval_node *, bool); -static struct interval_node *interval_stack_pop(struct interval_stack *); -static void interval_tree_update_limit(const struct interval_tree *, struct interval_node *); -static void interval_tree_inherit_offset(const struct interval_tree *, struct interval_node *); -static void interval_tree_propagate_limit(const struct interval_tree *, struct interval_node *); -static void interval_tree_rotate_left(struct interval_tree *, struct interval_node *); -static void interval_tree_rotate_right(struct interval_tree *, struct interval_node *); -static void interval_tree_insert_fix(struct interval_tree *, struct interval_node *); -static void interval_tree_remove_fix(struct interval_tree *, struct interval_node *); -static void interval_tree_transplant(struct interval_tree *, struct interval_node *, struct interval_node *); -static struct interval_node *interval_tree_subtree_min(const struct interval_tree *, struct interval_node *); +static struct interval_node *interval_tree_validate (struct interval_tree *, struct interval_node *); +static void interval_generator_ensure_space (struct interval_generator *); +static bool interval_node_intersects (const struct interval_node *, ptrdiff_t, ptrdiff_t); +static int interval_tree_max_height (const struct interval_tree *); +static struct interval_stack *interval_stack_create (intmax_t); +static void interval_stack_destroy (struct interval_stack *); +static void interval_stack_clear (struct interval_stack *); +static void interval_stack_ensure_space (struct interval_stack *, intmax_t); +static void interval_stack_push (struct interval_stack *, struct interval_node *); +static void interval_stack_push_flagged (struct interval_stack *, struct interval_node *, bool); +static struct interval_node *interval_stack_pop (struct interval_stack *); +static void interval_tree_update_limit (const struct interval_tree *, struct interval_node *); +static void interval_tree_inherit_offset (const struct interval_tree *, struct interval_node *); +static void interval_tree_propagate_limit (const struct interval_tree *, struct interval_node *); +static void interval_tree_rotate_left (struct interval_tree *, struct interval_node *); +static void interval_tree_rotate_right (struct interval_tree *, struct interval_node *); +static void interval_tree_insert_fix (struct interval_tree *, struct interval_node *); +static void interval_tree_remove_fix (struct interval_tree *, struct interval_node *); +static void interval_tree_transplant (struct interval_tree *, struct interval_node *, struct interval_node *); +static struct interval_node *interval_tree_subtree_min (const struct interval_tree *, struct interval_node *); static struct interval_generator* interval_generator_create (struct interval_tree *); static void interval_generator_destroy (struct interval_generator *); static void interval_generator_reset (struct interval_generator *, @@ -124,7 +124,7 @@ interval_generator_narrow (struct interval_generator *g, ptrdiff_t begin, ptrdiff_t end); static inline struct interval_node* interval_generator_next (struct interval_generator *g); -static inline void interval_tree_iter_ensure_space(struct interval_tree *); +static inline void interval_tree_iter_ensure_space (struct interval_tree *); @@ -454,11 +454,10 @@ interval_tree_iter_start (struct interval_tree *tree, interval can only shrink, but never grow.*/ inline void -interval_tree_iter_narrow(struct interval_tree *tree, - ptrdiff_t begin, ptrdiff_t end) +interval_tree_iter_narrow (struct interval_tree *tree, + ptrdiff_t begin, ptrdiff_t end) { - if (! tree->iter_running) - emacs_abort (); + eassert (tree->iter_running); interval_generator_narrow (tree->iter, begin, end); } @@ -467,8 +466,7 @@ interval_tree_iter_narrow(struct interval_tree *tree, void interval_tree_iter_finish (struct interval_tree *tree) { - if (! tree->iter_running) - emacs_abort (); + eassert (tree->iter_running); tree->iter_running = false; } @@ -478,8 +476,7 @@ interval_tree_iter_finish (struct interval_tree *tree) inline struct interval_node* interval_tree_iter_next (struct interval_tree *tree) { - if (! tree->iter_running) - emacs_abort (); + eassert (tree->iter_running); return interval_generator_next (tree->iter); } commit 8e61648693c779a411aad5ce8ad3a493e4032155 Author: Stefan Monnier Date: Thu Sep 29 14:10:04 2022 -0400 * src/itree.c (interval_tree_iter_start): Improve error message diff --git a/src/itree.c b/src/itree.c index aa6fcc1bea..bdaa3fe09b 100644 --- a/src/itree.c +++ b/src/itree.c @@ -438,7 +438,12 @@ interval_tree_iter_start (struct interval_tree *tree, const char* file, int line) { if (tree->iter_running) - emacs_abort (); + { + fprintf (stderr, + "Detected nested iteration!\nOuter: %s:%d\nInner: %s:%d\n", + tree->file, tree->line, file, line); + emacs_abort (); + } interval_generator_reset (tree->iter, begin, end, order); tree->iter_running = true; tree->file = file; commit 5b954f8f9d9b4b015e5aa57a2307f92801669932 Author: Stefan Monnier Date: Thu Sep 29 09:08:37 2022 -0400 alloc.c: Avoid nested overlay iterations (bug#58158) * src/alloc.c (mark_overlays): New function. (mark_buffer): Use it instead of using the overlay iterator. diff --git a/src/alloc.c b/src/alloc.c index be55dcf8df..8dc45659b5 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -6512,6 +6512,21 @@ mark_overlay (struct Lisp_Overlay *ov) mark_object (ov->plist); } +static void +mark_overlays (struct interval_tree *it, struct interval_node *in) +{ + /* `left/right` are set to NULL when the overlay is deleted, but + they use the `null` node instead when the overlay is not deleted + (i.e. is within an overlay tree). */ + eassert (in); + if (in == &it->null) + return; + + mark_object (in->data); + mark_overlays (it, in->left); + mark_overlays (it, in->right); +} + /* Mark Lisp_Objects and special pointers in BUFFER. */ static void @@ -6533,11 +6548,8 @@ mark_buffer (struct buffer *buffer) if (!BUFFER_LIVE_P (buffer)) mark_object (BVAR (buffer, undo_list)); - struct interval_node *node; - buffer_overlay_iter_start (buffer, PTRDIFF_MIN, PTRDIFF_MAX, ITREE_ASCENDING); - while ((node = buffer_overlay_iter_next (buffer))) - mark_overlay (XOVERLAY (node->data)); - buffer_overlay_iter_finish (buffer); + if (buffer->overlays) + mark_overlays (buffer->overlays, buffer->overlays->root); /* If this is an indirect buffer, mark its base buffer. */ if (buffer->base_buffer && commit ea8daec9bb8ebf3cbca35edec4e4ef7b6edac3de Author: Stefan Monnier Date: Wed Sep 28 19:05:16 2022 -0400 itree.[ch]: Add sanity checks, comments, and minor tweaks * src/alloc.c (mark_overlay): Add sanity check. * src/buffer.c (next_overlay_change, previous_overlay_change): Tweak code to keep the same vars for the bounds. * src/itree.c (interval_tree_clear, interval_tree_insert) (interval_tree_remove, interval_tree_insert_fix, interval_tree_remove_fix): Adjust to the `color` -> `red` change. (interval_tree_clear): Prefer `true/false` for booleans. (interval_generator_create): Use an actual `interval_tree_order` value rather than 0. (interval_generator_next): Simplify a tiny bit. Add comment. (interval_generator_narrow): Add sanity check. * src/itree.h (struct interval_node): Replace `color` field with boolean `red` field. (enum interval_tree_order): Remove unused `ITREE_DEFLT_ORDER` value. * src/pdumper.c (dump_interval_node): Adjust to the `color` -> `red` change. diff --git a/src/alloc.c b/src/alloc.c index 20b8981bd6..be55dcf8df 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -6505,6 +6505,9 @@ mark_char_table (struct Lisp_Vector *ptr, enum pvec_type pvectype) static void mark_overlay (struct Lisp_Overlay *ov) { + /* We don't mark the `interval_node` object, because it is managed manually + rather than by the GC. */ + eassert (BASE_EQ (ov->interval->data, make_lisp_ptr (ov, Lisp_Vectorlike))); set_vectorlike_marked (&ov->header); mark_object (ov->plist); } diff --git a/src/buffer.c b/src/buffer.c index 2b1997fc8b..879e14be96 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -2992,7 +2992,7 @@ next_overlay_change (ptrdiff_t pos) ptrdiff_t next = ZV; struct interval_node *node; - buffer_overlay_iter_start (current_buffer, pos, ZV, ITREE_ASCENDING); + buffer_overlay_iter_start (current_buffer, pos, next, ITREE_ASCENDING); while ((node = buffer_overlay_iter_next (current_buffer))) { if (node->begin > pos) @@ -3020,7 +3020,7 @@ previous_overlay_change (ptrdiff_t pos) struct interval_node *node; ptrdiff_t prev = BEGV; - buffer_overlay_iter_start (current_buffer, BEGV, pos, ITREE_DESCENDING); + buffer_overlay_iter_start (current_buffer, prev, pos, ITREE_DESCENDING); while ((node = buffer_overlay_iter_next (current_buffer))) { if (node->end < pos) diff --git a/src/itree.c b/src/itree.c index e31ce39ba1..aa6fcc1bea 100644 --- a/src/itree.c +++ b/src/itree.c @@ -233,11 +233,11 @@ interval_tree_clear (struct interval_tree *tree) null->begin = PTRDIFF_MIN; null->end = PTRDIFF_MIN; null->limit = PTRDIFF_MIN; /* => max(x, null.limit) = x */ - null->color = ITREE_BLACK; + null->red = false; tree->root = null; tree->otick = 1; tree->size = 0; - tree->iter_running = 0; + tree->iter_running = false; } #ifdef ITREE_TESTING @@ -308,7 +308,7 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) node->parent = parent; node->left = &tree->null; node->right = &tree->null; - node->color = ITREE_RED; + node->red = true; node->offset = 0; node->begin -= offset; node->end -= offset; @@ -351,7 +351,7 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) { struct interval_node *subst = (node->right == &tree->null) ? node->left : node->right; - if (node->color == ITREE_BLACK) + if (!node->red) broken = subst; interval_tree_transplant (tree, subst, node); interval_tree_propagate_limit (tree, subst); @@ -361,7 +361,7 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) struct interval_node *min = interval_tree_subtree_min (tree, node->right); struct interval_node *min_right = min->right; - if (min->color == ITREE_BLACK) + if (!min->red) broken = min->right; if (min->parent == node) min_right->parent = min; /* set parent, if min_right = null */ @@ -375,7 +375,7 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) interval_tree_transplant (tree, min, node); min->left = node->left; min->left->parent = min; - min->color = node->color; + min->red = node->red; interval_tree_propagate_limit (tree, min_right); interval_tree_propagate_limit (tree, min); } @@ -440,7 +440,7 @@ interval_tree_iter_start (struct interval_tree *tree, if (tree->iter_running) emacs_abort (); interval_generator_reset (tree->iter, begin, end, order); - tree->iter_running = 1; + tree->iter_running = true; tree->file = file; tree->line = line; } @@ -464,7 +464,7 @@ interval_tree_iter_finish (struct interval_tree *tree) { if (! tree->iter_running) emacs_abort (); - tree->iter_running = 0; + tree->iter_running = false; } /* Return the next node of the iterator in the order given when it was @@ -637,7 +637,7 @@ interval_generator_create (struct interval_tree *tree) g->stack = interval_stack_create (size); g->tree = tree; - interval_generator_reset (g, 1, 0, 0); + interval_generator_reset (g, 1, 0, ITREE_ASCENDING); return g; } @@ -667,7 +667,13 @@ interval_generator_ensure_space (struct interval_generator *g) interval_stack_ensure_space (g->stack, interval_tree_max_height (g->tree) + 1); } -/* Return true, if NODE's interval intersects with [BEGIN, END). */ +/* Return true, if NODE's interval intersects with [BEGIN, END). + Note: We always include empty nodes at BEGIN (and not at END), + but if BEGIN==END, then we don't include non-empty nodes starting + at BEGIN or ending at END. This seems to match the behavior of the + old overlays code but it's not clear if it's The Right Thing + (e.g. it breaks the expectation that if NODE1 is included, then + a NODE2 strictly bigger than NODE1 should also be included). */ static inline bool interval_node_intersects (const struct interval_node *node, @@ -687,10 +693,29 @@ interval_generator_next (struct interval_generator *g) struct interval_node * const null = &g->tree->null; struct interval_node *node; - do { - node = interval_stack_pop (g->stack); + /* The `visited` flag stored in each node is used here (and only here): + We keep a "workstack" of nodes we need to consider. This stack + consist of nodes of two types: nodes that we have decided + should be returned by the generator, and nodes which we may + need to consider (including checking their children). + We start an iteration with a stack containing just the root + node marked as "not visited" which means that it (and its children) + needs to be considered but we haven't yet decided whether it's included + in the generator's output. */ + + /* FIXME: We should move the `visited` flag to the stack: each entry + there should simply consist of a node and a bool (the `visited` status) + so this internal implementation detail doesn't leak into the + `interval_node` structure. + [ In theory it would also allow multiple iterations to be active + at the same time, tho that does not seem particularly useful at + this time and would require further changes anyway. ] + To save space on the stack, we could hijack the LSB bit of the `node*` + word to hold the `visited` bit. */ - while (node && ! node->visited) + do { + while ((node = interval_stack_pop (g->stack)) + && ! node->visited) { struct interval_node * const left = node->left; struct interval_node * const right = node->right; @@ -724,7 +749,6 @@ interval_generator_next (struct interval_generator *g) interval_stack_push_flagged (g->stack, node, true); break; } - node = interval_stack_pop (g->stack); } /* Node may have been invalidated by interval_generator_narrow after it was pushed: Check if it still intersects. */ @@ -740,6 +764,8 @@ static inline void interval_generator_narrow (struct interval_generator *g, ptrdiff_t begin, ptrdiff_t end) { + eassert (begin >= g->begin); + eassert (end <= g->end); g->begin = max (begin, g->begin); g->end = min (end, g->end); } @@ -980,7 +1006,7 @@ interval_tree_rotate_right (struct interval_tree *tree, struct interval_node *no static void interval_tree_insert_fix (struct interval_tree *tree, struct interval_node *node) { - while (node->parent->color == ITREE_RED) + while (node->parent->red) { /* NODE is red and its parent is red. This is a violation of red-black tree property #3. */ @@ -991,14 +1017,14 @@ interval_tree_insert_fix (struct interval_tree *tree, struct interval_node *node our "uncle". */ struct interval_node *uncle = node->parent->parent->right; - if (uncle->color == ITREE_RED) /* case 1.a */ + if (uncle->red) /* case 1.a */ { /* Uncle and parent are red but should be black because NODE is red. Change the colors accordingly and proceed with the grandparent. */ - node->parent->color = ITREE_BLACK; - uncle->color = ITREE_BLACK; - node->parent->parent->color = ITREE_RED; + node->parent->red = false; + uncle->red = false; + node->parent->parent->red = true; node = node->parent->parent; } else @@ -1011,8 +1037,8 @@ interval_tree_insert_fix (struct interval_tree *tree, struct interval_node *node interval_tree_rotate_left (tree, node); } /* case 3.a */ - node->parent->color = ITREE_BLACK; - node->parent->parent->color = ITREE_RED; + node->parent->red = false; + node->parent->parent->red = true; interval_tree_rotate_right (tree, node->parent->parent); } } @@ -1021,11 +1047,11 @@ interval_tree_insert_fix (struct interval_tree *tree, struct interval_node *node /* This is the symmetrical case of above. */ struct interval_node *uncle = node->parent->parent->left; - if (uncle->color == ITREE_RED) /* case 1.b */ + if (uncle->red) /* case 1.b */ { - node->parent->color = ITREE_BLACK; - uncle->color = ITREE_BLACK; - node->parent->parent->color = ITREE_RED; + node->parent->red = false; + uncle->red = false; + node->parent->parent->red = true; node = node->parent->parent; } else @@ -1036,8 +1062,8 @@ interval_tree_insert_fix (struct interval_tree *tree, struct interval_node *node interval_tree_rotate_right (tree, node); } /* case 3.b */ - node->parent->color = ITREE_BLACK; - node->parent->parent->color = ITREE_RED; + node->parent->red = false; + node->parent->parent->red = true; interval_tree_rotate_left (tree, node->parent->parent); } } @@ -1045,7 +1071,7 @@ interval_tree_insert_fix (struct interval_tree *tree, struct interval_node *node /* The root may have been changed to red due to the algorithm. Set it to black so that property #5 is satisfied. */ - tree->root->color = ITREE_BLACK; + tree->root->red = false; } /* Repair the tree after a deletion. Part of the RB-Tree @@ -1054,38 +1080,38 @@ interval_tree_insert_fix (struct interval_tree *tree, struct interval_node *node static void interval_tree_remove_fix (struct interval_tree *tree, struct interval_node *node) { - while (node != tree->root && node->color == ITREE_BLACK) + while (node != tree->root && !node->red) { if (node == node->parent->left) { struct interval_node *other = node->parent->right; - if (other->color == ITREE_RED) /* case 1.a */ + if (other->red) /* case 1.a */ { - other->color = ITREE_BLACK; - node->parent->color = ITREE_RED; + other->red = false; + node->parent->red = true; interval_tree_rotate_left (tree, node->parent); other = node->parent->right; } - if (other->left->color == ITREE_BLACK /* 2.a */ - && other->right->color == ITREE_BLACK) + if (!other->left->red /* 2.a */ + && !other->right->red) { - other->color = ITREE_RED; + other->red = true; node = node->parent; } else { - if (other->right->color == ITREE_BLACK) /* 3.a */ + if (!other->right->red) /* 3.a */ { - other->left->color = ITREE_BLACK; - other->color = ITREE_RED; + other->left->red = false; + other->red = true; interval_tree_rotate_right (tree, other); other = node->parent->right; } - other->color = node->parent->color; /* 4.a */ - node->parent->color = ITREE_BLACK; - other->right->color = ITREE_BLACK; + other->red = node->parent->red; /* 4.a */ + node->parent->red = false; + other->right->red = false; interval_tree_rotate_left (tree, node->parent); node = tree->root; } @@ -1094,40 +1120,40 @@ interval_tree_remove_fix (struct interval_tree *tree, struct interval_node *node { struct interval_node *other = node->parent->left; - if (other->color == ITREE_RED) /* 1.b */ + if (other->red) /* 1.b */ { - other->color = ITREE_BLACK; - node->parent->color = ITREE_RED; + other->red = false; + node->parent->red = true; interval_tree_rotate_right (tree, node->parent); other = node->parent->left; } - if (other->right->color == ITREE_BLACK /* 2.b */ - && other->left->color == ITREE_BLACK) + if (!other->right->red /* 2.b */ + && !other->left->red) { - other->color = ITREE_RED; + other->red = true; node = node->parent; } else { - if (other->left->color == ITREE_BLACK) /* 3.b */ + if (!other->left->red) /* 3.b */ { - other->right->color = ITREE_BLACK; - other->color = ITREE_RED; + other->right->red = false; + other->red = true; interval_tree_rotate_left (tree, other); other = node->parent->left; } - other->color = node->parent->color; /* 4.b */ - node->parent->color = ITREE_BLACK; - other->left->color = ITREE_BLACK; + other->red = node->parent->red; /* 4.b */ + node->parent->red = false; + other->left->red = false; interval_tree_rotate_right (tree, node->parent); node = tree->root; } } } - node->color = ITREE_BLACK; + node->red = false; } /* Link node SOURCE in DEST's place. */ diff --git a/src/itree.h b/src/itree.h index e5d68fbfab..8f0454dc7a 100644 --- a/src/itree.h +++ b/src/itree.h @@ -45,8 +45,8 @@ struct interval_node ptrdiff_t offset; /* The amount of shift to apply to this subtree. */ uintmax_t otick; /* offset modified tick */ Lisp_Object data; /* Exclusively used by the client. */ - enum { ITREE_RED, ITREE_BLACK } color; - bool_bf visited : 1; /* For traversal via generator. */ + bool_bf red : 1; + bool_bf visited : 1; /* Internal to `interval_generator_next`. */ bool_bf rear_advance : 1; /* Same as for marker and overlays. */ bool_bf front_advance : 1; /* Same as for marker and overlays. */ }; @@ -64,8 +64,7 @@ struct interval_tree }; enum interval_tree_order { - ITREE_ASCENDING = 0, - ITREE_DEFLT_ORDER = 0, + ITREE_ASCENDING, ITREE_DESCENDING, ITREE_PRE_ORDER, }; diff --git a/src/pdumper.c b/src/pdumper.c index 79644c0151..44486015f0 100644 --- a/src/pdumper.c +++ b/src/pdumper.c @@ -2154,7 +2154,7 @@ dump_interval_node (struct dump_context *ctx, struct interval_node *node, DUMP_FIELD_COPY (&out, node, offset); DUMP_FIELD_COPY (&out, node, otick); dump_field_lv (ctx, &out, node, &node->data, WEIGHT_STRONG); - DUMP_FIELD_COPY (&out, node, color); + DUMP_FIELD_COPY (&out, node, red); DUMP_FIELD_COPY (&out, node, visited); DUMP_FIELD_COPY (&out, node, rear_advance); DUMP_FIELD_COPY (&out, node, front_advance); commit 800ecd4767df48beeefabccdacd089b8c4286529 Merge: 17ba7ac5d9 1a77f09f3c Author: Stefan Monnier Date: Wed Sep 28 11:57:16 2022 -0400 Merge remote-tracking branch 'origin/scratch/noverlay-wip' into noverlay commit 17ba7ac5d9295d39ec3b97712d061b3c7866a093 Author: Gerd Möllmann Date: Wed Sep 28 16:38:04 2022 +0200 Avoid nested iteration over intervals (bug#58144) * src/xdisp.c (strings_with_newlines): Call buffer_overlay_iter_finish. diff --git a/src/xdisp.c b/src/xdisp.c index cee75def80..6ecd3bdf24 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -7031,13 +7031,21 @@ strings_with_newlines (ptrdiff_t startpos, ptrdiff_t endpos, struct window *w) str = Foverlay_get (overlay, Qbefore_string); if (STRINGP (str) && SCHARS (str) && memchr (SDATA (str), '\n', SBYTES (str))) - return true; + { + buffer_overlay_iter_finish (current_buffer); + return true; + } str = Foverlay_get (overlay, Qafter_string); if (STRINGP (str) && SCHARS (str) && memchr (SDATA (str), '\n', SBYTES (str))) - return true; + { + buffer_overlay_iter_finish (current_buffer); + return true; + } } + buffer_overlay_iter_finish (current_buffer); + /* Check for 'display' properties whose values include strings. */ Lisp_Object cpos = make_fixnum (startpos); Lisp_Object limpos = make_fixnum (endpos); commit 498d331b07117408c5c5d8a3889b49b275b9a654 Author: Gerd Möllmann Date: Wed Sep 28 16:30:34 2022 +0200 Add debugging help for nested iterators (nug#58144) When starting an iteration, store __FILE__ and __LINE__ where this happens in the interval_tree structure. * src/buffer.h (buffer_overlay_iter_start): New macro adding __FILE and __LINE__. (buffer_overlay_iter_start1): Renamed from ..._start. * src/itree.h (struct interval_tree): Add file and line info. * src/itree.c: (interval_tree_contains, interval_tree_nodes, interval_tree_insert_gap): Pass __FILE__ and __LINE__ to iter_start. (interval_tree_iter_start): Record file and line info in tree. diff --git a/src/buffer.h b/src/buffer.h index 7d6c693b0f..097283be67 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -1457,13 +1457,16 @@ remove_buffer_overlay (struct buffer *b, struct Lisp_Overlay *ov) } INLINE void -buffer_overlay_iter_start (struct buffer *b, ptrdiff_t begin, ptrdiff_t end, - enum interval_tree_order order) +buffer_overlay_iter_start1 (struct buffer *b, ptrdiff_t begin, ptrdiff_t end, + enum interval_tree_order order, const char* file, int line) { if (b->overlays) - interval_tree_iter_start (b->overlays, begin, end, order); + interval_tree_iter_start (b->overlays, begin, end, order, file, line); } +#define buffer_overlay_iter_start(b, begin, end, order) \ + buffer_overlay_iter_start1 ((b), (begin), (end), (order), __FILE__, __LINE__) + INLINE struct interval_node* buffer_overlay_iter_next (struct buffer *b) { diff --git a/src/itree.c b/src/itree.c index a643997062..e31ce39ba1 100644 --- a/src/itree.c +++ b/src/itree.c @@ -328,7 +328,7 @@ interval_tree_contains (struct interval_tree *tree, struct interval_node *node) { struct interval_node *other; - interval_tree_iter_start (tree, node->begin, PTRDIFF_MAX, ITREE_ASCENDING); + interval_tree_iter_start (tree, node->begin, PTRDIFF_MAX, ITREE_ASCENDING, __FILE__, __LINE__); while ((other = interval_tree_iter_next (tree))) if (other == node) break; @@ -417,7 +417,7 @@ interval_tree_nodes (struct interval_tree *tree, { struct interval_node *node; - interval_tree_iter_start (tree, PTRDIFF_MIN, PTRDIFF_MAX, order); + interval_tree_iter_start (tree, PTRDIFF_MIN, PTRDIFF_MAX, order, __FILE__, __LINE__); while ((node = interval_tree_iter_next (tree))) { *nodes = node; @@ -434,12 +434,15 @@ interval_tree_nodes (struct interval_tree *tree, void interval_tree_iter_start (struct interval_tree *tree, ptrdiff_t begin, ptrdiff_t end, - enum interval_tree_order order) + enum interval_tree_order order, + const char* file, int line) { if (tree->iter_running) emacs_abort (); interval_generator_reset (tree->iter, begin, end, order); tree->iter_running = 1; + tree->file = file; + tree->line = line; } /* Limit the search interval of the iterator to the given values. The @@ -511,7 +514,7 @@ interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l order, so we need to remove them first. */ struct interval_stack *saved = interval_stack_create (0); struct interval_node *node = NULL; - interval_tree_iter_start (tree, pos, pos + 1, ITREE_PRE_ORDER); + interval_tree_iter_start (tree, pos, pos + 1, ITREE_PRE_ORDER, __FILE__, __LINE__); while ((node = interval_tree_iter_next (tree))) { if (node->begin == pos && node->front_advance diff --git a/src/itree.h b/src/itree.h index 21d8b21a02..e5d68fbfab 100644 --- a/src/itree.h +++ b/src/itree.h @@ -59,6 +59,8 @@ struct interval_tree intmax_t size; /* Number of nodes in the tree. */ struct interval_generator *iter; bool_bf iter_running : 1; + const char* file; + int line; }; enum interval_tree_order { @@ -79,7 +81,8 @@ void interval_tree_clear (struct interval_tree *); void interval_tree_insert (struct interval_tree *, struct interval_node *); bool interval_tree_contains (struct interval_tree *, struct interval_node *); struct interval_node *interval_tree_remove (struct interval_tree *, struct interval_node *); -void interval_tree_iter_start (struct interval_tree *, ptrdiff_t, ptrdiff_t, enum interval_tree_order); +void interval_tree_iter_start (struct interval_tree *, ptrdiff_t, ptrdiff_t, enum interval_tree_order, + const char* file, int line); void interval_tree_iter_narrow (struct interval_tree *, ptrdiff_t, ptrdiff_t); void interval_tree_iter_finish (struct interval_tree *); struct interval_node *interval_tree_iter_next (struct interval_tree *); commit 8173a292e7366beff0279480cbd2f73bf2357568 Author: Gerd Möllmann Date: Wed Sep 28 12:10:30 2022 +0200 Fix last change diff --git a/src/itree.c b/src/itree.c index ab734c3c18..a643997062 100644 --- a/src/itree.c +++ b/src/itree.c @@ -281,7 +281,7 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) eassert (node && node->begin <= node->end && node != &tree->null); struct interval_node *parent = &tree->null; - struct interval_node *child = &tree->null; + struct interval_node *child = tree->root; ptrdiff_t offset = 0; /* Find the insertion point, accumulate node's offset and update commit 409327ff68f9ccdc8099f6a2ba2fee76abaaab70 Author: Gerd Möllmann Date: Tue Sep 27 12:45:16 2022 +0200 Fix macOS build (bug#58108) * src/itree.h (struct interval_tree): Rename member nil to null. * src/itree.c: Use null instead of nil * src/pdumper.c (dump_buffer): Use null instead of nil. * src/itree.c: Fix copyright. * src/itree.h: Fix copyright. diff --git a/src/itree.c b/src/itree.c index adb55fe950..ab734c3c18 100644 --- a/src/itree.c +++ b/src/itree.c @@ -1,8 +1,8 @@ /* This file implements an efficient interval data-structure. -Copyright (C) 2017 Andreas Politz (politza@hochschule-trier.de) +Copyright (C) 2017-2022 Free Software Foundation, Inc. -This file is not part of GNU Emacs. +This file is part of GNU Emacs. GNU Emacs is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -227,14 +227,14 @@ interval_tree_create (void) void interval_tree_clear (struct interval_tree *tree) { - struct interval_node *nil = &tree->nil; - nil->left = nil->right = nil->parent = nil; - nil->offset = nil->otick = 0; - nil->begin = PTRDIFF_MIN; - nil->end = PTRDIFF_MIN; - nil->limit = PTRDIFF_MIN; /* => max(x, nil.limit) = x */ - nil->color = ITREE_BLACK; - tree->root = nil; + struct interval_node *null = &tree->null; + null->left = null->right = null->parent = null; + null->offset = null->otick = 0; + null->begin = PTRDIFF_MIN; + null->end = PTRDIFF_MIN; + null->limit = PTRDIFF_MIN; /* => max(x, null.limit) = x */ + null->color = ITREE_BLACK; + tree->root = null; tree->otick = 1; tree->size = 0; tree->iter_running = 0; @@ -278,15 +278,15 @@ interval_tree_size (struct interval_tree *tree) void interval_tree_insert (struct interval_tree *tree, struct interval_node *node) { - eassert (node && node->begin <= node->end && node != &tree->nil); + eassert (node && node->begin <= node->end && node != &tree->null); - struct interval_node *parent = &tree->nil; - struct interval_node *child = tree->root; + struct interval_node *parent = &tree->null; + struct interval_node *child = &tree->null; ptrdiff_t offset = 0; /* Find the insertion point, accumulate node's offset and update ancestors limit values. */ - while (child != &tree->nil) + while (child != &tree->null) { parent = child; offset += child->offset; @@ -297,7 +297,7 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) } /* Insert the node */ - if (parent == &tree->nil) + if (parent == &tree->null) tree->root = node; else if (node->begin <= parent->begin) parent->left = node; @@ -306,8 +306,8 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) /* Init the node */ node->parent = parent; - node->left = &tree->nil; - node->right = &tree->nil; + node->left = &tree->null; + node->right = &tree->null; node->color = ITREE_RED; node->offset = 0; node->begin -= offset; @@ -347,10 +347,10 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) struct interval_node *broken = NULL; interval_tree_inherit_offset (tree, node); - if (node->left == &tree->nil || node->right == &tree->nil) + if (node->left == &tree->null || node->right == &tree->null) { struct interval_node *subst = - (node->right == &tree->nil) ? node->left : node->right; + (node->right == &tree->null) ? node->left : node->right; if (node->color == ITREE_BLACK) broken = subst; interval_tree_transplant (tree, subst, node); @@ -364,7 +364,7 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) if (min->color == ITREE_BLACK) broken = min->right; if (min->parent == node) - min_right->parent = min; /* set parent, if min_right = nil */ + min_right->parent = min; /* set parent, if min_right = null */ else { interval_tree_transplant (tree, min->right, min); @@ -386,7 +386,7 @@ interval_tree_remove (struct interval_tree *tree, struct interval_node *node) node->right = node->left = node->parent = NULL; --tree->size; - eassert (tree->size == 0 || (tree->size > 0 && tree->root != &tree->nil)); + eassert (tree->size == 0 || (tree->size > 0 && tree->root != &tree->null)); return node; } @@ -395,7 +395,7 @@ static struct interval_node* interval_tree_validate (struct interval_tree *tree, struct interval_node *node) { - if (tree->otick == node->otick || node == &tree->nil) + if (tree->otick == node->otick || node == &tree->null) return node; if (node != tree->root) interval_tree_validate (tree, node->parent); @@ -532,7 +532,7 @@ interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l { /* Process in pre-order. */ interval_tree_inherit_offset (tree, node); - if (node->right != &tree->nil) + if (node->right != &tree->null) { if (node->begin > pos) { @@ -543,7 +543,7 @@ interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l else interval_stack_push (stack, node->right); } - if (node->left != &tree->nil + if (node->left != &tree->null && pos <= node->left->limit + node->left->offset) interval_stack_push (stack, node->left); @@ -592,7 +592,7 @@ interval_tree_delete_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l while ((node = interval_stack_pop (stack))) { interval_tree_inherit_offset (tree, node); - if (node->right != &tree->nil) + if (node->right != &tree->null) { if (node->begin > pos + length) { @@ -603,7 +603,7 @@ interval_tree_delete_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l else interval_stack_push (stack, node->right); } - if (node->left != &tree->nil + if (node->left != &tree->null && pos <= node->left->limit + node->left->offset) interval_stack_push (stack, node->left); @@ -681,7 +681,7 @@ interval_generator_next (struct interval_generator *g) { if (! g) return NULL; - struct interval_node * const nil = &g->tree->nil; + struct interval_node * const null = &g->tree->null; struct interval_node *node; do { @@ -696,26 +696,26 @@ interval_generator_next (struct interval_generator *g) switch (g->order) { case ITREE_ASCENDING: - if (right != nil && node->begin <= g->end) + if (right != null && node->begin <= g->end) interval_stack_push_flagged (g->stack, right, false); if (interval_node_intersects (node, g->begin, g->end)) interval_stack_push_flagged (g->stack, node, true); /* Node's children may still be off-set and we need to add it. */ - if (left != nil && g->begin <= left->limit + left->offset) + if (left != null && g->begin <= left->limit + left->offset) interval_stack_push_flagged (g->stack, left, false); break; case ITREE_DESCENDING: - if (left != nil && g->begin <= left->limit + left->offset) + if (left != null && g->begin <= left->limit + left->offset) interval_stack_push_flagged (g->stack, left, false); if (interval_node_intersects (node, g->begin, g->end)) interval_stack_push_flagged (g->stack, node, true); - if (right != nil && node->begin <= g->end) + if (right != null && node->begin <= g->end) interval_stack_push_flagged (g->stack, right, false); break; case ITREE_PRE_ORDER: - if (right != nil && node->begin <= g->end) + if (right != null && node->begin <= g->end) interval_stack_push_flagged (g->stack, right, false); - if (left != nil && g->begin <= left->limit + left->offset) + if (left != null && g->begin <= left->limit + left->offset) interval_stack_push_flagged (g->stack, left, false); if (interval_node_intersects (node, g->begin, g->end)) interval_stack_push_flagged (g->stack, node, true); @@ -832,7 +832,7 @@ static void interval_tree_update_limit (const struct interval_tree *tree, struct interval_node *node) { - if (node == &tree->nil) + if (node == &tree->null) return; node->limit = max (node->end, max (node->left->limit + node->left->offset, @@ -856,9 +856,9 @@ interval_tree_inherit_offset (const struct interval_tree *tree, node->begin += node->offset; node->end += node->offset; node->limit += node->offset; - if (node->left != &tree->nil) + if (node->left != &tree->null) node->left->offset += node->offset; - if (node->right != &tree->nil) + if (node->right != &tree->null) node->right->offset += node->offset; node->offset = 0; if (node == tree->root || node->parent->otick == tree->otick) @@ -868,7 +868,7 @@ interval_tree_inherit_offset (const struct interval_tree *tree, /* Update limit of NODE and its ancestors. Stop when it becomes stable, i.e. new_limit = old_limit. - NODE may also be the nil node, in which case its parent is + NODE may also be the null node, in which case its parent is used. (This feature is due to the RB algorithm.) */ @@ -876,9 +876,9 @@ static void interval_tree_propagate_limit (const struct interval_tree *tree, struct interval_node *node) { - if (node == &tree->nil) + if (node == &tree->null) node = node->parent; - if (node == &tree->nil) + if (node == &tree->null) return; while (1) { @@ -898,7 +898,7 @@ interval_tree_propagate_limit (const struct interval_tree *tree, static void interval_tree_rotate_left (struct interval_tree *tree, struct interval_node *node) { - eassert (node->right != &tree->nil); + eassert (node->right != &tree->null); struct interval_node *right = node->right; @@ -907,11 +907,11 @@ interval_tree_rotate_left (struct interval_tree *tree, struct interval_node *nod /* Turn right's left subtree into node's right subtree. */ node->right = right->left; - if (right->left != &tree->nil) + if (right->left != &tree->null) right->left->parent = node; /* right's parent was node's parent. */ - if (right != &tree->nil) + if (right != &tree->null) right->parent = node->parent; /* Get the parent to point to right instead of node. */ @@ -927,7 +927,7 @@ interval_tree_rotate_left (struct interval_tree *tree, struct interval_node *nod /* Put node on right's left. */ right->left = node; - if (node != &tree->nil) + if (node != &tree->null) node->parent = right; /* Order matters here. */ @@ -940,7 +940,7 @@ interval_tree_rotate_left (struct interval_tree *tree, struct interval_node *nod static void interval_tree_rotate_right (struct interval_tree *tree, struct interval_node *node) { - eassert (tree && node && node->left != &tree->nil); + eassert (tree && node && node->left != &tree->null); struct interval_node *left = node->left; @@ -948,10 +948,10 @@ interval_tree_rotate_right (struct interval_tree *tree, struct interval_node *no interval_tree_inherit_offset (tree, left); node->left = left->right; - if (left->right != &tree->nil) + if (left->right != &tree->null) left->right->parent = node; - if (left != &tree->nil) + if (left != &tree->null) left->parent = node->parent; if (node != tree->root) { @@ -964,7 +964,7 @@ interval_tree_rotate_right (struct interval_tree *tree, struct interval_node *no tree->root = left; left->right = node; - if (node != &tree->nil) + if (node != &tree->null) node->parent = left; interval_tree_update_limit (tree, left); @@ -1133,7 +1133,7 @@ static void interval_tree_transplant (struct interval_tree *tree, struct interval_node *source, struct interval_node *dest) { - eassert (tree && source && dest && dest != &tree->nil); + eassert (tree && source && dest && dest != &tree->null); if (dest == tree->root) tree->root = source; @@ -1149,9 +1149,9 @@ interval_tree_transplant (struct interval_tree *tree, struct interval_node *sour static struct interval_node* interval_tree_subtree_min (const struct interval_tree *tree, struct interval_node *node) { - if (node == &tree->nil) + if (node == &tree->null) return node; - while (node->left != &tree->nil) + while (node->left != &tree->null) node = node->left; return node; } diff --git a/src/itree.h b/src/itree.h index 08b152f92d..21d8b21a02 100644 --- a/src/itree.h +++ b/src/itree.h @@ -1,8 +1,8 @@ /* This file implements an efficient interval data-structure. -Copyright (C) 2017 Andreas Politz (politza@hochschule-trier.de) +Copyright (C) 2017-2022 Free Software Foundation, Inc. -This file is not part of GNU Emacs. +This file is part of GNU Emacs. GNU Emacs is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -54,7 +54,7 @@ struct interval_node struct interval_tree { struct interval_node *root; - struct interval_node nil; /* The tree's version of NULL. */ + struct interval_node null; /* The tree's version of NULL. */ uintmax_t otick; /* offset tick, compared with node's otick. */ intmax_t size; /* Number of nodes in the tree. */ struct interval_generator *iter; diff --git a/src/pdumper.c b/src/pdumper.c index 7618f5d1e8..6c8c213179 100644 --- a/src/pdumper.c +++ b/src/pdumper.c @@ -2865,7 +2865,7 @@ dump_buffer (struct dump_context *ctx, const struct buffer *in_buffer) DUMP_FIELD_COPY (out, buffer, long_line_optimizations_p); if (buffer->overlays - && (buffer->overlays->root != &buffer->overlays->nil)) + && (buffer->overlays->root != &buffer->overlays->null)) /* We haven't implemented the code to dump overlays. */ emacs_abort (); else commit 1a77f09f3ceaafa81cd400fc825f16b194b627b1 (refs/remotes/origin/scratch/noverlay-wip) Author: Stefan Monnier Date: Sun Sep 25 22:24:35 2022 -0400 * src/buffer.c (overlays_in): Fix confusion Z-vs-ZV This fixes test failures in `test-overlays-in-2` and `test-remove-overlays`. diff --git a/src/buffer.c b/src/buffer.c index 1bb2af98e7..2b1997fc8b 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -2933,7 +2933,7 @@ overlays_in (ptrdiff_t beg, ptrdiff_t end, bool extend, buffer_overlay_iter_start (current_buffer, beg, /* Find empty OV at Z ? */ - (end >= Z && empty) ? Z + 1 : ZV, + (end >= ZV && empty) ? ZV + 1 : ZV, ITREE_ASCENDING); while ((node = buffer_overlay_iter_next (current_buffer))) @@ -2946,7 +2946,7 @@ overlays_in (ptrdiff_t beg, ptrdiff_t end, bool extend, else if (node->begin == end) { next = node->begin; - if ((! empty || end < Z) && beg < end) + if ((! empty || end < ZV) && beg < end) break; } diff --git a/src/pdumper.c b/src/pdumper.c index 7618f5d1e8..7053c2d74f 100644 --- a/src/pdumper.c +++ b/src/pdumper.c @@ -2871,9 +2871,6 @@ dump_buffer (struct dump_context *ctx, const struct buffer *in_buffer) else out->overlays = NULL; - /* dump_field_lv_rawptr (ctx, out, buffer, &buffer->overlays, - ?Lisp_Vectorlike?, WEIGHT_NORMAL); */ - dump_field_lv (ctx, out, buffer, &buffer->undo_list_, WEIGHT_STRONG); dump_off offset = finish_dump_pvec (ctx, &out->header); commit 650c20f1ca4e07591a727e1cfcc74b3363d15985 Merge: 8869332684 4b85ae6a24 Author: Stefan Monnier Date: Sun Sep 25 16:15:16 2022 -0400 Merge 'master' into noverlay commit 8869332684c2302b5ba1ead4568bbc7ba1c0183e Author: Andreas Politz Date: Mon Oct 9 08:22:43 2017 +0200 Remove redundant checks for the empty overlay tree * src/alloc.c (mark_buffer): Remove b->overlays check. * src/buffer.c (copy_overlays): Also. * src/buffer.c (swap_buffer_overlays, overlays_in): Also. (next_overlay_change, previous_overlay_change): Also. (overlay_touches_p, Foverlay_lists): Also. (evaporate_overlays): Also. * src/xdisp.c (load_overlay_strings): Also. diff --git a/src/alloc.c b/src/alloc.c index 15a6fc43b7..9f72f914e0 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -6306,15 +6306,11 @@ mark_buffer (struct buffer *buffer) a special way just before the sweep phase, and after stripping some of its elements that are not needed any more. */ - if (buffer->overlays) - { - struct interval_node *node; - buffer_overlay_iter_start (buffer, PTRDIFF_MIN, PTRDIFF_MAX, ITREE_ASCENDING); - - while ((node = buffer_overlay_iter_next (buffer))) - mark_overlay (XOVERLAY (node->data)); - buffer_overlay_iter_finish (buffer); - } + struct interval_node *node; + buffer_overlay_iter_start (buffer, PTRDIFF_MIN, PTRDIFF_MAX, ITREE_ASCENDING); + while ((node = buffer_overlay_iter_next (buffer))) + mark_overlay (XOVERLAY (node->data)); + buffer_overlay_iter_finish (buffer); /* If this is an indirect buffer, mark its base buffer. */ if (buffer->base_buffer && !VECTOR_MARKED_P (buffer->base_buffer)) diff --git a/src/buffer.c b/src/buffer.c index 122c60fab3..9ddc9c7e05 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -607,14 +607,9 @@ static void copy_overlays (struct buffer *from, struct buffer *to) { eassert (to && ! to->overlays); - struct interval_node *node; - if (! from->overlays) - return; - buffer_overlay_iter_start (from, PTRDIFF_MIN, PTRDIFF_MAX, ITREE_ASCENDING); - while ((node = buffer_overlay_iter_next (from))) { Lisp_Object ov = node->data; @@ -2277,20 +2272,16 @@ swap_buffer_overlays (struct buffer *buffer, struct buffer *other) { struct interval_node *node; - if (buffer->overlays) - { - buffer_overlay_iter_start (buffer, PTRDIFF_MIN, PTRDIFF_MAX, ITREE_ASCENDING); - while ((node = buffer_overlay_iter_next (buffer))) - XOVERLAY (node->data)->buffer = other; - buffer_overlay_iter_finish (buffer); - } - if (other->overlays) - { - buffer_overlay_iter_start (other, PTRDIFF_MIN, PTRDIFF_MAX, ITREE_ASCENDING); - while ((node = buffer_overlay_iter_next (other))) - XOVERLAY (node->data)->buffer = buffer; - buffer_overlay_iter_finish (other); - } + buffer_overlay_iter_start (buffer, PTRDIFF_MIN, PTRDIFF_MAX, ITREE_ASCENDING); + while ((node = buffer_overlay_iter_next (buffer))) + XOVERLAY (node->data)->buffer = other; + buffer_overlay_iter_finish (buffer); + + buffer_overlay_iter_start (other, PTRDIFF_MIN, PTRDIFF_MAX, ITREE_ASCENDING); + while ((node = buffer_overlay_iter_next (other))) + XOVERLAY (node->data)->buffer = buffer; + buffer_overlay_iter_finish (other); + /* Swap the interval trees. */ void *tmp = buffer->overlays; buffer->overlays = other->overlays; @@ -2837,12 +2828,8 @@ overlays_in (ptrdiff_t beg, ptrdiff_t end, bool extend, ptrdiff_t len = *len_ptr; ptrdiff_t next = ZV; Lisp_Object *vec = *vec_ptr; - struct interval_node *node; - if (! current_buffer->overlays) - return idx; - buffer_overlay_iter_start (current_buffer, beg, /* Find empty OV at Z ? */ (end >= Z && empty) ? Z + 1 : ZV, @@ -2904,9 +2891,6 @@ next_overlay_change (ptrdiff_t pos) ptrdiff_t next = ZV; struct interval_node *node; - if (! current_buffer->overlays) - return next; - buffer_overlay_iter_start (current_buffer, pos, ZV, ITREE_ASCENDING); while ((node = buffer_overlay_iter_next (current_buffer))) { @@ -2935,9 +2919,6 @@ previous_overlay_change (ptrdiff_t pos) struct interval_node *node; ptrdiff_t prev = BEGV; - if (! current_buffer->overlays) - return prev; - buffer_overlay_iter_start (current_buffer, BEGV, pos, ITREE_DESCENDING); while ((node = buffer_overlay_iter_next (current_buffer))) { @@ -3023,9 +3004,6 @@ overlay_touches_p (ptrdiff_t pos) struct interval_node *node; bool result = false; - if (! current_buffer->overlays) - return false; - /* We need to find overlays ending in pos, as well as empty ones at pos. */ buffer_overlay_iter_start (current_buffer, @@ -3729,16 +3707,13 @@ However, the overlays you get are the real objects that the buffer uses. */) (void) { Lisp_Object overlays = Qnil; + struct interval_node *node; - if (current_buffer->overlays) - { - struct interval_node *node; + buffer_overlay_iter_start (current_buffer, BEG, Z, ITREE_DESCENDING); + while ((node = buffer_overlay_iter_next (current_buffer))) + overlays = Fcons (node->data, overlays); + buffer_overlay_iter_finish (current_buffer); - buffer_overlay_iter_start (current_buffer, BEG, Z, ITREE_DESCENDING); - while ((node = buffer_overlay_iter_next (current_buffer))) - overlays = Fcons (node->data, overlays); - buffer_overlay_iter_finish (current_buffer); - } return Fcons (overlays, Qnil); } @@ -3972,15 +3947,10 @@ call_overlay_mod_hooks (Lisp_Object list, Lisp_Object overlay, bool after, void evaporate_overlays (ptrdiff_t pos) { - Lisp_Object hit_list; + Lisp_Object hit_list = Qnil; struct interval_node *node; - if (! current_buffer->overlays) - return; - - hit_list = Qnil; buffer_overlay_iter_start (current_buffer, pos, pos, ITREE_ASCENDING); - while ((node = buffer_overlay_iter_next (current_buffer))) { if (node->end == pos @@ -3988,6 +3958,7 @@ evaporate_overlays (ptrdiff_t pos) hit_list = Fcons (node->data, hit_list); } buffer_overlay_iter_finish (current_buffer); + for (; CONSP (hit_list); hit_list = XCDR (hit_list)) Fdelete_overlay (XCAR (hit_list)); } diff --git a/src/xdisp.c b/src/xdisp.c index b3b9ecae37..f94643b1f7 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -5790,48 +5790,46 @@ load_overlay_strings (struct it *it, ptrdiff_t charpos) } \ while (false) - if (current_buffer->overlays) - { - buffer_overlay_iter_start (current_buffer, - charpos - 1, charpos + 1, ITREE_DESCENDING); - /* Process overlays. */ - while ((node = buffer_overlay_iter_next (current_buffer))) - { - overlay = node->data; - eassert (OVERLAYP (overlay)); - start = node->begin; - end = node->end; - - /* Skip this overlay if it doesn't start or end at IT's current - position. */ - if (end != charpos && start != charpos) - continue; - - /* Skip this overlay if it doesn't apply to IT->w. */ - window = Foverlay_get (overlay, Qwindow); - if (WINDOWP (window) && XWINDOW (window) != it->w) - continue; - - /* If the text ``under'' the overlay is invisible, both before- - and after-strings from this overlay are visible; start and - end position are indistinguishable. */ - invisible = Foverlay_get (overlay, Qinvisible); - invis = TEXT_PROP_MEANS_INVISIBLE (invisible); - - /* If overlay has a non-empty before-string, record it. */ - if ((start == charpos || (end == charpos && invis != 0)) - && (str = Foverlay_get (overlay, Qbefore_string), STRINGP (str)) - && SCHARS (str)) - RECORD_OVERLAY_STRING (overlay, str, false); - - /* If overlay has a non-empty after-string, record it. */ - if ((end == charpos || (start == charpos && invis != 0)) - && (str = Foverlay_get (overlay, Qafter_string), STRINGP (str)) - && SCHARS (str)) - RECORD_OVERLAY_STRING (overlay, str, true); - } - buffer_overlay_iter_finish (current_buffer); - } + + buffer_overlay_iter_start (current_buffer, + charpos - 1, charpos + 1, ITREE_DESCENDING); + /* Process overlays. */ + while ((node = buffer_overlay_iter_next (current_buffer))) + { + overlay = node->data; + eassert (OVERLAYP (overlay)); + start = node->begin; + end = node->end; + + /* Skip this overlay if it doesn't start or end at IT's current + position. */ + if (end != charpos && start != charpos) + continue; + + /* Skip this overlay if it doesn't apply to IT->w. */ + window = Foverlay_get (overlay, Qwindow); + if (WINDOWP (window) && XWINDOW (window) != it->w) + continue; + + /* If the text ``under'' the overlay is invisible, both before- + and after-strings from this overlay are visible; start and + end position are indistinguishable. */ + invisible = Foverlay_get (overlay, Qinvisible); + invis = TEXT_PROP_MEANS_INVISIBLE (invisible); + + /* If overlay has a non-empty before-string, record it. */ + if ((start == charpos || (end == charpos && invis != 0)) + && (str = Foverlay_get (overlay, Qbefore_string), STRINGP (str)) + && SCHARS (str)) + RECORD_OVERLAY_STRING (overlay, str, false); + + /* If overlay has a non-empty after-string, record it. */ + if ((end == charpos || (start == charpos && invis != 0)) + && (str = Foverlay_get (overlay, Qafter_string), STRINGP (str)) + && SCHARS (str)) + RECORD_OVERLAY_STRING (overlay, str, true); + } + buffer_overlay_iter_finish (current_buffer); #undef RECORD_OVERLAY_STRING commit a38c38c0526894ff05e2317994703c1157d5f909 Author: Andreas Politz Date: Sat Oct 7 17:19:45 2017 +0200 Make boolean struct member use one bit * src/itree.h (struct interval_tree): Add bit descriptor. diff --git a/src/itree.h b/src/itree.h index 7a5c4ae02a..2f418f0cf8 100644 --- a/src/itree.h +++ b/src/itree.h @@ -58,7 +58,7 @@ struct interval_tree uintmax_t otick; /* offset tick, compared with node's otick. */ intmax_t size; /* Number of nodes in the tree. */ struct interval_generator *iter; - bool_bf iter_running; + bool_bf iter_running : 1; }; enum interval_tree_order { commit 55ac6f21053935fb8369a33a827f5df2f99e872a Author: Andreas Politz Date: Sat Oct 7 17:18:37 2017 +0200 Optimize struct layout for space * src/itree.h (struct interval_node): Move color member near the end. diff --git a/src/itree.h b/src/itree.h index 80115aa467..7a5c4ae02a 100644 --- a/src/itree.h +++ b/src/itree.h @@ -36,7 +36,6 @@ along with GNU Emacs. If not, see . */ struct interval_node; struct interval_node { - enum { ITREE_RED, ITREE_BLACK } color; struct interval_node *parent; struct interval_node *left; struct interval_node *right; @@ -46,6 +45,7 @@ struct interval_node ptrdiff_t offset; /* The amount of shift to apply to this subtree. */ uintmax_t otick; /* offset modified tick */ Lisp_Object data; /* Exclusively used by the client. */ + enum { ITREE_RED, ITREE_BLACK } color; bool_bf visited : 1; /* For traversal via generator. */ bool_bf rear_advance : 1; /* Same as for marker and overlays. */ bool_bf front_advance : 1; /* Same as for marker and overlays. */ commit 46a3a8f6ed31ff4d0411ecc4cb276345a02e23dc Author: Andreas Politz Date: Sat Oct 7 17:11:05 2017 +0200 Make boolean struct member use one bit * src/itree.h (struct interval_node): Add bit descriptor. diff --git a/src/itree.h b/src/itree.h index d685a69eb7..80115aa467 100644 --- a/src/itree.h +++ b/src/itree.h @@ -46,7 +46,7 @@ struct interval_node ptrdiff_t offset; /* The amount of shift to apply to this subtree. */ uintmax_t otick; /* offset modified tick */ Lisp_Object data; /* Exclusively used by the client. */ - bool_bf visited; /* For traversal via generator. */ + bool_bf visited : 1; /* For traversal via generator. */ bool_bf rear_advance : 1; /* Same as for marker and overlays. */ bool_bf front_advance : 1; /* Same as for marker and overlays. */ }; commit 1ebc62c41e0cb622b04cbbfab692c617bb59d263 Author: Andreas Politz Date: Sat Oct 7 16:19:52 2017 +0200 Expand overlay multibyte tests * test/src/buffer-tests.el (test-overlay-multibyte-transition-1): Expand test. (test-overlay-multibyte-transition-2): New test. diff --git a/test/src/buffer-tests.el b/test/src/buffer-tests.el index c2c9d06033..153aea3a20 100644 --- a/test/src/buffer-tests.el +++ b/test/src/buffer-tests.el @@ -1215,10 +1215,65 @@ with parameters from the *Messages* buffer modification." (ert-deftest test-overlay-multibyte-transition-1 () (with-temp-buffer (set-buffer-multibyte t) - (insert "Ä") - (let ((ov (make-overlay (point-min) (point-max)))) + (insert "ääää") + ;; aeaeaeae + ;; 1 2 3 4 5 + ;; 123456789 + (let ((nonempty-bob (make-overlay 1 2)) + (empty-bob (make-overlay 1 1)) + (empty (make-overlay 2 2)) + (nonempty (make-overlay 2 4)) + (nonempty-eob (make-overlay 4 5)) + (empty-eob (make-overlay 5 5))) (set-buffer-multibyte nil) - (should (eq (overlay-end ov) (point-max)))))) + (cl-macrolet ((ovshould (ov begin end) + `(should (equal (list (overlay-start ,ov) (overlay-end ,ov)) + (list ,begin ,end))))) + (ovshould nonempty-bob 1 3) + (ovshould empty-bob 1 1) + (ovshould empty 3 3) + (ovshould nonempty 3 7) + (ovshould nonempty-eob 7 9) + (ovshould empty-eob 9 9))))) + +(ert-deftest test-overlay-multibyte-transition-2 () + (with-temp-buffer + (set-buffer-multibyte t) + (insert "ääää") + (set-buffer-multibyte nil) + ;; aeaeaeae + ;; 1 2 3 4 5 + ;; 123456789 + (let ((nonempty-bob-end (make-overlay 1 2)) + (nonempty-bob-beg (make-overlay 1 3)) + (empty-bob (make-overlay 1 1)) + (empty-beg (make-overlay 3 3)) + (empty-end (make-overlay 2 2)) + (nonempty-beg-beg (make-overlay 3 7)) + (nonempty-beg-end (make-overlay 3 8)) + (nonempty-end-beg (make-overlay 4 7)) + (nonempty-end-end (make-overlay 4 8)) + (nonempty-eob-beg (make-overlay 5 9)) + (nonempty-eob-end (make-overlay 6 9)) + (empty-eob (make-overlay 9 9))) + (set-buffer-multibyte t) + (cl-macrolet ((ovshould (ov begin end) + `(should (equal (list (overlay-start ,ov) (overlay-end ,ov)) + (list ,begin ,end))))) + (ovshould nonempty-bob-end 1 2) + (ovshould nonempty-bob-beg 1 2) + (ovshould empty-bob 1 1) + (ovshould empty-beg 2 2) + (ovshould empty-end 2 2) + (ovshould nonempty-beg-beg 2 4) + (ovshould nonempty-beg-end 2 5) + (ovshould nonempty-end-beg 3 4) + (ovshould nonempty-end-end 3 5) + (ovshould nonempty-eob-beg 3 5) + (ovshould nonempty-eob-end 4 5) + (ovshould empty-eob 5 5))))) + + ;; +===================================================================================+ commit c2826d9a0312981e2553f699d3afe9b39880f6ce Author: Andreas Politz Date: Fri Oct 6 12:02:54 2017 +0200 Adapt overlays when multibyteness changes * src/buffer.c (set_overlays_multibyte): New function. (set-buffer-multibyte): Use function. diff --git a/src/buffer.c b/src/buffer.c index 21c040ad0e..122c60fab3 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -879,6 +879,56 @@ delete_all_overlays (struct buffer *b) interval_tree_clear (b->overlays); } +/* Adjust the position of overlays in the current buffer according to + MULTIBYTE. + + Assume that positions currently correspond to byte positions, if + MULTIBYTE is true and to character positions if not. +*/ + +static void +set_overlays_multibyte (bool multibyte) +{ + if (! current_buffer->overlays || Z == Z_BYTE) + return; + + struct interval_node **nodes = NULL; + struct interval_tree *tree = current_buffer->overlays; + const intmax_t size = interval_tree_size (tree); + + USE_SAFE_ALLOCA; + SAFE_NALLOCA (nodes, 1, size); + interval_tree_nodes (tree, nodes, ITREE_ASCENDING); + + for (int i = 0; i < size; ++i, ++nodes) + { + struct interval_node * const node = *nodes; + + if (multibyte) + { + ptrdiff_t begin = interval_node_begin (tree, node); + ptrdiff_t end = interval_node_end (tree, node); + + /* This models the behavior of markers. (The behavior of + text-intervals differs slightly.) */ + while (begin < Z_BYTE + && !CHAR_HEAD_P (FETCH_BYTE (begin))) + begin++; + while (end < Z_BYTE + && !CHAR_HEAD_P (FETCH_BYTE (end))) + end++; + interval_node_set_region (tree, node, BYTE_TO_CHAR (begin), + BYTE_TO_CHAR (end)); + } + else + { + interval_node_set_region (tree, node, CHAR_TO_BYTE (node->begin), + CHAR_TO_BYTE (node->end)); + } + } + SAFE_FREE (); +} + /* Reinitialize everything about a buffer except its name and contents and local variables. If called on an already-initialized buffer, the list of overlays @@ -2456,6 +2506,7 @@ current buffer is cleared. */) /* Do this first, so it can use CHAR_TO_BYTE to calculate the old correspondences. */ set_intervals_multibyte (0); + set_overlays_multibyte (0); bset_enable_multibyte_characters (current_buffer, Qnil); @@ -2648,6 +2699,7 @@ current buffer is cleared. */) /* Do this last, so it can calculate the new correspondences between chars and bytes. */ set_intervals_multibyte (1); + set_overlays_multibyte (1); } if (!EQ (old_undo, Qt)) commit 3945c019ecc8bea75bf868bb388fa56881cb82b6 Author: Andreas Politz Date: Fri Oct 6 11:59:54 2017 +0200 Add a function collecting all interval nodes * src/itree.c (interval_tree_nodes): New function diff --git a/src/itree.c b/src/itree.c index 785c83ab79..f43189cabe 100644 --- a/src/itree.c +++ b/src/itree.c @@ -404,6 +404,28 @@ interval_tree_validate (struct interval_tree *tree, struct interval_node *node) return node; } +/* Fill memory pointed at via NODES with all nodes of TREE in the + given ORDER. + + The size of NODES must be sufficiently large. + */ + +void +interval_tree_nodes (struct interval_tree *tree, + struct interval_node **nodes, + enum interval_tree_order order) +{ + struct interval_node *node; + + interval_tree_iter_start (tree, PTRDIFF_MIN, PTRDIFF_MAX, order); + while ((node = interval_tree_iter_next (tree))) + { + *nodes = node; + ++nodes; + } + interval_tree_iter_finish (tree); +} + /* Start a generator iterating all intervals in [BEGIN,END) in the given ORDER. Only one iterator per tree can be running at any time. diff --git a/src/itree.h b/src/itree.h index d35c5afc24..d685a69eb7 100644 --- a/src/itree.h +++ b/src/itree.h @@ -85,4 +85,5 @@ void interval_tree_iter_finish(struct interval_tree *); struct interval_node *interval_tree_iter_next(struct interval_tree *); void interval_tree_insert_gap(struct interval_tree *, ptrdiff_t, ptrdiff_t); void interval_tree_delete_gap(struct interval_tree *, ptrdiff_t, ptrdiff_t); +void interval_tree_nodes (struct interval_tree *tree, struct interval_node **nodes, enum interval_tree_order order); #endif commit bb9ab6f5c06e8589ac6635e9700dd20e0c925625 Author: Andreas Politz Date: Thu Oct 5 21:57:17 2017 +0200 Fix assignment of offset to newly inserted nodes. * src/itree.c (interval_tree_insert): Fix offset. diff --git a/src/itree.c b/src/itree.c index 5df2d8d1cc..785c83ab79 100644 --- a/src/itree.c +++ b/src/itree.c @@ -309,7 +309,9 @@ interval_tree_insert (struct interval_tree *tree, struct interval_node *node) node->left = &tree->nil; node->right = &tree->nil; node->color = ITREE_RED; - node->offset = offset; + node->offset = 0; + node->begin -= offset; + node->end -= offset; node->limit = node->end; node->otick = tree->otick - 1; commit 828552916eb4e14a8f3dcf83b78687f9f34b1e4b Author: Andreas Politz Date: Thu Oct 5 21:55:43 2017 +0200 Add offsets when inspecting a node's children's values *src/itree.c (interval_tree_insert_gap): Add offset. diff --git a/src/itree.c b/src/itree.c index 0c10100eef..5df2d8d1cc 100644 --- a/src/itree.c +++ b/src/itree.c @@ -519,7 +519,8 @@ interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l else interval_stack_push (stack, node->right); } - if (node->left != &tree->nil && pos <= node->left->limit) + if (node->left != &tree->nil + && pos <= node->left->limit + node->left->offset) interval_stack_push (stack, node->left); /* node->begin == pos implies no front-advance. */ @@ -578,7 +579,8 @@ interval_tree_delete_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t l else interval_stack_push (stack, node->right); } - if (node->left != &tree->nil && pos <= node->left->limit) + if (node->left != &tree->nil + && pos <= node->left->limit + node->left->offset) interval_stack_push (stack, node->left); if (pos < node->begin) commit 342fc823a6622f25c79baa8535cb9ea939ef666f Author: Andreas Politz Date: Thu Oct 5 20:40:31 2017 +0200 Add test exposing overlay bug * test/src/buffer-tests.el (overlay-autogenerated-test-64): New test. diff --git a/test/src/buffer-tests.el b/test/src/buffer-tests.el index e3bc2c5dfc..c2c9d06033 100644 --- a/test/src/buffer-tests.el +++ b/test/src/buffer-tests.el @@ -7577,4 +7577,105 @@ with parameters from the *Messages* buffer modification." (25 . 32) (25 . 84)))))) +(ert-deftest overlay-autogenerated-test-64 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 31 10 nil nil nil) + (make-overlay 17 58 nil nil t) + (make-overlay 20 21 nil t nil) + (make-overlay 3 47 nil t t) + (make-overlay 47 43 nil t t) + (make-overlay 54 8 nil nil t) + (make-overlay 51 26 nil t nil) + (make-overlay 60 14 nil t nil) + (make-overlay 38 6 nil nil t) + (make-overlay 41 9 nil nil nil) + (make-overlay 44 38 nil nil t) + (make-overlay 55 48 nil nil t) + (make-overlay 10 41 nil nil t) + (make-overlay 35 49 nil t nil) + (make-overlay 50 46 nil nil nil) + (make-overlay 28 28 nil t nil) + (goto-char 59) + (delete-char 3) + (goto-char 28) + (widen) + (narrow-to-region 13 7) + (goto-char 11) + (insert ".") + (goto-char 9) + (delete-char 3) + (goto-char 8) + (delete-char 0) + (goto-char 7) + (insert ".............") + (goto-char 9) + (insert "..........") + (goto-char 22) + (delete-char 1) + (goto-char 31) + (delete-char 2) + (goto-char 22) + (insert ".........") + (goto-char 33) + (delete-char 1) + (goto-char 29) + (widen) + (narrow-to-region 59 51) + (goto-char 52) + (insert ".........") + (goto-char 53) + (insert "........") + (goto-char 53) + (delete-char 4) + (goto-char 54) + (insert "........") + (goto-char 53) + (insert "....") + (goto-char 75) + (widen) + (goto-char 70) + (delete-char 2) + (goto-char 108) + (delete-char 1) + (goto-char 80) + (widen) + (goto-char 70) + (widen) + (narrow-to-region 49 63) + (goto-char 49) + (insert "...") + (goto-char 66) + (delete-char 0) + (goto-char 63) + (delete-char 3) + (goto-char 59) + (insert "..........") + (goto-char 56) + (delete-char 6) + (goto-char 60) + (insert ".........") + (goto-char 62) + (widen) + (goto-char 58) + (insert ".............") + (goto-char 105) + (widen) + (narrow-to-region 94 109) + (goto-char 103) + (insert "............") + (should + (equal + (test-overlay-regions) + '((3 . 134) + (6 . 125) + (38 . 141) + (39 . 118) + (39 . 128) + (39 . 128) + (40 . 146) + (43 . 145) + (101 . 138) + (103 . 103)))))) + ;;; buffer-tests.el ends here commit 7c0824a1d04d660c86c78143d5485af174ee0c6b Author: Andreas Politz Date: Thu Oct 5 19:24:55 2017 +0200 Add test regarding overlay and buffer encoding * test/src/buffer-tests.el (test-overlay-multibyte-transition-1): New test exposing a bug regarding overlays when changing the multibyteness of a buffer. diff --git a/test/src/buffer-tests.el b/test/src/buffer-tests.el index 71f8705c6f..e3bc2c5dfc 100644 --- a/test/src/buffer-tests.el +++ b/test/src/buffer-tests.el @@ -1212,6 +1212,14 @@ with parameters from the *Messages* buffer modification." (54 . 135) (68 . 99)))))) +(ert-deftest test-overlay-multibyte-transition-1 () + (with-temp-buffer + (set-buffer-multibyte t) + (insert "Ä") + (let ((ov (make-overlay (point-min) (point-max)))) + (set-buffer-multibyte nil) + (should (eq (overlay-end ov) (point-max)))))) + ;; +===================================================================================+ ;; | Autogenerated insert/delete/narrow tests commit 8d7bdfa3fca076b34aaf86548d3243bee11872ad Author: Andreas Politz Date: Tue Feb 7 17:56:50 2017 +0100 Provide a new tree data-structure for overlays. * src/itree.c (interval_generator_narrow, interval_generator_next) (interval_node_init, interval_node_begin) (interval_node_end, interval_node_set_region) (interval_tree_create, interval_tree_clear) (interval_tree_init, interval_tree_destroy) (interval_tree_size, interval_tree_insert) (interval_tree_contains, interval_tree_remove) (interval_tree_validate, interval_tree_iter_start) (interval_tree_iter_finish, interval_tree_iter_next) (interval_tree_iter_ensure_space, interval_tree_max_height) (interval_tree_insert_gap, interval_tree_delete_gap) (interval_generator_create, interval_generator_reset) (interval_generator_ensure_space, interval_node_intersects) (interval_generator_next, interval_generator_narrow) (interval_generator_destroy, interval_stack_create) (interval_stack_destroy, interval_stack_clear) (interval_stack_ensure_space, interval_stack_push) (interval_stack_push_flagged, interval_stack_pop) (interval_tree_update_limit, interval_tree_inherit_offset) (interval_tree_propagate_limit, interval_tree_rotate_left) (interval_tree_rotate_right, interval_tree_insert_fix) (interval_tree_remove_fix, interval_tree_transplant) (interval_tree_subtree_min): New file and new functions. * src/itree.h: New file. * configure.ac: Create Makefile for manual overlay tests. * src/Makefile.in: Add itree.o target. * src/alloc.c (build_overlay, mark_overlay, mark_buffer) (sweep_misc, sweep_buffers): Adapt to new tree data-structure. * src/buffer.c (overlays_in, overlays_at): Remove unused arguments prev_ptr and change_req, adapt to new data-structure and reuse code. (copy_overlays, drop_overlays, delete_all_overlays) (reset_buffer, kill-buffer, buffer-swap-text, next_overlay_change) (previous_overlay_change, mouse_face_overlay_overlaps) (disable_line_numbers_overlay_at_eob, overlay_touches_p) (overlay_strings, adjust_overlays_for_insert) (adjust_overlays_for_delete, overlayp, make-overlay, move-overlay) (delete-overlay, overlay-start, overlay-end, overlay-buffer) (overlay-properties, overlays-at, overlays-in) (next-overlay-change, previous-overlay-change, overlay-put) (overlay-get, report_overlay_modification, evaporate_overlays) (init_buffer_once): Adapt to changes and tree data-structure. (overlay-lists, overlay-recenter): Funtions are now obsolete, but kept anyway. (set_buffer_overlays_before, set_buffer_overlays_after) (recenter_overlay_lists,fix_start_end_in_overlays,fix_overlays_before) (unchain_overlay,): Removed functions of the old list data-structure. (swap_buffer_overlays, make_sortvec_item): New functions. (sort_overlays): Adapt to changes and tree data-structure. (sortvec): Moved to buffer.h . (make_lispy_interval_node, overlay_tree, overlay-tree) [ITREE_DEBUG]: New debugging functions. * src/buffer.h (overlays_before, overlays_after): Removed struct member of the list data-structure. (overlays): Added tree struct member. (sortvec): Moved here from buffer.c . (GET_OVERLAYS_AT): Adapt to changes. (set_buffer_intervals, OVERLAY_START, OVERLAY_END, OVERLAY_PLIST): Adapt to tree data-structure. (OVERLAY_POSITION): Removed macro of the list data-structure. (OVERLAY_REAR_ADVANCE_P, OVERLAY_FRONT_ADVANCE_P): New macros. (overlay_start, overlay_end) (set_overlay_region, maybe_alloc_buffer_overlays) (free_buffer_overlays, add_buffer_overlay) (remove_buffer_overlay, buffer_overlay_iter_start) (buffer_overlay_iter_next, buffer_overlay_iter_finish) (buffer_overlay_iter_narrow): New functions. (compare_overlays, make_sortvec_item): Export functions. * src/editfns.c (overlays_around): Reuse overlays_in. (get-pos-property): Adapt to tree data-structure. (transpose-regions): Remove call to deleted function. * src/fileio.c: (insert-file-contents): Remove references to deleted struct member. * src/fns.c (internal_equal): Adapt to tree data-structure. * src/indent.c (check_display_width): Adapt to tree data-structure. (skip_invisible): Remove call to deleted function. * src/insdel.c (adjust_markers_for_insert): Remove calls to deleted functions. * src/intervals.c (adjust_for_invis_intang): Adapt to tree data-structure. * src/keyboard.c (adjust_point_for_property): Adapt to tree data-structure. * src/lisp.h (Lisp_Overlay): Modified struct layout. * src/print.c (temp_output_buffer_setup, print_object): Adapt to tree data-structure. * src/textprop.c (get_char_property_and_overlay): Adapt to tree data-structure. Take advantage of the new data-structure. * src/window.h (overlay_matches_window): New function. * src/xdisp.h (next_overlay_change): Removed function. Use next-overlay-change, which does not use xmalloc anymore. (handle_single_display_spec, load_overlay_strings) (back_to_previous_visible_line_start, note_mouse_highlight): Adapt to tree data-structure. (move_it_to, display_line): Remove calls to deleted functions. * src/xfaces.c (face_at_buffer_position): Adapt to changes and tree data-structure. * test/src/buffer-tests.el: Many tests regarding overlays added. * test/manual/noverlay/itree-tests.c: New file with tests of the tree data-structure on the C level. * test/manual/noverlay/Makefile.in: New file. * test/manual/noverlay/check-sanitize.sh: New file. * test/manual/noverlay/emacs-compat.h: New file. * test/manual/noverlay/.gitignore: New file. * test/manual/noverlay/overlay-perf.el: New file providing performance tests. * test/manual/noverlay/many-errors.h: New file. diff --git a/configure.ac b/configure.ac index eba95e2fb8..76cf5570b5 100644 --- a/configure.ac +++ b/configure.ac @@ -5495,6 +5495,7 @@ if test -f "$srcdir/$opt_makefile.in"; then dnl Again, it's best not to use a variable. Though you can add dnl ", [], [opt_makefile='$opt_makefile']" and it should work. AC_CONFIG_FILES([test/Makefile]) + AC_CONFIG_FILES([test/manual/noverlay/Makefile]) fi diff --git a/src/Makefile.in b/src/Makefile.in index 9a8c9c85f0..8a8df03e49 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -395,6 +395,7 @@ base_obj = dispnew.o frame.o scroll.o xdisp.o menu.o $(XMENU_OBJ) window.o \ $(XWIDGETS_OBJ) \ profiler.o decompress.o \ thread.o systhread.o \ + itree.o \ $(if $(HYBRID_MALLOC),sheap.o) \ $(MSDOS_OBJ) $(MSDOS_X_OBJ) $(NS_OBJ) $(CYGWIN_OBJ) $(FONT_OBJ) \ $(W32_OBJ) $(WINDOW_SYSTEM_OBJ) $(XGSELOBJ) diff --git a/src/alloc.c b/src/alloc.c index 2e6399e7f8..15a6fc43b7 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -43,6 +43,7 @@ along with GNU Emacs. If not, see . */ #include "frame.h" #include "blockinput.h" #include "termhooks.h" /* For struct terminal. */ +#include "itree.h" #ifdef HAVE_WINDOW_SYSTEM #include TERM_HEADER #endif /* HAVE_WINDOW_SYSTEM */ @@ -3835,16 +3836,19 @@ free_save_value (Lisp_Object save) /* Return a Lisp_Misc_Overlay object with specified START, END and PLIST. */ Lisp_Object -build_overlay (Lisp_Object start, Lisp_Object end, Lisp_Object plist) +build_overlay (ptrdiff_t begin, ptrdiff_t end, + bool front_advance, bool rear_advance, + Lisp_Object plist) { - register Lisp_Object overlay; + Lisp_Object ov = allocate_misc (Lisp_Misc_Overlay); + struct interval_node *node = xmalloc (sizeof (*node)); - overlay = allocate_misc (Lisp_Misc_Overlay); - OVERLAY_START (overlay) = start; - OVERLAY_END (overlay) = end; - set_overlay_plist (overlay, plist); - XOVERLAY (overlay)->next = NULL; - return overlay; + interval_node_init (node, begin, end, front_advance, + rear_advance, ov); + XOVERLAY (ov)->interval = node; + XOVERLAY (ov)->buffer = NULL; + set_overlay_plist (ov, plist); + return ov; } DEFUN ("make-marker", Fmake_marker, Smake_marker, 0, 0, 0, @@ -6280,16 +6284,10 @@ mark_compiled (struct Lisp_Vector *ptr) /* Mark the chain of overlays starting at PTR. */ static void -mark_overlay (struct Lisp_Overlay *ptr) +mark_overlay (struct Lisp_Overlay *ov) { - for (; ptr && !ptr->gcmarkbit; ptr = ptr->next) - { - ptr->gcmarkbit = 1; - /* These two are always markers and can be marked fast. */ - XMARKER (ptr->start)->gcmarkbit = 1; - XMARKER (ptr->end)->gcmarkbit = 1; - mark_object (ptr->plist); - } + ov->gcmarkbit = 1; + mark_object (ov->plist); } /* Mark Lisp_Objects and special pointers in BUFFER. */ @@ -6308,8 +6306,15 @@ mark_buffer (struct buffer *buffer) a special way just before the sweep phase, and after stripping some of its elements that are not needed any more. */ - mark_overlay (buffer->overlays_before); - mark_overlay (buffer->overlays_after); + if (buffer->overlays) + { + struct interval_node *node; + buffer_overlay_iter_start (buffer, PTRDIFF_MIN, PTRDIFF_MAX, ITREE_ASCENDING); + + while ((node = buffer_overlay_iter_next (buffer))) + mark_overlay (XOVERLAY (node->data)); + buffer_overlay_iter_finish (buffer); + } /* If this is an indirect buffer, mark its base buffer. */ if (buffer->base_buffer && !VECTOR_MARKED_P (buffer->base_buffer)) @@ -7090,6 +7095,11 @@ sweep_misc (void) unchain_marker (&mblk->markers[i].m.u_marker); else if (mblk->markers[i].m.u_any.type == Lisp_Misc_Finalizer) unchain_finalizer (&mblk->markers[i].m.u_finalizer); + else if (mblk->markers[i].m.u_any.type == Lisp_Misc_Overlay) + { + xfree (mblk->markers[i].m.u_overlay.interval); + mblk->markers[i].m.u_overlay.interval = NULL; + } #ifdef HAVE_MODULES else if (mblk->markers[i].m.u_any.type == Lisp_Misc_User_Ptr) { @@ -7145,6 +7155,7 @@ sweep_buffers (void) if (!VECTOR_MARKED_P (buffer)) { *bprev = buffer->next; + free_buffer_overlays (buffer); lisp_free (buffer); } else diff --git a/src/buffer.c b/src/buffer.c index 76670b8954..21c040ad0e 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -44,6 +44,7 @@ along with GNU Emacs. If not, see . */ #include "keymap.h" #include "frame.h" #include "xwidget.h" +#include "itree.h" #ifdef WINDOWSNT #include "w32heap.h" /* for mmap_* */ @@ -120,7 +121,7 @@ static Lisp_Object QSFundamental; /* A string "Fundamental". */ static void alloc_buffer_text (struct buffer *, ptrdiff_t); static void free_buffer_text (struct buffer *b); -static struct Lisp_Overlay * copy_overlays (struct buffer *, struct Lisp_Overlay *); +static void copy_overlays (struct buffer *, struct buffer *); static void modify_overlay (struct buffer *, ptrdiff_t, ptrdiff_t); static Lisp_Object buffer_lisp_local_variables (struct buffer *, bool); @@ -600,51 +601,30 @@ even if it is dead. The return value is never nil. */) } -/* Return a list of overlays which is a copy of the overlay list - LIST, but for buffer B. */ +/* Copy overlays of buffer FROM to buffer TO. */ -static struct Lisp_Overlay * -copy_overlays (struct buffer *b, struct Lisp_Overlay *list) +static void +copy_overlays (struct buffer *from, struct buffer *to) { - struct Lisp_Overlay *result = NULL, *tail = NULL; + eassert (to && ! to->overlays); - for (; list; list = list->next) - { - Lisp_Object overlay, start, end; - struct Lisp_Marker *m; - - eassert (MARKERP (list->start)); - m = XMARKER (list->start); - start = build_marker (b, m->charpos, m->bytepos); - XMARKER (start)->insertion_type = m->insertion_type; - - eassert (MARKERP (list->end)); - m = XMARKER (list->end); - end = build_marker (b, m->charpos, m->bytepos); - XMARKER (end)->insertion_type = m->insertion_type; - - overlay = build_overlay (start, end, Fcopy_sequence (list->plist)); - if (tail) - tail = tail->next = XOVERLAY (overlay); - else - result = tail = XOVERLAY (overlay); - } - - return result; -} + struct interval_node *node; -/* Set an appropriate overlay of B. */ + if (! from->overlays) + return; -static void -set_buffer_overlays_before (struct buffer *b, struct Lisp_Overlay *o) -{ - b->overlays_before = o; -} + buffer_overlay_iter_start (from, PTRDIFF_MIN, PTRDIFF_MAX, ITREE_ASCENDING); -static void -set_buffer_overlays_after (struct buffer *b, struct Lisp_Overlay *o) -{ - b->overlays_after = o; + while ((node = buffer_overlay_iter_next (from))) + { + Lisp_Object ov = node->data; + Lisp_Object copy = build_overlay (node->begin, node->end, + node->front_advance, + node->rear_advance, + Fcopy_sequence (OVERLAY_PLIST (ov))); + add_buffer_overlay (to, XOVERLAY (copy)); + } + buffer_overlay_iter_finish (from); } /* Clone per-buffer values of buffer FROM. @@ -681,8 +661,7 @@ clone_per_buffer_values (struct buffer *from, struct buffer *to) memcpy (to->local_flags, from->local_flags, sizeof to->local_flags); - set_buffer_overlays_before (to, copy_overlays (to, from->overlays_before)); - set_buffer_overlays_after (to, copy_overlays (to, from->overlays_after)); + copy_overlays (from, to); /* Get (a copy of) the alist of Lisp-level local variables of FROM and install that in TO. */ @@ -867,17 +846,16 @@ CLONE nil means the indirect buffer's state is reset to default values. */) return buf; } -/* Mark OV as no longer associated with B. */ +/* Mark OV as no longer associated with its buffer. */ static void -drop_overlay (struct buffer *b, struct Lisp_Overlay *ov) +drop_overlay (struct Lisp_Overlay *ov) { - eassert (b == XBUFFER (Fmarker_buffer (ov->start))); - modify_overlay (b, marker_position (ov->start), - marker_position (ov->end)); - unchain_marker (XMARKER (ov->start)); - unchain_marker (XMARKER (ov->end)); + if (! ov->buffer) + return; + modify_overlay (ov->buffer, overlay_start (ov), overlay_end (ov)); + remove_buffer_overlay (ov->buffer, ov); } /* Delete all overlays of B and reset its overlay lists. */ @@ -885,26 +863,20 @@ drop_overlay (struct buffer *b, struct Lisp_Overlay *ov) void delete_all_overlays (struct buffer *b) { - struct Lisp_Overlay *ov, *next; + struct interval_node *node; - /* FIXME: Since each drop_overlay will scan BUF_MARKERS to unlink its - markers, we have an unneeded O(N^2) behavior here. */ - for (ov = b->overlays_before; ov; ov = next) - { - drop_overlay (b, ov); - next = ov->next; - ov->next = NULL; - } + if (! b->overlays) + return; - for (ov = b->overlays_after; ov; ov = next) + buffer_overlay_iter_start (b, PTRDIFF_MIN, PTRDIFF_MAX, ITREE_ASCENDING); + while ((node = buffer_overlay_iter_next (b))) { - drop_overlay (b, ov); - next = ov->next; - ov->next = NULL; + modify_overlay (b, node->begin, node->end); + /* Where are the nodes freed ? --ap */ + XOVERLAY (node->data)->buffer = NULL; } - - set_buffer_overlays_before (b, NULL); - set_buffer_overlays_after (b, NULL); + buffer_overlay_iter_finish (b); + interval_tree_clear (b->overlays); } /* Reinitialize everything about a buffer except its name and contents @@ -932,9 +904,7 @@ reset_buffer (register struct buffer *b) b->auto_save_failure_time = 0; bset_auto_save_file_name (b, Qnil); bset_read_only (b, Qnil); - set_buffer_overlays_before (b, NULL); - set_buffer_overlays_after (b, NULL); - b->overlay_center = BEG; + b->overlays = NULL; bset_mark_active (b, Qnil); bset_point_before_scroll (b, Qnil); bset_file_format (b, Qnil); @@ -1843,10 +1813,8 @@ cleaning up all windows currently displaying the buffer to be killed. */) /* Perhaps we should explicitly free the interval tree here... */ } - /* Since we've unlinked the markers, the overlays can't be here any more - either. */ - b->overlays_before = NULL; - b->overlays_after = NULL; + delete_all_overlays (b); + free_buffer_overlays (b); /* Reset the local variables, so that this buffer's local values won't be protected from GC. They would be protected @@ -2254,6 +2222,31 @@ advance_to_char_boundary (ptrdiff_t byte_pos) return byte_pos; } +static void +swap_buffer_overlays (struct buffer *buffer, struct buffer *other) +{ + struct interval_node *node; + + if (buffer->overlays) + { + buffer_overlay_iter_start (buffer, PTRDIFF_MIN, PTRDIFF_MAX, ITREE_ASCENDING); + while ((node = buffer_overlay_iter_next (buffer))) + XOVERLAY (node->data)->buffer = other; + buffer_overlay_iter_finish (buffer); + } + if (other->overlays) + { + buffer_overlay_iter_start (other, PTRDIFF_MIN, PTRDIFF_MAX, ITREE_ASCENDING); + while ((node = buffer_overlay_iter_next (other))) + XOVERLAY (node->data)->buffer = buffer; + buffer_overlay_iter_finish (other); + } + /* Swap the interval trees. */ + void *tmp = buffer->overlays; + buffer->overlays = other->overlays; + other->overlays = tmp; +} + DEFUN ("buffer-swap-text", Fbuffer_swap_text, Sbuffer_swap_text, 1, 1, 0, doc: /* Swap the text between current buffer and BUFFER. @@ -2324,9 +2317,6 @@ results, see Info node `(elisp)Swapping Text'. */) swapfield (bidi_paragraph_cache, struct region_cache *); current_buffer->prevent_redisplay_optimizations_p = 1; other_buffer->prevent_redisplay_optimizations_p = 1; - swapfield (overlays_before, struct Lisp_Overlay *); - swapfield (overlays_after, struct Lisp_Overlay *); - swapfield (overlay_center, ptrdiff_t); swapfield_ (undo_list, Lisp_Object); swapfield_ (mark, Lisp_Object); swapfield_ (enable_multibyte_characters, Lisp_Object); @@ -2349,6 +2339,7 @@ results, see Info node `(elisp)Swapping Text'. */) current_buffer->text->end_unchanged = current_buffer->text->gpt; other_buffer->text->beg_unchanged = other_buffer->text->gpt; other_buffer->text->end_unchanged = other_buffer->text->gpt; + swap_buffer_overlays (current_buffer, other_buffer); { struct Lisp_Marker *m; for (m = BUF_MARKERS (current_buffer); m; m = m->next) @@ -2763,285 +2754,160 @@ swap_out_buffer_local_variables (struct buffer *b) } } } + -/* Find all the overlays in the current buffer that contain position POS. +/* Find all the overlays in the current buffer that overlap the range + [BEG, END). + + If EMPTY is true, include empty overlays in that range and also at + END, provided END denotes the position at the end of the buffer. + Return the number found, and store them in a vector in *VEC_PTR. Store in *LEN_PTR the size allocated for the vector. Store in *NEXT_PTR the next position after POS where an overlay starts, - or ZV if there are no more overlays between POS and ZV. - Store in *PREV_PTR the previous position before POS where an overlay ends, - or where an overlay starts which ends at or after POS; - or BEGV if there are no such overlays from BEGV to POS. - NEXT_PTR and/or PREV_PTR may be 0, meaning don't store that info. + or ZV if there are no more overlays. + NEXT_PTR may be 0, meaning don't store that info. *VEC_PTR and *LEN_PTR should contain a valid vector and size when this function is called. - If EXTEND, make the vector bigger if necessary. - If not, never extend the vector, - and store only as many overlays as will fit. + If EXTEND, make the vector bigger if necessary. If not, never + extend the vector, and store only as many overlays as will fit. But still return the total number of overlays. - - If CHANGE_REQ, any position written into *PREV_PTR or - *NEXT_PTR is guaranteed to be not equal to POS, unless it is the - default (BEGV or ZV). */ +*/ ptrdiff_t -overlays_at (EMACS_INT pos, bool extend, Lisp_Object **vec_ptr, - ptrdiff_t *len_ptr, - ptrdiff_t *next_ptr, ptrdiff_t *prev_ptr, bool change_req) +overlays_in (ptrdiff_t beg, ptrdiff_t end, bool extend, + Lisp_Object **vec_ptr, ptrdiff_t *len_ptr, bool empty, + ptrdiff_t *next_ptr) { - Lisp_Object overlay, start, end; - struct Lisp_Overlay *tail; ptrdiff_t idx = 0; ptrdiff_t len = *len_ptr; - Lisp_Object *vec = *vec_ptr; ptrdiff_t next = ZV; - ptrdiff_t prev = BEGV; - bool inhibit_storing = 0; - - for (tail = current_buffer->overlays_before; tail; tail = tail->next) - { - ptrdiff_t startpos, endpos; + Lisp_Object *vec = *vec_ptr; - XSETMISC (overlay, tail); + struct interval_node *node; - start = OVERLAY_START (overlay); - end = OVERLAY_END (overlay); - endpos = OVERLAY_POSITION (end); - if (endpos < pos) - { - if (prev < endpos) - prev = endpos; - break; - } - startpos = OVERLAY_POSITION (start); - /* This one ends at or after POS - so its start counts for PREV_PTR if it's before POS. */ - if (prev < startpos && startpos < pos) - prev = startpos; - if (endpos == pos) - continue; - if (startpos <= pos) - { - if (idx == len) - { - /* The supplied vector is full. - Either make it bigger, or don't store any more in it. */ - if (extend) - { - vec = xpalloc (vec, len_ptr, 1, OVERLAY_COUNT_MAX, - sizeof *vec); - *vec_ptr = vec; - len = *len_ptr; - } - else - inhibit_storing = 1; - } + if (! current_buffer->overlays) + return idx; - if (!inhibit_storing) - vec[idx] = overlay; - /* Keep counting overlays even if we can't return them all. */ - idx++; - } - else if (startpos < next) - next = startpos; - } + buffer_overlay_iter_start (current_buffer, beg, + /* Find empty OV at Z ? */ + (end >= Z && empty) ? Z + 1 : ZV, + ITREE_ASCENDING); - for (tail = current_buffer->overlays_after; tail; tail = tail->next) + while ((node = buffer_overlay_iter_next (current_buffer))) { - ptrdiff_t startpos, endpos; - - XSETMISC (overlay, tail); - - start = OVERLAY_START (overlay); - end = OVERLAY_END (overlay); - startpos = OVERLAY_POSITION (start); - if (pos < startpos) - { - if (startpos < next) - next = startpos; - break; - } - endpos = OVERLAY_POSITION (end); - if (pos < endpos) - { - if (idx == len) - { - if (extend) - { - vec = xpalloc (vec, len_ptr, 1, OVERLAY_COUNT_MAX, - sizeof *vec); - *vec_ptr = vec; - len = *len_ptr; - } - else - inhibit_storing = 1; - } + if (node->begin > end) + { + next = min (next, node->begin); + break; + } + else if (node->begin == end) + { + next = node->begin; + if ((! empty || end < Z) && beg < end) + break; + } - if (!inhibit_storing) - vec[idx] = overlay; - idx++; + if (! empty && node->begin == node->end) + continue; - if (startpos < pos && startpos > prev) - prev = startpos; - } - else if (endpos < pos && endpos > prev) - prev = endpos; - else if (endpos == pos && startpos > prev - && (!change_req || startpos < pos)) - prev = startpos; + if (extend && idx == len) + { + vec = xpalloc (vec, len_ptr, 1, OVERLAY_COUNT_MAX, + sizeof *vec); + *vec_ptr = vec; + len = *len_ptr; + } + if (idx < len) + vec[idx] = node->data; + /* Keep counting overlays even if we can't return them all. */ + idx++; } - + buffer_overlay_iter_finish (current_buffer); if (next_ptr) - *next_ptr = next; - if (prev_ptr) - *prev_ptr = prev; + *next_ptr = next ? next : ZV; + return idx; } - -/* Find all the overlays in the current buffer that overlap the range - BEG-END, or are empty at BEG, or are empty at END provided END - denotes the position at the end of the current buffer. - Return the number found, and store them in a vector in *VEC_PTR. - Store in *LEN_PTR the size allocated for the vector. - Store in *NEXT_PTR the next position after POS where an overlay starts, - or ZV if there are no more overlays. - Store in *PREV_PTR the previous position before POS where an overlay ends, - or BEGV if there are no previous overlays. - NEXT_PTR and/or PREV_PTR may be 0, meaning don't store that info. +/* Find all non-empty overlays in the current buffer that contain + position POS. - *VEC_PTR and *LEN_PTR should contain a valid vector and size - when this function is called. + See overlays_in for the meaning of the arguments. + */ - If EXTEND, make the vector bigger if necessary. - If not, never extend the vector, - and store only as many overlays as will fit. - But still return the total number of overlays. */ +ptrdiff_t +overlays_at (ptrdiff_t pos, bool extend, + Lisp_Object **vec_ptr, ptrdiff_t *len_ptr, + ptrdiff_t *next_ptr) +{ + return overlays_in (pos, pos + 1, extend, vec_ptr, len_ptr, false, next_ptr); +} -static ptrdiff_t -overlays_in (EMACS_INT beg, EMACS_INT end, bool extend, - Lisp_Object **vec_ptr, ptrdiff_t *len_ptr, - ptrdiff_t *next_ptr, ptrdiff_t *prev_ptr) +ptrdiff_t +next_overlay_change (ptrdiff_t pos) { - Lisp_Object overlay, ostart, oend; - struct Lisp_Overlay *tail; - ptrdiff_t idx = 0; - ptrdiff_t len = *len_ptr; - Lisp_Object *vec = *vec_ptr; ptrdiff_t next = ZV; - ptrdiff_t prev = BEGV; - bool inhibit_storing = 0; - bool end_is_Z = end == Z; + struct interval_node *node; - for (tail = current_buffer->overlays_before; tail; tail = tail->next) - { - ptrdiff_t startpos, endpos; + if (! current_buffer->overlays) + return next; - XSETMISC (overlay, tail); - - ostart = OVERLAY_START (overlay); - oend = OVERLAY_END (overlay); - endpos = OVERLAY_POSITION (oend); - if (endpos < beg) - { - if (prev < endpos) - prev = endpos; - break; - } - startpos = OVERLAY_POSITION (ostart); - /* Count an interval if it overlaps the range, is empty at the - start of the range, or is empty at END provided END denotes the - end of the buffer. */ - if ((beg < endpos && startpos < end) - || (startpos == endpos - && (beg == endpos || (end_is_Z && endpos == end)))) - { - if (idx == len) - { - /* The supplied vector is full. - Either make it bigger, or don't store any more in it. */ - if (extend) - { - vec = xpalloc (vec, len_ptr, 1, OVERLAY_COUNT_MAX, - sizeof *vec); - *vec_ptr = vec; - len = *len_ptr; - } - else - inhibit_storing = 1; - } - - if (!inhibit_storing) - vec[idx] = overlay; - /* Keep counting overlays even if we can't return them all. */ - idx++; - } - else if (startpos < next) - next = startpos; + buffer_overlay_iter_start (current_buffer, pos, ZV, ITREE_ASCENDING); + while ((node = buffer_overlay_iter_next (current_buffer))) + { + if (node->begin > pos) + { + /* If we reach this branch, node->begin must be the least upper bound + of pos, because the search is limited to [pos,next) . */ + eassert (node->begin < next); + next = node->begin; + break; + } + else if (node->begin < node->end && node->end < next) + { + next = node->end; + buffer_overlay_iter_narrow (current_buffer, pos, next); + } } + buffer_overlay_iter_finish (current_buffer); - for (tail = current_buffer->overlays_after; tail; tail = tail->next) - { - ptrdiff_t startpos, endpos; + return next; +} - XSETMISC (overlay, tail); +ptrdiff_t +previous_overlay_change (ptrdiff_t pos) +{ + struct interval_node *node; + ptrdiff_t prev = BEGV; - ostart = OVERLAY_START (overlay); - oend = OVERLAY_END (overlay); - startpos = OVERLAY_POSITION (ostart); - if (end < startpos) - { - if (startpos < next) - next = startpos; - break; - } - endpos = OVERLAY_POSITION (oend); - /* Count an interval if it overlaps the range, is empty at the - start of the range, or is empty at END provided END denotes the - end of the buffer. */ - if ((beg < endpos && startpos < end) - || (startpos == endpos - && (beg == endpos || (end_is_Z && endpos == end)))) - { - if (idx == len) - { - if (extend) - { - vec = xpalloc (vec, len_ptr, 1, OVERLAY_COUNT_MAX, - sizeof *vec); - *vec_ptr = vec; - len = *len_ptr; - } - else - inhibit_storing = 1; - } + if (! current_buffer->overlays) + return prev; - if (!inhibit_storing) - vec[idx] = overlay; - idx++; - } - else if (endpos < beg && endpos > prev) - prev = endpos; + buffer_overlay_iter_start (current_buffer, BEGV, pos, ITREE_DESCENDING); + while ((node = buffer_overlay_iter_next (current_buffer))) + { + if (node->end < pos) + prev = node->end; + else + prev = max (prev, node->begin); + buffer_overlay_iter_narrow (current_buffer, prev, pos); } + buffer_overlay_iter_finish (current_buffer); - if (next_ptr) - *next_ptr = next; - if (prev_ptr) - *prev_ptr = prev; - return idx; + return prev; } - /* Return true if there exists an overlay with a non-nil `mouse-face' property overlapping OVERLAY. */ bool mouse_face_overlay_overlaps (Lisp_Object overlay) { - ptrdiff_t start = OVERLAY_POSITION (OVERLAY_START (overlay)); - ptrdiff_t end = OVERLAY_POSITION (OVERLAY_END (overlay)); + ptrdiff_t start = OVERLAY_START (overlay); + ptrdiff_t end = OVERLAY_END (overlay); ptrdiff_t n, i, size; Lisp_Object *v, tem; Lisp_Object vbuf[10]; @@ -3049,11 +2915,11 @@ mouse_face_overlay_overlaps (Lisp_Object overlay) size = ARRAYELTS (vbuf); v = vbuf; - n = overlays_in (start, end, 0, &v, &size, NULL, NULL); + n = overlays_in (start, end, 0, &v, &size, true, NULL); if (n > size) { SAFE_NALLOCA (v, 1, n); - overlays_in (start, end, 0, &v, &n, NULL, NULL); + overlays_in (start, end, 0, &v, &n, true, NULL); } for (i = 0; i < n; ++i) @@ -3095,52 +2961,34 @@ disable_line_numbers_overlay_at_eob (void) } -/* Fast function to just test if we're at an overlay boundary. */ +/* Fast function to just test if we're at an overlay boundary. + + Returns true if some overlay starts or ends (or both) at POS, +*/ bool overlay_touches_p (ptrdiff_t pos) { - Lisp_Object overlay; - struct Lisp_Overlay *tail; - - for (tail = current_buffer->overlays_before; tail; tail = tail->next) - { - ptrdiff_t endpos; + struct interval_node *node; + bool result = false; - XSETMISC (overlay ,tail); - eassert (OVERLAYP (overlay)); + if (! current_buffer->overlays) + return false; - endpos = OVERLAY_POSITION (OVERLAY_END (overlay)); - if (endpos < pos) - break; - if (endpos == pos || OVERLAY_POSITION (OVERLAY_START (overlay)) == pos) - return 1; - } + /* We need to find overlays ending in pos, as well as empty ones at + pos. */ + buffer_overlay_iter_start (current_buffer, + pos - 1, pos + 1, ITREE_DESCENDING); - for (tail = current_buffer->overlays_after; tail; tail = tail->next) - { - ptrdiff_t startpos; + while (! result && (node = buffer_overlay_iter_next (current_buffer))) + result = (node->begin == pos || node->end == pos); - XSETMISC (overlay, tail); - eassert (OVERLAYP (overlay)); + buffer_overlay_iter_finish (current_buffer); - startpos = OVERLAY_POSITION (OVERLAY_START (overlay)); - if (pos < startpos) - break; - if (startpos == pos || OVERLAY_POSITION (OVERLAY_END (overlay)) == pos) - return 1; - } - return 0; + return result; } - -struct sortvec -{ - Lisp_Object overlay; - ptrdiff_t beg, end; - EMACS_INT priority; - EMACS_INT spriority; /* Secondary priority. */ -}; -static int + +int compare_overlays (const void *v1, const void *v2) { const struct sortvec *s1 = v1; @@ -3169,6 +3017,33 @@ compare_overlays (const void *v1, const void *v2) return XLI (s1->overlay) < XLI (s2->overlay) ? -1 : 1; } +void +make_sortvec_item (struct sortvec *item, Lisp_Object overlay) +{ + Lisp_Object tem; + /* This overlay is good and counts: put it into sortvec. */ + item->overlay = overlay; + item->beg = OVERLAY_START (overlay); + item->end = OVERLAY_END (overlay); + tem = Foverlay_get (overlay, Qpriority); + if (NILP (tem)) + { + item->priority = 0; + item->spriority = 0; + } + else if (INTEGERP (tem)) + { + item->priority = XINT (tem); + item->spriority = 0; + } + else if (CONSP (tem)) + { + Lisp_Object car = XCAR (tem); + Lisp_Object cdr = XCDR (tem); + item->priority = INTEGERP (car) ? XINT (car) : 0; + item->spriority = INTEGERP (cdr) ? XINT (cdr) : 0; + } +} /* Sort an array of overlays by priority. The array is modified in place. The return value is the new size; this may be smaller than the original size if some of the overlays were invalid or were window-specific. */ @@ -3185,47 +3060,18 @@ sort_overlays (Lisp_Object *overlay_vec, ptrdiff_t noverlays, struct window *w) for (i = 0, j = 0; i < noverlays; i++) { - Lisp_Object tem; Lisp_Object overlay; overlay = overlay_vec[i]; if (OVERLAYP (overlay) - && OVERLAY_POSITION (OVERLAY_START (overlay)) > 0 - && OVERLAY_POSITION (OVERLAY_END (overlay)) > 0) + && OVERLAY_START (overlay) > 0 + && OVERLAY_END (overlay) > 0) { - /* If we're interested in a specific window, then ignore - overlays that are limited to some other window. */ - if (w) - { - Lisp_Object window; - - window = Foverlay_get (overlay, Qwindow); - if (WINDOWP (window) && XWINDOW (window) != w) - continue; - } - - /* This overlay is good and counts: put it into sortvec. */ - sortvec[j].overlay = overlay; - sortvec[j].beg = OVERLAY_POSITION (OVERLAY_START (overlay)); - sortvec[j].end = OVERLAY_POSITION (OVERLAY_END (overlay)); - tem = Foverlay_get (overlay, Qpriority); - if (NILP (tem)) - { - sortvec[j].priority = 0; - sortvec[j].spriority = 0; - } - else if (INTEGERP (tem)) - { - sortvec[j].priority = XINT (tem); - sortvec[j].spriority = 0; - } - else if (CONSP (tem)) - { - Lisp_Object car = XCAR (tem); - Lisp_Object cdr = XCDR (tem); - sortvec[j].priority = INTEGERP (car) ? XINT (car) : 0; - sortvec[j].spriority = INTEGERP (cdr) ? XINT (cdr) : 0; - } + /* If we're interested in a specific window, then ignore + overlays that are limited to some other window. */ + if (w && ! overlay_matches_window (w, overlay)) + continue; + make_sortvec_item (sortvec + j, overlay); j++; } } @@ -3340,68 +3186,44 @@ ptrdiff_t overlay_strings (ptrdiff_t pos, struct window *w, unsigned char **pstr) { Lisp_Object overlay, window, str; - struct Lisp_Overlay *ov; - ptrdiff_t startpos, endpos; + ptrdiff_t obegin, oend; bool multibyte = ! NILP (BVAR (current_buffer, enable_multibyte_characters)); + struct interval_node *node; overlay_heads.used = overlay_heads.bytes = 0; overlay_tails.used = overlay_tails.bytes = 0; - for (ov = current_buffer->overlays_before; ov; ov = ov->next) + + buffer_overlay_iter_start (current_buffer, + pos - 1, pos + 1, ITREE_DESCENDING); + while ((node = buffer_overlay_iter_next (current_buffer))) { - XSETMISC (overlay, ov); + overlay = node->data; eassert (OVERLAYP (overlay)); - startpos = OVERLAY_POSITION (OVERLAY_START (overlay)); - endpos = OVERLAY_POSITION (OVERLAY_END (overlay)); - if (endpos < pos) - break; - if (endpos != pos && startpos != pos) + obegin = node->begin; + oend = node->end; + + if (oend != pos && obegin != pos) continue; window = Foverlay_get (overlay, Qwindow); if (WINDOWP (window) && XWINDOW (window) != w) continue; - if (startpos == pos + if (obegin == pos && (str = Foverlay_get (overlay, Qbefore_string), STRINGP (str))) record_overlay_string (&overlay_heads, str, - (startpos == endpos + (obegin == oend ? Foverlay_get (overlay, Qafter_string) : Qnil), Foverlay_get (overlay, Qpriority), - endpos - startpos); - else if (endpos == pos + oend - obegin); + else if (oend == pos && (str = Foverlay_get (overlay, Qafter_string), STRINGP (str))) record_overlay_string (&overlay_tails, str, Qnil, Foverlay_get (overlay, Qpriority), - endpos - startpos); + oend - obegin); } - for (ov = current_buffer->overlays_after; ov; ov = ov->next) - { - XSETMISC (overlay, ov); - eassert (OVERLAYP (overlay)); + buffer_overlay_iter_finish (current_buffer); - startpos = OVERLAY_POSITION (OVERLAY_START (overlay)); - endpos = OVERLAY_POSITION (OVERLAY_END (overlay)); - if (startpos > pos) - break; - if (endpos != pos && startpos != pos) - continue; - window = Foverlay_get (overlay, Qwindow); - if (WINDOWP (window) && XWINDOW (window) != w) - continue; - if (startpos == pos - && (str = Foverlay_get (overlay, Qbefore_string), STRINGP (str))) - record_overlay_string (&overlay_heads, str, - (startpos == endpos - ? Foverlay_get (overlay, Qafter_string) - : Qnil), - Foverlay_get (overlay, Qpriority), - endpos - startpos); - else if (endpos == pos - && (str = Foverlay_get (overlay, Qafter_string), STRINGP (str))) - record_overlay_string (&overlay_tails, str, Qnil, - Foverlay_get (overlay, Qpriority), - endpos - startpos); - } if (overlay_tails.used > 1) qsort (overlay_tails.buf, overlay_tails.used, sizeof (struct sortstr), cmp_for_strings); @@ -3456,385 +3278,26 @@ overlay_strings (ptrdiff_t pos, struct window *w, unsigned char **pstr) } return 0; } - -/* Shift overlays in BUF's overlay lists, to center the lists at POS. */ - -void -recenter_overlay_lists (struct buffer *buf, ptrdiff_t pos) -{ - Lisp_Object overlay, beg, end; - struct Lisp_Overlay *prev, *tail, *next; - - /* See if anything in overlays_before should move to overlays_after. */ - - /* We don't strictly need prev in this loop; it should always be nil. - But we use it for symmetry and in case that should cease to be true - with some future change. */ - prev = NULL; - for (tail = buf->overlays_before; tail; prev = tail, tail = next) - { - next = tail->next; - XSETMISC (overlay, tail); - eassert (OVERLAYP (overlay)); - - beg = OVERLAY_START (overlay); - end = OVERLAY_END (overlay); - - if (OVERLAY_POSITION (end) > pos) - { - /* OVERLAY needs to be moved. */ - ptrdiff_t where = OVERLAY_POSITION (beg); - struct Lisp_Overlay *other, *other_prev; - - /* Splice the cons cell TAIL out of overlays_before. */ - if (prev) - prev->next = next; - else - set_buffer_overlays_before (buf, next); - - /* Search thru overlays_after for where to put it. */ - other_prev = NULL; - for (other = buf->overlays_after; other; - other_prev = other, other = other->next) - { - Lisp_Object otherbeg, otheroverlay; - - XSETMISC (otheroverlay, other); - eassert (OVERLAYP (otheroverlay)); - - otherbeg = OVERLAY_START (otheroverlay); - if (OVERLAY_POSITION (otherbeg) >= where) - break; - } - - /* Add TAIL to overlays_after before OTHER. */ - tail->next = other; - if (other_prev) - other_prev->next = tail; - else - set_buffer_overlays_after (buf, tail); - tail = prev; - } - else - /* We've reached the things that should stay in overlays_before. - All the rest of overlays_before must end even earlier, - so stop now. */ - break; - } - - /* See if anything in overlays_after should be in overlays_before. */ - prev = NULL; - for (tail = buf->overlays_after; tail; prev = tail, tail = next) - { - next = tail->next; - XSETMISC (overlay, tail); - eassert (OVERLAYP (overlay)); - - beg = OVERLAY_START (overlay); - end = OVERLAY_END (overlay); - - /* Stop looking, when we know that nothing further - can possibly end before POS. */ - if (OVERLAY_POSITION (beg) > pos) - break; - - if (OVERLAY_POSITION (end) <= pos) - { - /* OVERLAY needs to be moved. */ - ptrdiff_t where = OVERLAY_POSITION (end); - struct Lisp_Overlay *other, *other_prev; - - /* Splice the cons cell TAIL out of overlays_after. */ - if (prev) - prev->next = next; - else - set_buffer_overlays_after (buf, next); - - /* Search thru overlays_before for where to put it. */ - other_prev = NULL; - for (other = buf->overlays_before; other; - other_prev = other, other = other->next) - { - Lisp_Object otherend, otheroverlay; - - XSETMISC (otheroverlay, other); - eassert (OVERLAYP (otheroverlay)); - - otherend = OVERLAY_END (otheroverlay); - if (OVERLAY_POSITION (otherend) <= where) - break; - } - - /* Add TAIL to overlays_before before OTHER. */ - tail->next = other; - if (other_prev) - other_prev->next = tail; - else - set_buffer_overlays_before (buf, tail); - tail = prev; - } - } - - buf->overlay_center = pos; -} + void adjust_overlays_for_insert (ptrdiff_t pos, ptrdiff_t length) { /* After an insertion, the lists are still sorted properly, but we may need to update the value of the overlay center. */ - if (current_buffer->overlay_center >= pos) - current_buffer->overlay_center += length; + if (! current_buffer->overlays) + return; + interval_tree_insert_gap (current_buffer->overlays, pos, length); } void adjust_overlays_for_delete (ptrdiff_t pos, ptrdiff_t length) { - if (current_buffer->overlay_center < pos) - /* The deletion was to our right. No change needed; the before- and - after-lists are still consistent. */ - ; - else if (current_buffer->overlay_center - pos > length) - /* The deletion was to our left. We need to adjust the center value - to account for the change in position, but the lists are consistent - given the new value. */ - current_buffer->overlay_center -= length; - else - /* We're right in the middle. There might be things on the after-list - that now belong on the before-list. Recentering will move them, - and also update the center point. */ - recenter_overlay_lists (current_buffer, pos); -} - -/* Fix up overlays that were garbled as a result of permuting markers - in the range START through END. Any overlay with at least one - endpoint in this range will need to be unlinked from the overlay - list and reinserted in its proper place. - Such an overlay might even have negative size at this point. - If so, we'll make the overlay empty. */ -void -fix_start_end_in_overlays (register ptrdiff_t start, register ptrdiff_t end) -{ - Lisp_Object overlay; - struct Lisp_Overlay *before_list; - struct Lisp_Overlay *after_list; - /* These are either nil, indicating that before_list or after_list - should be assigned, or the cons cell the cdr of which should be - assigned. */ - struct Lisp_Overlay *beforep = NULL, *afterp = NULL; - /* 'Parent', likewise, indicates a cons cell or - current_buffer->overlays_before or overlays_after, depending - which loop we're in. */ - struct Lisp_Overlay *tail, *parent; - ptrdiff_t startpos, endpos; - - /* This algorithm shifts links around instead of consing and GCing. - The loop invariant is that before_list (resp. after_list) is a - well-formed list except that its last element, the CDR of beforep - (resp. afterp) if beforep (afterp) isn't nil or before_list - (after_list) if it is, is still uninitialized. So it's not a bug - that before_list isn't initialized, although it may look - strange. */ - for (parent = NULL, tail = current_buffer->overlays_before; tail;) - { - XSETMISC (overlay, tail); - - endpos = OVERLAY_POSITION (OVERLAY_END (overlay)); - startpos = OVERLAY_POSITION (OVERLAY_START (overlay)); - - /* If the overlay is backwards, make it empty. */ - if (endpos < startpos) - { - startpos = endpos; - Fset_marker (OVERLAY_START (overlay), make_number (startpos), - Qnil); - } - - if (endpos < start) - break; - - if (endpos < end - || (startpos >= start && startpos < end)) - { - /* Add it to the end of the wrong list. Later on, - recenter_overlay_lists will move it to the right place. */ - if (endpos < current_buffer->overlay_center) - { - if (!afterp) - after_list = tail; - else - afterp->next = tail; - afterp = tail; - } - else - { - if (!beforep) - before_list = tail; - else - beforep->next = tail; - beforep = tail; - } - if (!parent) - set_buffer_overlays_before (current_buffer, tail->next); - else - parent->next = tail->next; - tail = tail->next; - } - else - parent = tail, tail = parent->next; - } - for (parent = NULL, tail = current_buffer->overlays_after; tail;) - { - XSETMISC (overlay, tail); - - startpos = OVERLAY_POSITION (OVERLAY_START (overlay)); - endpos = OVERLAY_POSITION (OVERLAY_END (overlay)); - - /* If the overlay is backwards, make it empty. */ - if (endpos < startpos) - { - startpos = endpos; - Fset_marker (OVERLAY_START (overlay), make_number (startpos), - Qnil); - } - - if (startpos >= end) - break; - - if (startpos >= start - || (endpos >= start && endpos < end)) - { - if (endpos < current_buffer->overlay_center) - { - if (!afterp) - after_list = tail; - else - afterp->next = tail; - afterp = tail; - } - else - { - if (!beforep) - before_list = tail; - else - beforep->next = tail; - beforep = tail; - } - if (!parent) - set_buffer_overlays_after (current_buffer, tail->next); - else - parent->next = tail->next; - tail = tail->next; - } - else - parent = tail, tail = parent->next; - } - - /* Splice the constructed (wrong) lists into the buffer's lists, - and let the recenter function make it sane again. */ - if (beforep) - { - beforep->next = current_buffer->overlays_before; - set_buffer_overlays_before (current_buffer, before_list); - } - - if (afterp) - { - afterp->next = current_buffer->overlays_after; - set_buffer_overlays_after (current_buffer, after_list); - } - recenter_overlay_lists (current_buffer, current_buffer->overlay_center); -} - -/* We have two types of overlay: the one whose ending marker is - after-insertion-marker (this is the usual case) and the one whose - ending marker is before-insertion-marker. When `overlays_before' - contains overlays of the latter type and the former type in this - order and both overlays end at inserting position, inserting a text - increases only the ending marker of the latter type, which results - in incorrect ordering of `overlays_before'. - - This function fixes ordering of overlays in the slot - `overlays_before' of the buffer *BP. Before the insertion, `point' - was at PREV, and now is at POS. */ - -void -fix_overlays_before (struct buffer *bp, ptrdiff_t prev, ptrdiff_t pos) -{ - /* If parent is nil, replace overlays_before; otherwise, parent->next. */ - struct Lisp_Overlay *tail = bp->overlays_before, *parent = NULL, *right_pair; - Lisp_Object tem; - ptrdiff_t end UNINIT; - - /* After the insertion, the several overlays may be in incorrect - order. The possibility is that, in the list `overlays_before', - an overlay which ends at POS appears after an overlay which ends - at PREV. Since POS is greater than PREV, we must fix the - ordering of these overlays, by moving overlays ends at POS before - the overlays ends at PREV. */ - - /* At first, find a place where disordered overlays should be linked - in. It is where an overlay which end before POS exists. (i.e. an - overlay whose ending marker is after-insertion-marker if disorder - exists). */ - while (tail - && (XSETMISC (tem, tail), - (end = OVERLAY_POSITION (OVERLAY_END (tem))) >= pos)) - { - parent = tail; - tail = tail->next; - } - - /* If we don't find such an overlay, - or the found one ends before PREV, - or the found one is the last one in the list, - we don't have to fix anything. */ - if (!tail || end < prev || !tail->next) + if (! current_buffer->overlays) return; - - right_pair = parent; - parent = tail; - tail = tail->next; - - /* Now, end position of overlays in the list TAIL should be before - or equal to PREV. In the loop, an overlay which ends at POS is - moved ahead to the place indicated by the CDR of RIGHT_PAIR. If - we found an overlay which ends before PREV, the remaining - overlays are in correct order. */ - while (tail) - { - XSETMISC (tem, tail); - end = OVERLAY_POSITION (OVERLAY_END (tem)); - - if (end == pos) - { /* This overlay is disordered. */ - struct Lisp_Overlay *found = tail; - - /* Unlink the found overlay. */ - tail = found->next; - parent->next = tail; - /* Move an overlay at RIGHT_PLACE to the next of the found one, - and link it into the right place. */ - if (!right_pair) - { - found->next = bp->overlays_before; - set_buffer_overlays_before (bp, found); - } - else - { - found->next = right_pair->next; - right_pair->next = found; - } - } - else if (end == prev) - { - parent = tail; - tail = tail->next; - } - else /* No more disordered overlay. */ - break; - } + interval_tree_delete_gap (current_buffer->overlays, pos, length); } + DEFUN ("overlayp", Foverlayp, Soverlayp, 1, 1, 0, doc: /* Return t if OBJECT is an overlay. */) @@ -3853,10 +3316,11 @@ for the front of the overlay advance when text is inserted there The fifth arg REAR-ADVANCE, if non-nil, makes the marker for the rear of the overlay advance when text is inserted there \(which means the text *is* included in the overlay). */) - (Lisp_Object beg, Lisp_Object end, Lisp_Object buffer, + (Lisp_Object begin, Lisp_Object end, Lisp_Object buffer, Lisp_Object front_advance, Lisp_Object rear_advance) { - Lisp_Object overlay; + Lisp_Object ov; + ptrdiff_t obegin, oend; struct buffer *b; if (NILP (buffer)) @@ -3864,53 +3328,35 @@ for the rear of the overlay advance when text is inserted there else CHECK_BUFFER (buffer); - if (MARKERP (beg) && !EQ (Fmarker_buffer (beg), buffer)) - signal_error ("Marker points into wrong buffer", beg); + b = XBUFFER (buffer); + if (! BUFFER_LIVE_P (b)) + error ("Attempt to create overlay in a dead buffer"); + + if (MARKERP (begin) && !EQ (Fmarker_buffer (begin), buffer)) + signal_error ("Marker points into wrong buffer", begin); if (MARKERP (end) && !EQ (Fmarker_buffer (end), buffer)) signal_error ("Marker points into wrong buffer", end); - CHECK_NUMBER_COERCE_MARKER (beg); + CHECK_NUMBER_COERCE_MARKER (begin); CHECK_NUMBER_COERCE_MARKER (end); - if (XINT (beg) > XINT (end)) + if (XINT (begin) > XINT (end)) { Lisp_Object temp; - temp = beg; beg = end; end = temp; + temp = begin; begin = end; end = temp; } - b = XBUFFER (buffer); - - beg = Fset_marker (Fmake_marker (), beg, buffer); - end = Fset_marker (Fmake_marker (), end, buffer); - - if (!NILP (front_advance)) - XMARKER (beg)->insertion_type = 1; - if (!NILP (rear_advance)) - XMARKER (end)->insertion_type = 1; - - overlay = build_overlay (beg, end, Qnil); - - /* Put the new overlay on the wrong list. */ - end = OVERLAY_END (overlay); - if (OVERLAY_POSITION (end) < b->overlay_center) - { - eassert (b->overlays_after || (XOVERLAY (overlay)->next == NULL)); - XOVERLAY (overlay)->next = b->overlays_after; - set_buffer_overlays_after (b, XOVERLAY (overlay)); - } - else - { - eassert (b->overlays_before || (XOVERLAY (overlay)->next == NULL)); - XOVERLAY (overlay)->next = b->overlays_before; - set_buffer_overlays_before (b, XOVERLAY (overlay)); - } - /* This puts it in the right list, and in the right order. */ - recenter_overlay_lists (b, b->overlay_center); + obegin = clip_to_bounds (BEG, XINT (begin), b->text->z); + oend = clip_to_bounds (obegin, XINT (end), b->text->z); + ov = build_overlay (obegin, oend, + ! NILP (front_advance), + ! NILP (rear_advance), Qnil); + add_buffer_overlay (b, XOVERLAY (ov)); /* We don't need to redisplay the region covered by the overlay, because the overlay has no properties at the moment. */ - return overlay; + return ov; } /* Mark a section of BUF as needing redisplay because of overlays changes. */ @@ -3932,35 +3378,6 @@ modify_overlay (struct buffer *buf, ptrdiff_t start, ptrdiff_t end) ++BUF_OVERLAY_MODIFF (buf); } -/* Remove OVERLAY from LIST. */ - -static struct Lisp_Overlay * -unchain_overlay (struct Lisp_Overlay *list, struct Lisp_Overlay *overlay) -{ - register struct Lisp_Overlay *tail, **prev = &list; - - for (tail = list; tail; prev = &tail->next, tail = *prev) - if (tail == overlay) - { - *prev = overlay->next; - overlay->next = NULL; - break; - } - return list; -} - -/* Remove OVERLAY from both overlay lists of B. */ - -static void -unchain_both (struct buffer *b, Lisp_Object overlay) -{ - struct Lisp_Overlay *ov = XOVERLAY (overlay); - - set_buffer_overlays_before (b, unchain_overlay (b->overlays_before, ov)); - set_buffer_overlays_after (b, unchain_overlay (b->overlays_after, ov)); - eassert (XOVERLAY (overlay)->next == NULL); -} - DEFUN ("move-overlay", Fmove_overlay, Smove_overlay, 3, 4, 0, doc: /* Set the endpoints of OVERLAY to BEG and END in BUFFER. If BUFFER is omitted, leave OVERLAY in the same buffer it inhabits now. @@ -3976,7 +3393,7 @@ buffer. */) CHECK_OVERLAY (overlay); if (NILP (buffer)) - buffer = Fmarker_buffer (OVERLAY_START (overlay)); + buffer = Foverlay_buffer (overlay); if (NILP (buffer)) XSETBUFFER (buffer, current_buffer); CHECK_BUFFER (buffer); @@ -4000,25 +3417,28 @@ buffer. */) specbind (Qinhibit_quit, Qt); - obuffer = Fmarker_buffer (OVERLAY_START (overlay)); + obuffer = Foverlay_buffer (overlay); b = XBUFFER (buffer); if (!NILP (obuffer)) { ob = XBUFFER (obuffer); - o_beg = OVERLAY_POSITION (OVERLAY_START (overlay)); - o_end = OVERLAY_POSITION (OVERLAY_END (overlay)); - - unchain_both (ob, overlay); + o_beg = OVERLAY_START (overlay); + o_end = OVERLAY_END (overlay); } + if (! EQ (buffer, obuffer)) + { + if (! NILP (obuffer)) + remove_buffer_overlay (XBUFFER (obuffer), XOVERLAY (overlay)); + add_buffer_overlay (XBUFFER (buffer), XOVERLAY (overlay)); + } /* Set the overlay boundaries, which may clip them. */ - Fset_marker (OVERLAY_START (overlay), beg, buffer); - Fset_marker (OVERLAY_END (overlay), end, buffer); + set_overlay_region (XOVERLAY (overlay), XINT (beg), XINT (end)); - n_beg = marker_position (OVERLAY_START (overlay)); - n_end = marker_position (OVERLAY_END (overlay)); + n_beg = OVERLAY_START (overlay); + n_end = OVERLAY_END (overlay); /* If the overlay has changed buffers, do a thorough redisplay. */ if (!EQ (buffer, obuffer)) @@ -4046,22 +3466,6 @@ buffer. */) if (n_beg == n_end && !NILP (Foverlay_get (overlay, Qevaporate))) return unbind_to (count, Fdelete_overlay (overlay)); - /* Put the overlay into the new buffer's overlay lists, first on the - wrong list. */ - if (n_end < b->overlay_center) - { - XOVERLAY (overlay)->next = b->overlays_after; - set_buffer_overlays_after (b, XOVERLAY (overlay)); - } - else - { - XOVERLAY (overlay)->next = b->overlays_before; - set_buffer_overlays_before (b, XOVERLAY (overlay)); - } - - /* This puts it in the right list, and in the right order. */ - recenter_overlay_lists (b, b->overlay_center); - return unbind_to (count, overlay); } @@ -4069,21 +3473,18 @@ DEFUN ("delete-overlay", Fdelete_overlay, Sdelete_overlay, 1, 1, 0, doc: /* Delete the overlay OVERLAY from its buffer. */) (Lisp_Object overlay) { - Lisp_Object buffer; struct buffer *b; ptrdiff_t count = SPECPDL_INDEX (); CHECK_OVERLAY (overlay); - buffer = Fmarker_buffer (OVERLAY_START (overlay)); - if (NILP (buffer)) + b = OVERLAY_BUFFER (overlay); + if (! b) return Qnil; - b = XBUFFER (buffer); specbind (Qinhibit_quit, Qt); - unchain_both (b, overlay); - drop_overlay (b, XOVERLAY (overlay)); + drop_overlay (XOVERLAY (overlay)); /* When deleting an overlay with before or after strings, turn off display optimizations for the affected buffer, on the basis that @@ -4114,8 +3515,10 @@ DEFUN ("overlay-start", Foverlay_start, Soverlay_start, 1, 1, 0, (Lisp_Object overlay) { CHECK_OVERLAY (overlay); + if (! OVERLAY_BUFFER (overlay)) + return Qnil; - return (Fmarker_position (OVERLAY_START (overlay))); + return make_number (OVERLAY_START (overlay)); } DEFUN ("overlay-end", Foverlay_end, Soverlay_end, 1, 1, 0, @@ -4123,8 +3526,10 @@ DEFUN ("overlay-end", Foverlay_end, Soverlay_end, 1, 1, 0, (Lisp_Object overlay) { CHECK_OVERLAY (overlay); + if (! OVERLAY_BUFFER (overlay)) + return Qnil; - return (Fmarker_position (OVERLAY_END (overlay))); + return make_number (OVERLAY_END (overlay)); } DEFUN ("overlay-buffer", Foverlay_buffer, Soverlay_buffer, 1, 1, 0, @@ -4132,9 +3537,16 @@ DEFUN ("overlay-buffer", Foverlay_buffer, Soverlay_buffer, 1, 1, 0, Return nil if OVERLAY has been deleted. */) (Lisp_Object overlay) { + Lisp_Object buffer; + CHECK_OVERLAY (overlay); - return Fmarker_buffer (OVERLAY_START (overlay)); + if (! OVERLAY_BUFFER (overlay)) + return Qnil; + + XSETBUFFER (buffer, OVERLAY_BUFFER (overlay)); + + return buffer; } DEFUN ("overlay-properties", Foverlay_properties, Soverlay_properties, 1, 1, 0, @@ -4145,7 +3557,7 @@ OVERLAY. */) { CHECK_OVERLAY (overlay); - return Fcopy_sequence (XOVERLAY (overlay)->plist); + return Fcopy_sequence (OVERLAY_PLIST (overlay)); } @@ -4169,8 +3581,7 @@ If SORTED is non-nil, then sort them by decreasing priority. */) /* Put all the overlays we want in a vector in overlay_vec. Store the length in len. */ - noverlays = overlays_at (XINT (pos), 1, &overlay_vec, &len, - NULL, NULL, 0); + noverlays = overlays_at (XINT (pos), 1, &overlay_vec, &len, NULL); if (!NILP (sorted)) noverlays = sort_overlays (overlay_vec, noverlays, @@ -4213,8 +3624,7 @@ end of the buffer. */) /* Put all the overlays we want in a vector in overlay_vec. Store the length in len. */ - noverlays = overlays_in (XINT (beg), XINT (end), 1, &overlay_vec, &len, - NULL, NULL); + noverlays = overlays_in (XINT (beg), XINT (end), 1, &overlay_vec, &len, true, NULL); /* Make a list of them all. */ result = Flist (noverlays, overlay_vec); @@ -4230,39 +3640,12 @@ If there are no overlay boundaries from POS to (point-max), the value is (point-max). */) (Lisp_Object pos) { - ptrdiff_t i, len, noverlays; - ptrdiff_t endpos; - Lisp_Object *overlay_vec; - CHECK_NUMBER_COERCE_MARKER (pos); if (!buffer_has_overlays ()) return make_number (ZV); - len = 10; - overlay_vec = xmalloc (len * sizeof *overlay_vec); - - /* Put all the overlays we want in a vector in overlay_vec. - Store the length in len. - endpos gets the position where the next overlay starts. */ - noverlays = overlays_at (XINT (pos), 1, &overlay_vec, &len, - &endpos, 0, 1); - - /* If any of these overlays ends before endpos, - use its ending point instead. */ - for (i = 0; i < noverlays; i++) - { - Lisp_Object oend; - ptrdiff_t oendpos; - - oend = OVERLAY_END (overlay_vec[i]); - oendpos = OVERLAY_POSITION (oend); - if (oendpos < endpos) - endpos = oendpos; - } - - xfree (overlay_vec); - return make_number (endpos); + return make_number (next_overlay_change (XINT (pos))); } DEFUN ("previous-overlay-change", Fprevious_overlay_change, @@ -4272,32 +3655,15 @@ If there are no overlay boundaries from (point-min) to POS, the value is (point-min). */) (Lisp_Object pos) { - ptrdiff_t prevpos; - Lisp_Object *overlay_vec; - ptrdiff_t len; CHECK_NUMBER_COERCE_MARKER (pos); if (!buffer_has_overlays ()) return make_number (BEGV); - /* At beginning of buffer, we know the answer; - avoid bug subtracting 1 below. */ - if (XINT (pos) == BEGV) - return pos; - - len = 10; - overlay_vec = xmalloc (len * sizeof *overlay_vec); - - /* Put all the overlays we want in a vector in overlay_vec. - Store the length in len. - prevpos gets the position of the previous change. */ - overlays_at (XINT (pos), 1, &overlay_vec, &len, - 0, &prevpos, 1); - - xfree (overlay_vec); - return make_number (prevpos); + return make_number (previous_overlay_change (XINT (pos))); } + /* These functions are for debugging overlays. */ @@ -4307,24 +3673,21 @@ The car has all the overlays before the overlay center; the cdr has all the overlays after the overlay center. Recentering overlays moves overlays between these lists. The lists you get are copies, so that changing them has no effect. -However, the overlays you get are the real objects that the buffer uses. */) +However, the overlays you get are the real objects that the buffer uses. */) (void) { - struct Lisp_Overlay *ol; - Lisp_Object before = Qnil, after = Qnil, tmp; + Lisp_Object overlays = Qnil; - for (ol = current_buffer->overlays_before; ol; ol = ol->next) - { - XSETMISC (tmp, ol); - before = Fcons (tmp, before); - } - for (ol = current_buffer->overlays_after; ol; ol = ol->next) + if (current_buffer->overlays) { - XSETMISC (tmp, ol); - after = Fcons (tmp, after); - } + struct interval_node *node; - return Fcons (Fnreverse (before), Fnreverse (after)); + buffer_overlay_iter_start (current_buffer, BEG, Z, ITREE_DESCENDING); + while ((node = buffer_overlay_iter_next (current_buffer))) + overlays = Fcons (node->data, overlays); + buffer_overlay_iter_finish (current_buffer); + } + return Fcons (overlays, Qnil); } DEFUN ("overlay-recenter", Foverlay_recenter, Soverlay_recenter, 1, 1, 0, @@ -4333,11 +3696,8 @@ That makes overlay lookup faster for positions near POS (but perhaps slower for positions far away from POS). */) (Lisp_Object pos) { - ptrdiff_t p; CHECK_NUMBER_COERCE_MARKER (pos); - - p = clip_to_bounds (PTRDIFF_MIN, XINT (pos), PTRDIFF_MAX); - recenter_overlay_lists (current_buffer, p); + /* Noop */ return Qnil; } @@ -4354,12 +3714,13 @@ DEFUN ("overlay-put", Foverlay_put, Soverlay_put, 3, 3, 0, VALUE will be returned.*/) (Lisp_Object overlay, Lisp_Object prop, Lisp_Object value) { - Lisp_Object tail, buffer; + Lisp_Object tail; + struct buffer *b; bool changed; CHECK_OVERLAY (overlay); - buffer = Fmarker_buffer (OVERLAY_START (overlay)); + b = OVERLAY_BUFFER (overlay); for (tail = XOVERLAY (overlay)->plist; CONSP (tail) && CONSP (XCDR (tail)); @@ -4375,15 +3736,14 @@ VALUE will be returned.*/) set_overlay_plist (overlay, Fcons (prop, Fcons (value, XOVERLAY (overlay)->plist))); found: - if (! NILP (buffer)) + if (b) { if (changed) - modify_overlay (XBUFFER (buffer), - marker_position (OVERLAY_START (overlay)), - marker_position (OVERLAY_END (overlay))); + modify_overlay (b, OVERLAY_START (overlay), + OVERLAY_END (overlay)); if (EQ (prop, Qevaporate) && ! NILP (value) - && (OVERLAY_POSITION (OVERLAY_START (overlay)) - == OVERLAY_POSITION (OVERLAY_END (overlay)))) + && (OVERLAY_START (overlay) + == OVERLAY_END (overlay))) Fdelete_overlay (overlay); } @@ -4441,14 +3801,9 @@ void report_overlay_modification (Lisp_Object start, Lisp_Object end, bool after, Lisp_Object arg1, Lisp_Object arg2, Lisp_Object arg3) { - Lisp_Object prop, overlay; - struct Lisp_Overlay *tail; /* True if this change is an insertion. */ bool insertion = (after ? XFASTINT (arg3) == 0 : EQ (start, end)); - overlay = Qnil; - tail = NULL; - /* We used to run the functions as soon as we found them and only register them in last_overlay_modification_hooks for the purpose of the `after' case. But running elisp code as we traverse the list of overlays is @@ -4459,68 +3814,35 @@ report_overlay_modification (Lisp_Object start, Lisp_Object end, bool after, if (!after) { + struct interval_node *node; + EMACS_INT begin_arg = XFASTINT (start); + EMACS_INT end_arg = XFASTINT (end); /* We are being called before a change. Scan the overlays to find the functions to call. */ last_overlay_modification_hooks_used = 0; - for (tail = current_buffer->overlays_before; tail; tail = tail->next) - { - ptrdiff_t startpos, endpos; - Lisp_Object ostart, oend; - - XSETMISC (overlay, tail); - - ostart = OVERLAY_START (overlay); - oend = OVERLAY_END (overlay); - endpos = OVERLAY_POSITION (oend); - if (XFASTINT (start) > endpos) - break; - startpos = OVERLAY_POSITION (ostart); - if (insertion && (XFASTINT (start) == startpos - || XFASTINT (end) == startpos)) - { - prop = Foverlay_get (overlay, Qinsert_in_front_hooks); - if (!NILP (prop)) - add_overlay_mod_hooklist (prop, overlay); - } - if (insertion && (XFASTINT (start) == endpos - || XFASTINT (end) == endpos)) - { - prop = Foverlay_get (overlay, Qinsert_behind_hooks); - if (!NILP (prop)) - add_overlay_mod_hooklist (prop, overlay); - } - /* Test for intersecting intervals. This does the right thing - for both insertion and deletion. */ - if (XFASTINT (end) > startpos && XFASTINT (start) < endpos) - { - prop = Foverlay_get (overlay, Qmodification_hooks); - if (!NILP (prop)) - add_overlay_mod_hooklist (prop, overlay); - } - } - for (tail = current_buffer->overlays_after; tail; tail = tail->next) + if (! current_buffer->overlays) + return; + buffer_overlay_iter_start (current_buffer, + begin_arg - (insertion ? 1 : 0), + end_arg + (insertion ? 1 : 0), + ITREE_ASCENDING); + while ((node = buffer_overlay_iter_next (current_buffer))) { - ptrdiff_t startpos, endpos; - Lisp_Object ostart, oend; - - XSETMISC (overlay, tail); - - ostart = OVERLAY_START (overlay); - oend = OVERLAY_END (overlay); - startpos = OVERLAY_POSITION (ostart); - endpos = OVERLAY_POSITION (oend); - if (XFASTINT (end) < startpos) - break; - if (insertion && (XFASTINT (start) == startpos - || XFASTINT (end) == startpos)) + Lisp_Object overlay = node->data; + ptrdiff_t obegin = OVERLAY_START (overlay); + ptrdiff_t oend = OVERLAY_END (overlay); + Lisp_Object prop; + + if (insertion && (begin_arg == obegin + || end_arg == obegin)) { prop = Foverlay_get (overlay, Qinsert_in_front_hooks); if (!NILP (prop)) add_overlay_mod_hooklist (prop, overlay); } - if (insertion && (XFASTINT (start) == endpos - || XFASTINT (end) == endpos)) + if (insertion && (begin_arg == oend + || end_arg == oend)) { prop = Foverlay_get (overlay, Qinsert_behind_hooks); if (!NILP (prop)) @@ -4528,15 +3850,15 @@ report_overlay_modification (Lisp_Object start, Lisp_Object end, bool after, } /* Test for intersecting intervals. This does the right thing for both insertion and deletion. */ - if (XFASTINT (end) > startpos && XFASTINT (start) < endpos) + if (! insertion || (end_arg > obegin && begin_arg < oend)) { prop = Foverlay_get (overlay, Qmodification_hooks); if (!NILP (prop)) add_overlay_mod_hooklist (prop, overlay); } } + buffer_overlay_iter_finish (current_buffer); } - { /* Call the functions recorded in last_overlay_modification_hooks. First copy the vector contents, in case some of these hooks @@ -4558,7 +3880,7 @@ report_overlay_modification (Lisp_Object start, Lisp_Object end, bool after, function is never called to record the overlay modification hook functions in the last_overlay_modification_hooks array, so anything we find there is not ours. */ - if (XMARKER (OVERLAY_START (ovl))->buffer != current_buffer) + if (OVERLAY_BUFFER (ovl) != current_buffer) return; } @@ -4598,34 +3920,22 @@ call_overlay_mod_hooks (Lisp_Object list, Lisp_Object overlay, bool after, void evaporate_overlays (ptrdiff_t pos) { - Lisp_Object overlay, hit_list; - struct Lisp_Overlay *tail; + Lisp_Object hit_list; + struct interval_node *node; + + if (! current_buffer->overlays) + return; hit_list = Qnil; - if (pos <= current_buffer->overlay_center) - for (tail = current_buffer->overlays_before; tail; tail = tail->next) - { - ptrdiff_t endpos; - XSETMISC (overlay, tail); - endpos = OVERLAY_POSITION (OVERLAY_END (overlay)); - if (endpos < pos) - break; - if (endpos == pos && OVERLAY_POSITION (OVERLAY_START (overlay)) == pos - && ! NILP (Foverlay_get (overlay, Qevaporate))) - hit_list = Fcons (overlay, hit_list); - } - else - for (tail = current_buffer->overlays_after; tail; tail = tail->next) - { - ptrdiff_t startpos; - XSETMISC (overlay, tail); - startpos = OVERLAY_POSITION (OVERLAY_START (overlay)); - if (startpos > pos) - break; - if (startpos == pos && OVERLAY_POSITION (OVERLAY_END (overlay)) == pos - && ! NILP (Foverlay_get (overlay, Qevaporate))) - hit_list = Fcons (overlay, hit_list); - } + buffer_overlay_iter_start (current_buffer, pos, pos, ITREE_ASCENDING); + + while ((node = buffer_overlay_iter_next (current_buffer))) + { + if (node->end == pos + && ! NILP (Foverlay_get (node->data, Qevaporate))) + hit_list = Fcons (node->data, hit_list); + } + buffer_overlay_iter_finish (current_buffer); for (; CONSP (hit_list); hit_list = XCDR (hit_list)) Fdelete_overlay (XCAR (hit_list)); } @@ -5212,9 +4522,7 @@ init_buffer_once (void) bset_mark_active (&buffer_defaults, Qnil); bset_file_format (&buffer_defaults, Qnil); bset_auto_save_file_format (&buffer_defaults, Qt); - set_buffer_overlays_before (&buffer_defaults, NULL); - set_buffer_overlays_after (&buffer_defaults, NULL); - buffer_defaults.overlay_center = BEG; + buffer_defaults.overlays = NULL; XSETFASTINT (BVAR (&buffer_defaults, tab_width), 8); bset_truncate_lines (&buffer_defaults, Qnil); @@ -5434,6 +4742,48 @@ defvar_per_buffer (struct Lisp_Buffer_Objfwd *bo_fwd, const char *namestring, emacs_abort (); } +#ifdef ITREE_DEBUG +static Lisp_Object +make_lispy_interval_node (const struct interval_node *node) +{ + return listn (CONSTYPE_HEAP, 12, + intern (":begin"), + make_number (node->begin), + intern (":end"), + make_number (node->end), + intern (":limit"), + make_number (node->limit), + intern (":offset"), + make_number (node->offset), + intern (":rear-advance"), + node->rear_advance ? Qt : Qnil, + intern (":front-advance"), + node->front_advance ? Qt : Qnil); +} + +static Lisp_Object +overlay_tree (const struct interval_tree *tree, + const struct interval_node *node) +{ + if (node == &tree->nil) + return Qnil; + return list3 (make_lispy_interval_node (node), + overlay_tree (tree, node->left), + overlay_tree (tree, node->right)); +} + +DEFUN ("overlay-tree", Foverlay_tree, Soverlay_tree, 0, 1, 0, + doc: /* Get the overlay tree for BUFFER. */) + (Lisp_Object buffer) +{ + struct buffer *b = decode_buffer (buffer); + if (! b->overlays) + return Qnil; + return overlay_tree (b->overlays, b->overlays->root); +} +#endif + + /* Initialize the buffer routines. */ void @@ -6303,6 +5653,10 @@ Functions running this hook are, `get-buffer-create', defsubr (&Srestore_buffer_modified_p); Fput (intern_c_string ("erase-buffer"), Qdisabled, Qt); + +#ifdef ITREE_DEBUG + defsubr (&Soverlay_tree); +#endif } void diff --git a/src/buffer.h b/src/buffer.h index ac7c5a5467..ef31ad1ed9 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -26,6 +26,7 @@ along with GNU Emacs. If not, see . */ #include "character.h" #include "lisp.h" +#include "itree.h" INLINE_HEADER_BEGIN @@ -877,16 +878,8 @@ struct buffer /* Non-zero whenever the narrowing is changed in this buffer. */ bool_bf clip_changed : 1; - /* List of overlays that end at or before the current center, - in order of end-position. */ - struct Lisp_Overlay *overlays_before; - - /* List of overlays that end after the current center, - in order of start-position. */ - struct Lisp_Overlay *overlays_after; - - /* Position where the overlay lists are centered. */ - ptrdiff_t overlay_center; + /* The inveral tree containing this buffer's overlays. */ + struct interval_tree *overlays; /* Changes in the buffer are recorded here for undo, and t means don't record anything. This information belongs to the base @@ -896,6 +889,14 @@ struct buffer Lisp_Object undo_list_; }; +struct sortvec +{ + Lisp_Object overlay; + ptrdiff_t beg, end; + EMACS_INT priority; + EMACS_INT spriority; /* Secondary priority. */ +}; + INLINE bool BUFFERP (Lisp_Object a) { @@ -1109,8 +1110,11 @@ extern void delete_all_overlays (struct buffer *); extern void reset_buffer (struct buffer *); extern void compact_buffer (struct buffer *); extern void evaporate_overlays (ptrdiff_t); -extern ptrdiff_t overlays_at (EMACS_INT, bool, Lisp_Object **, - ptrdiff_t *, ptrdiff_t *, ptrdiff_t *, bool); +extern ptrdiff_t overlays_at (ptrdiff_t, bool, Lisp_Object **, ptrdiff_t *, ptrdiff_t *); +extern ptrdiff_t overlays_in (ptrdiff_t, ptrdiff_t, bool, Lisp_Object **, + ptrdiff_t *, bool, ptrdiff_t *); +extern ptrdiff_t previous_overlay_change (ptrdiff_t); +extern ptrdiff_t next_overlay_change (ptrdiff_t); extern ptrdiff_t sort_overlays (Lisp_Object *, ptrdiff_t, struct window *); extern void recenter_overlay_lists (struct buffer *, ptrdiff_t); extern ptrdiff_t overlay_strings (ptrdiff_t, struct window *, unsigned char **); @@ -1162,18 +1166,16 @@ record_unwind_current_buffer (void) If NEXTP is non-NULL, return next overlay there. See overlay_at arg CHANGE_REQ for meaning of CHRQ arg. */ -#define GET_OVERLAYS_AT(posn, overlays, noverlays, nextp, chrq) \ +#define GET_OVERLAYS_AT(posn, overlays, noverlays, next) \ do { \ ptrdiff_t maxlen = 40; \ SAFE_NALLOCA (overlays, 1, maxlen); \ - (noverlays) = overlays_at (posn, false, &(overlays), &maxlen, \ - nextp, NULL, chrq); \ + (noverlays) = overlays_at (posn, false, &(overlays), &maxlen, next); \ if ((noverlays) > maxlen) \ { \ maxlen = noverlays; \ SAFE_NALLOCA (overlays, 1, maxlen); \ - (noverlays) = overlays_at (posn, false, &(overlays), &maxlen, \ - nextp, NULL, chrq); \ + (noverlays) = overlays_at (posn, false, &(overlays), &maxlen, next); \ } \ } while (false) @@ -1208,7 +1210,8 @@ set_buffer_intervals (struct buffer *b, INTERVAL i) INLINE bool buffer_has_overlays (void) { - return current_buffer->overlays_before || current_buffer->overlays_after; + return current_buffer->overlays + && (interval_tree_size (current_buffer->overlays) > 0); } /* Return character code of multi-byte form at byte position POS. If POS @@ -1248,23 +1251,124 @@ buffer_window_count (struct buffer *b) /* Overlays */ -/* Return the marker that stands for where OV starts in the buffer. */ +INLINE ptrdiff_t +overlay_start (struct Lisp_Overlay *ov) +{ + if (! ov->buffer) + return -1; + return interval_node_begin (ov->buffer->overlays, ov->interval); +} + +INLINE ptrdiff_t +overlay_end (struct Lisp_Overlay *ov) +{ + if (! ov->buffer) + return -1; + return interval_node_end (ov->buffer->overlays, ov->interval); +} + +INLINE void +set_overlay_region (struct Lisp_Overlay *ov, ptrdiff_t begin, ptrdiff_t end) +{ + eassert (ov->buffer); + begin = clip_to_bounds (BEG, begin, ov->buffer->text->z); + end = clip_to_bounds (begin, end, ov->buffer->text->z); + interval_node_set_region (ov->buffer->overlays, ov->interval, begin, end); +} + +INLINE void +maybe_alloc_buffer_overlays (struct buffer *b) +{ + if (! b->overlays) + b->overlays = interval_tree_create (); +} + +/* FIXME: Actually this does not free any overlay, but the tree + only. --ap */ + +INLINE void +free_buffer_overlays (struct buffer *b) +{ + eassert (! b->overlays || 0 == interval_tree_size (b->overlays)); + if (b->overlays) + { + interval_tree_destroy (b->overlays); + b->overlays = NULL; + } +} + +INLINE void +add_buffer_overlay (struct buffer *b, struct Lisp_Overlay *ov) +{ + eassert (! ov->buffer); + maybe_alloc_buffer_overlays (b); + ov->buffer = b; + interval_tree_insert (b->overlays, ov->interval); +} + +INLINE void +remove_buffer_overlay (struct buffer *b, struct Lisp_Overlay *ov) +{ + eassert (b->overlays); + eassert (ov->buffer == b); + interval_tree_remove (ov->buffer->overlays, ov->interval); + ov->buffer = NULL; +} + +INLINE void +buffer_overlay_iter_start (struct buffer *b, ptrdiff_t begin, ptrdiff_t end, + enum interval_tree_order order) +{ + if (b->overlays) + interval_tree_iter_start (b->overlays, begin, end, order); +} + +INLINE struct interval_node* +buffer_overlay_iter_next (struct buffer *b) +{ + if (! b->overlays) + return NULL; + return interval_tree_iter_next (b->overlays); +} + +INLINE void +buffer_overlay_iter_finish (struct buffer *b) +{ + if (b->overlays) + interval_tree_iter_finish (b->overlays); +} + +INLINE void +buffer_overlay_iter_narrow (struct buffer *b, ptrdiff_t begin, ptrdiff_t end) +{ + if (b->overlays) + interval_tree_iter_narrow (b->overlays, begin, end); +} -#define OVERLAY_START(OV) XOVERLAY (OV)->start +/* Return the start of OV in its buffer, or -1 if OV is not associated + with any buffer. */ -/* Return the marker that stands for where OV ends in the buffer. */ +#define OVERLAY_START(OV) (overlay_start (XOVERLAY (OV))) -#define OVERLAY_END(OV) XOVERLAY (OV)->end +/* Return the end of OV in its buffer, or -1. */ + +#define OVERLAY_END(OV) (overlay_end (XOVERLAY (OV))) /* Return the plist of overlay OV. */ -#define OVERLAY_PLIST(OV) XOVERLAY (OV)->plist +#define OVERLAY_PLIST(OV) (XOVERLAY (OV)->plist) + +/* Return the buffer of overlay OV. */ + +#define OVERLAY_BUFFER(OV) (XOVERLAY (OV)->buffer) -/* Return the actual buffer position for the marker P. - We assume you know which buffer it's pointing into. */ +/* Return true, if OV's rear-advance is set. */ -#define OVERLAY_POSITION(P) \ - (MARKERP (P) ? marker_position (P) : (emacs_abort (), 0)) +#define OVERLAY_REAR_ADVANCE_P(OV) (XOVERLAY (OV)->interval->rear_advance) + +/* Return true, if OV's front-advance is set. */ + +#define OVERLAY_FRONT_ADVANCE_P(OV) (XOVERLAY (OV)->interval->front_advance) /*********************************************************************** @@ -1405,4 +1509,7 @@ lowercasep (int c) INLINE_HEADER_END +int compare_overlays (const void *v1, const void *v2); +void make_sortvec_item (struct sortvec *item, Lisp_Object overlay); + #endif /* EMACS_BUFFER_H */ diff --git a/src/editfns.c b/src/editfns.c index 4dcf7cbe6e..8628b1b2d4 100644 --- a/src/editfns.c +++ b/src/editfns.c @@ -457,51 +457,9 @@ If you set the marker not to point anywhere, the buffer will have no mark. */) of length LEN. */ static ptrdiff_t -overlays_around (EMACS_INT pos, Lisp_Object *vec, ptrdiff_t len) +overlays_around (ptrdiff_t pos, Lisp_Object *vec, ptrdiff_t len) { - Lisp_Object overlay, start, end; - struct Lisp_Overlay *tail; - ptrdiff_t startpos, endpos; - ptrdiff_t idx = 0; - - for (tail = current_buffer->overlays_before; tail; tail = tail->next) - { - XSETMISC (overlay, tail); - - end = OVERLAY_END (overlay); - endpos = OVERLAY_POSITION (end); - if (endpos < pos) - break; - start = OVERLAY_START (overlay); - startpos = OVERLAY_POSITION (start); - if (startpos <= pos) - { - if (idx < len) - vec[idx] = overlay; - /* Keep counting overlays even if we can't return them all. */ - idx++; - } - } - - for (tail = current_buffer->overlays_after; tail; tail = tail->next) - { - XSETMISC (overlay, tail); - - start = OVERLAY_START (overlay); - startpos = OVERLAY_POSITION (start); - if (pos < startpos) - break; - end = OVERLAY_END (overlay); - endpos = OVERLAY_POSITION (end); - if (pos <= endpos) - { - if (idx < len) - vec[idx] = overlay; - idx++; - } - } - - return idx; + return overlays_in (pos - 1, pos, false, &vec, &len, false, NULL); } DEFUN ("get-pos-property", Fget_pos_property, Sget_pos_property, 2, 3, 0, @@ -561,11 +519,10 @@ at POSITION. */) if (!NILP (tem)) { /* Check the overlay is indeed active at point. */ - Lisp_Object start = OVERLAY_START (ol), finish = OVERLAY_END (ol); - if ((OVERLAY_POSITION (start) == posn - && XMARKER (start)->insertion_type == 1) - || (OVERLAY_POSITION (finish) == posn - && XMARKER (finish)->insertion_type == 0)) + if ((OVERLAY_START (ol) == posn + && OVERLAY_FRONT_ADVANCE_P (ol)) + || (OVERLAY_END (ol) == posn + && ! OVERLAY_REAR_ADVANCE_P (ol))) ; /* The overlay will not cover a char inserted at point. */ else { @@ -5385,7 +5342,6 @@ Transposing beyond buffer boundaries is an error. */) transpose_markers (start1, end1, start2, end2, start1_byte, start1_byte + len1_byte, start2_byte, start2_byte + len2_byte); - fix_start_end_in_overlays (start1, end2); } else { diff --git a/src/fileio.c b/src/fileio.c index 11370279d1..6b22b29aa7 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -3656,8 +3656,7 @@ by calling `format-decode', which see. */) bset_read_only (buf, Qnil); bset_filename (buf, Qnil); bset_undo_list (buf, Qt); - eassert (buf->overlays_before == NULL); - eassert (buf->overlays_after == NULL); + eassert (buf->overlays == NULL); set_buffer_internal (buf); Ferase_buffer (); diff --git a/src/fns.c b/src/fns.c index 2311a6e041..9f41103682 100644 --- a/src/fns.c +++ b/src/fns.c @@ -2240,10 +2240,9 @@ internal_equal (Lisp_Object o1, Lisp_Object o2, enum equal_kind equal_kind, return false; if (OVERLAYP (o1)) { - if (!internal_equal (OVERLAY_START (o1), OVERLAY_START (o2), - equal_kind, depth + 1, ht) - || !internal_equal (OVERLAY_END (o1), OVERLAY_END (o2), - equal_kind, depth + 1, ht)) + if (OVERLAY_START (o1) != OVERLAY_START (o2) + || OVERLAY_END (o1) != OVERLAY_END (o2) + || OVERLAY_BUFFER (o1) != OVERLAY_BUFFER (o2)) return false; o1 = XOVERLAY (o1)->plist; o2 = XOVERLAY (o2)->plist; diff --git a/src/indent.c b/src/indent.c index 26507b5eb5..8ac7c6ef10 100644 --- a/src/indent.c +++ b/src/indent.c @@ -225,9 +225,6 @@ skip_invisible (ptrdiff_t pos, ptrdiff_t *next_boundary_p, ptrdiff_t to, Lisp_Ob XSETFASTINT (position, pos); XSETBUFFER (buffer, current_buffer); - /* Give faster response for overlay lookup near POS. */ - recenter_overlay_lists (current_buffer, pos); - /* We must not advance farther than the next overlay change. The overlay change might change the invisible property; or there might be overlay strings to be displayed there. */ @@ -501,7 +498,7 @@ check_display_width (ptrdiff_t pos, ptrdiff_t col, ptrdiff_t *endpos) { ptrdiff_t start; if (OVERLAYP (overlay)) - *endpos = OVERLAY_POSITION (OVERLAY_END (overlay)); + *endpos = OVERLAY_END (overlay); else get_property_and_range (pos, Qdisplay, &val, &start, endpos, Qnil); diff --git a/src/insdel.c b/src/insdel.c index 5dfc62843a..3e9f0c90e3 100644 --- a/src/insdel.c +++ b/src/insdel.c @@ -276,7 +276,6 @@ adjust_markers_for_insert (ptrdiff_t from, ptrdiff_t from_byte, ptrdiff_t to, ptrdiff_t to_byte, bool before_markers) { struct Lisp_Marker *m; - bool adjusted = 0; ptrdiff_t nchars = to - from; ptrdiff_t nbytes = to_byte - from_byte; @@ -292,8 +291,6 @@ adjust_markers_for_insert (ptrdiff_t from, ptrdiff_t from_byte, { m->bytepos = to_byte; m->charpos = to; - if (m->insertion_type) - adjusted = 1; } } else if (m->bytepos > from_byte) @@ -302,15 +299,6 @@ adjust_markers_for_insert (ptrdiff_t from, ptrdiff_t from_byte, m->charpos += nchars; } } - - /* Adjusting only markers whose insertion-type is t may result in - - disordered start and end in overlays, and - - disordered overlays in the slot `overlays_before' of current_buffer. */ - if (adjusted) - { - fix_start_end_in_overlays (from, to); - fix_overlays_before (current_buffer, from, to); - } } /* Adjust point for an insertion of NBYTES bytes, which are NCHARS characters. diff --git a/src/intervals.c b/src/intervals.c index e711212d74..3db80ebed4 100644 --- a/src/intervals.c +++ b/src/intervals.c @@ -1810,8 +1810,8 @@ adjust_for_invis_intang (ptrdiff_t pos, ptrdiff_t test_offs, ptrdiff_t adj, == (test_offs == 0 ? 1 : -1)) /* Invisible property is from an overlay. */ : (test_offs == 0 - ? XMARKER (OVERLAY_START (invis_overlay))->insertion_type == 0 - : XMARKER (OVERLAY_END (invis_overlay))->insertion_type == 1))) + ? ! OVERLAY_FRONT_ADVANCE_P (invis_overlay) + : OVERLAY_REAR_ADVANCE_P (invis_overlay)))) pos += adj; return pos; diff --git a/src/itree.c b/src/itree.c new file mode 100644 index 0000000000..0c10100eef --- /dev/null +++ b/src/itree.c @@ -0,0 +1,1138 @@ +/* This file implements an efficient interval data-structure. + +Copyright (C) 2017 Andreas Politz (politza@hochschule-trier.de) + +This file is not part of GNU Emacs. + +GNU Emacs is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or (at +your option) any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs. If not, see . */ + +#include +#include +#include "lisp.h" +#include "itree.h" + +/* + Intervals of the form [BEGIN, END), are stored as nodes inside a RB + tree, sorted by BEGIN . The core operation of this tree (besides + insert, remove, etc.) is finding all intervals intersecting with + some given interval. In order to perform this operation + efficiently, every node stores a third value called LIMIT. (See + https://en.wikipedia.org/wiki/Interval_tree#Augmented_tree and its + source Introduction to Algorithms (Section 14.3), Cormen et al. .) + + ==== Finding intervals ==== + + If we search for all intervals intersecting with (X, Y], we look at + some node and test whether + + NODE.BEGIN > Y + + Due to the invariant of the search tree, we know, that we may + safely prune NODE's right subtree if this test succeeds, since all + intervals begin strictly after Y. + + But we can not make such an assumptions about the left tree, since + all we know is that the intervals in this subtree must start before + or at NODE.BEGIN. So we can't tell, whether they end before X or + not. To solve this problem we add another attribute to each node, + called LIMIT. + + The LIMIT of a node is the largest END value occurring in the nodes + subtree (including the node itself). Thus, we may look at the left + child of some NODE and test whether + + NODE.left.LIMIT < X + + and this tells us, if all intervals in the left subtree of NODE end + before X and if they can be pruned. + + Conversely, if this inequality is false, the left subtree must + contain at least one intersecting interval, giving a resulting time + complexity of O(K*log(N)) for this operation, where K is the size + of the result set and N the size of the tree. + + ==== Adjusting intervals ==== + + Since this data-structure will be used for overlays in an Emacs + buffer, a second core operation implements the ability to insert or + delete gaps in resp. from the tree. This models the insertion + resp. deletion of text in a buffer and the effects it may have on + the positions of overlays. + + Consider this: Something gets inserted at position P into a buffer + and assume that all overlays occur strictly after P. Ordinarily, + we would have to iterate all overlays and increment their BEGIN and + END values accordingly (the insertion of text pushes them back). + In order to avoid this, we introduce yet another node attribute, + called OFFSET. + + The OFFSET of some some subtree, represented by its root, is the + amount of shift that needs to be applied to its BEGIN, END and + LIMIT values, in order to get to the real values. Coming back to + the example, all we would need to do in this case, is to increment + the OFFSET of the tree's root, without any traversal of the tree + itself. + + As a consequence, the real values of BEGIN, END and LIMIT of some + NODE need to be computed by incrementing them by the sum of NODE's + OFFSET and all of its ancestors offsets. Therefore, we store a + counter (otick) inside every node and also the tree, by which we + remember the fact, that a node's path to the root has no offsets + applied (i.e. its values are up to date). This is the case if some + node's value differs from the tree's one, the later of which is + incremented whenever some node's offset has changed. +*/ + +static struct interval_node *interval_tree_validate(struct interval_tree *, struct interval_node *); +static void interval_generator_ensure_space(struct interval_generator *); +static bool interval_node_intersects(const struct interval_node *, ptrdiff_t, ptrdiff_t); +static int interval_tree_max_height(const struct interval_tree *); +static struct interval_stack *interval_stack_create(intmax_t); +static void interval_stack_destroy(struct interval_stack *); +static void interval_stack_clear(struct interval_stack *); +static void interval_stack_ensure_space(struct interval_stack *, intmax_t); +static void interval_stack_push(struct interval_stack *, struct interval_node *); +static void interval_stack_push_flagged(struct interval_stack *, struct interval_node *, bool); +static struct interval_node *interval_stack_pop(struct interval_stack *); +static void interval_tree_update_limit(const struct interval_tree *, struct interval_node *); +static void interval_tree_inherit_offset(const struct interval_tree *, struct interval_node *); +static void interval_tree_propagate_limit(const struct interval_tree *, struct interval_node *); +static void interval_tree_rotate_left(struct interval_tree *, struct interval_node *); +static void interval_tree_rotate_right(struct interval_tree *, struct interval_node *); +static void interval_tree_insert_fix(struct interval_tree *, struct interval_node *); +static void interval_tree_remove_fix(struct interval_tree *, struct interval_node *); +static void interval_tree_transplant(struct interval_tree *, struct interval_node *, struct interval_node *); +static struct interval_node *interval_tree_subtree_min(const struct interval_tree *, struct interval_node *); +static struct interval_generator* interval_generator_create (struct interval_tree *); +static void interval_generator_destroy (struct interval_generator *); +static void interval_generator_reset (struct interval_generator *, + ptrdiff_t, ptrdiff_t, + enum interval_tree_order); +static void +interval_generator_narrow (struct interval_generator *g, + ptrdiff_t begin, ptrdiff_t end); +static inline struct interval_node* +interval_generator_next (struct interval_generator *g); +static inline void interval_tree_iter_ensure_space(struct interval_tree *); + + + +/* +------------------------------------------------------------------------------------+ */ + +/* Simple dynamic array. */ +struct interval_stack +{ + struct interval_node **nodes; + size_t size; + size_t length; +}; + +/* State used when iterating interval. */ +struct interval_generator +{ + struct interval_tree *tree; + struct interval_stack *stack; + ptrdiff_t begin; + ptrdiff_t end; + enum interval_tree_order order; +}; + + + +/* +===================================================================================+ + * | Tree operations + * +===================================================================================+ */ + +/* Initialize an allocated node. */ + +void +interval_node_init (struct interval_node *node, + ptrdiff_t begin, ptrdiff_t end, + bool front_advance, bool rear_advance, + Lisp_Object data) +{ + node->begin = begin; + node->end = max (begin, end); + node->front_advance = front_advance; + node->rear_advance = rear_advance; + node->data = data; +} + +/* Return NODE's begin value, computing it if necessary. */ + +ptrdiff_t +interval_node_begin (struct interval_tree *tree, + struct interval_node *node) +{ + interval_tree_validate (tree, node); + return node->begin; +} + +/* Return NODE's end value, computing it if necessary. */ + +ptrdiff_t +interval_node_end (struct interval_tree *tree, + struct interval_node *node) +{ + interval_tree_validate (tree, node); + return node->end; +} + +/* Safely modify a node's interval. */ + +void +interval_node_set_region (struct interval_tree *tree, + struct interval_node *node, + ptrdiff_t begin, ptrdiff_t end) +{ + interval_tree_validate (tree, node); + if (begin != node->begin) + { + interval_tree_remove (tree, node); + node->begin = min (begin, PTRDIFF_MAX - 1); + node->end = max (node->begin, end); + interval_tree_insert (tree, node); + } + else if (end != node->end) + { + node->end = max (node->begin, end); + interval_tree_propagate_limit (tree, node); + } +} + +/* Allocate an interval_tree. Free with interval_tree_destroy. */ + +struct interval_tree* +interval_tree_create (void) +{ + struct interval_tree *tree = xmalloc (sizeof (*tree)); + interval_tree_clear (tree); + tree->iter = interval_generator_create (tree); + return tree; +} + +/* Reset the tree TREE to its empty state. */ + +void +interval_tree_clear (struct interval_tree *tree) +{ + struct interval_node *nil = &tree->nil; + nil->left = nil->right = nil->parent = nil; + nil->offset = nil->otick = 0; + nil->begin = PTRDIFF_MIN; + nil->end = PTRDIFF_MIN; + nil->limit = PTRDIFF_MIN; /* => max(x, nil.limit) = x */ + nil->color = ITREE_BLACK; + tree->root = nil; + tree->otick = 1; + tree->size = 0; + tree->iter_running = 0; +} + +#ifdef ITREE_TESTING +/* Initialize a pre-allocated tree (presumably on the stack). */ + +static void +interval_tree_init (struct interval_tree *tree) +{ + interval_tree_clear (tree); + tree->iter = interval_generator_create (tree); +} +#endif + +/* Release a tree, freeing its allocated memory. */ +void +interval_tree_destroy (struct interval_tree *tree) +{ + if (! tree) + return; + if (tree->iter) + interval_generator_destroy (tree->iter); + xfree (tree); +} + +/* Return the number of nodes in TREE. */ + +intmax_t +interval_tree_size (struct interval_tree *tree) +{ + return tree->size; +} + +/* Insert a NODE into the TREE. + + Note, that inserting a node twice results in undefined behaviour. +*/ + +void +interval_tree_insert (struct interval_tree *tree, struct interval_node *node) +{ + eassert (node && node->begin <= node->end && node != &tree->nil); + + struct interval_node *parent = &tree->nil; + struct interval_node *child = tree->root; + ptrdiff_t offset = 0; + + /* Find the insertion point, accumulate node's offset and update + ancestors limit values. */ + while (child != &tree->nil) + { + parent = child; + offset += child->offset; + child->limit = max (child->limit, node->end - offset); + /* This suggests that nodes in the right subtree are strictly + greater. But this is not true due to later rotations. */ + child = node->begin <= child->begin ? child->left : child->right; + } + + /* Insert the node */ + if (parent == &tree->nil) + tree->root = node; + else if (node->begin <= parent->begin) + parent->left = node; + else + parent->right = node; + + /* Init the node */ + node->parent = parent; + node->left = &tree->nil; + node->right = &tree->nil; + node->color = ITREE_RED; + node->offset = offset; + node->limit = node->end; + node->otick = tree->otick - 1; + + /* Fix/update the tree */ + ++tree->size; + interval_tree_insert_fix (tree, node); + interval_tree_iter_ensure_space (tree); +} + +/* Return true, if NODE is a member of TREE. */ + +bool +interval_tree_contains (struct interval_tree *tree, struct interval_node *node) +{ + struct interval_node *other; + + interval_tree_iter_start (tree, node->begin, PTRDIFF_MAX, ITREE_ASCENDING); + while ((other = interval_tree_iter_next (tree))) + if (other == node) + break; + + interval_tree_iter_finish (tree); + return other == node; +} + +/* Remove NODE from TREE and return it. NODE must exist in TREE.*/ + +struct interval_node* +interval_tree_remove (struct interval_tree *tree, struct interval_node *node) +{ + eassert (interval_tree_contains (tree, node)); + + struct interval_node *broken = NULL; + + interval_tree_inherit_offset (tree, node); + if (node->left == &tree->nil || node->right == &tree->nil) + { + struct interval_node *subst = + (node->right == &tree->nil) ? node->left : node->right; + if (node->color == ITREE_BLACK) + broken = subst; + interval_tree_transplant (tree, subst, node); + interval_tree_propagate_limit (tree, subst); + } + else + { + struct interval_node *min = interval_tree_subtree_min (tree, node->right); + struct interval_node *min_right = min->right; + + if (min->color == ITREE_BLACK) + broken = min->right; + if (min->parent == node) + min_right->parent = min; /* set parent, if min_right = nil */ + else + { + interval_tree_transplant (tree, min->right, min); + min->right = node->right; + min->right->parent = min; + } + interval_tree_inherit_offset (tree, min); + interval_tree_transplant (tree, min, node); + min->left = node->left; + min->left->parent = min; + min->color = node->color; + interval_tree_propagate_limit (tree, min_right); + interval_tree_propagate_limit (tree, min); + } + + if (broken) + interval_tree_remove_fix (tree, broken); + + node->right = node->left = node->parent = NULL; + --tree->size; + + eassert (tree->size == 0 || (tree->size > 0 && tree->root != &tree->nil)); + + return node; +} + +static struct interval_node* +interval_tree_validate (struct interval_tree *tree, struct interval_node *node) +{ + + if (tree->otick == node->otick || node == &tree->nil) + return node; + if (node != tree->root) + interval_tree_validate (tree, node->parent); + + interval_tree_inherit_offset (tree, node); + return node; +} + +/* Start a generator iterating all intervals in [BEGIN,END) in the + given ORDER. Only one iterator per tree can be running at any + time. +*/ + +void +interval_tree_iter_start (struct interval_tree *tree, + ptrdiff_t begin, ptrdiff_t end, + enum interval_tree_order order) +{ + if (tree->iter_running) + emacs_abort (); + interval_generator_reset (tree->iter, begin, end, order); + tree->iter_running = 1; +} + +/* Limit the search interval of the iterator to the given values. The + interval can only shrink, but never grow.*/ + +inline void +interval_tree_iter_narrow(struct interval_tree *tree, + ptrdiff_t begin, ptrdiff_t end) +{ + if (! tree->iter_running) + emacs_abort (); + interval_generator_narrow (tree->iter, begin, end); +} + +/* Stop using the iterator. */ + +void +interval_tree_iter_finish (struct interval_tree *tree) +{ + if (! tree->iter_running) + emacs_abort (); + tree->iter_running = 0; +} + +/* Return the next node of the iterator in the order given when it was + started; or NULL if there are no more nodes. */ + +inline struct interval_node* +interval_tree_iter_next (struct interval_tree *tree) +{ + if (! tree->iter_running) + emacs_abort (); + return interval_generator_next (tree->iter); +} + +/* Ensure that the tree's iterator does not need to allocate space + until the tree grows in size. */ + +static inline void +interval_tree_iter_ensure_space (struct interval_tree *tree) +{ + interval_generator_ensure_space (tree->iter); +} + +static int +interval_tree_max_height (const struct interval_tree *tree) +{ + return 2 * log (tree->size + 1) / log (2) + 0.5; +} + + +/* +===================================================================================+ + * | Insert/Delete Gaps + * +===================================================================================+ */ + +/* Insert a gap at POS of length LENGTH expanding all intervals + intersecting it, while respecting their rear_advance and + front_advance setting. */ + +void +interval_tree_insert_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t length) +{ + if (length <= 0 || tree->size == 0) + return; + + /* FIXME: Don't allocate generator/stack anew every time. */ + + /* Nodes with front_advance starting at pos may mess up the tree + order, so we need to remove them first. */ + struct interval_stack *saved = interval_stack_create (0); + struct interval_node *node = NULL; + interval_tree_iter_start (tree, pos, pos + 1, ITREE_PRE_ORDER); + while ((node = interval_tree_iter_next (tree))) + { + if (node->begin == pos && node->front_advance + && (node->begin != node->end || node->rear_advance)) + interval_stack_push (saved, node); + } + interval_tree_iter_finish (tree); + for (int i = 0; i < saved->length; ++i) + interval_tree_remove (tree, saved->nodes[i]); + + + /* We can't use a generator here, because we can't effectively + narrow AND shift some subtree at the same time. */ + const int size = interval_tree_max_height (tree) + 1; + struct interval_stack *stack = interval_stack_create (size); + interval_stack_push (stack, tree->root); + while ((node = interval_stack_pop (stack))) + { + /* Process in pre-order. */ + interval_tree_inherit_offset (tree, node); + if (node->right != &tree->nil) + { + if (node->begin > pos) + { + /* All nodes in this subtree are shifted by length. */ + node->right->offset += length; + ++tree->otick; + } + else + interval_stack_push (stack, node->right); + } + if (node->left != &tree->nil && pos <= node->left->limit) + interval_stack_push (stack, node->left); + + /* node->begin == pos implies no front-advance. */ + if (node->begin > pos) + node->begin += length; + if (node->end > pos || (node->end == pos && node->rear_advance)) + { + node->end += length; + interval_tree_propagate_limit (tree, node); + } + } + interval_stack_destroy (stack); + + /* Reinsert nodes starting at POS having front-advance. */ + while ((node = interval_stack_pop (saved))) + { + node->begin += length; + if (node->end != pos || node->rear_advance) + node->end += length; + interval_tree_insert (tree, node); + } + + interval_stack_destroy (saved); +} + +/* Delete a gap at POS of length LENGTH, contracting all intervals + intersecting it. */ + +void +interval_tree_delete_gap (struct interval_tree *tree, ptrdiff_t pos, ptrdiff_t length) +{ + if (length <= 0 || tree->size == 0) + return; + + /* FIXME: Don't allocate stack anew every time. */ + + /* Can't use the generator here, because by decrementing begin, we + might unintentionally bring shifted nodes back into our search + space. */ + const int size = interval_tree_max_height (tree) + 1; + struct interval_stack *stack = interval_stack_create (size); + struct interval_node *node; + + interval_stack_push (stack, tree->root); + while ((node = interval_stack_pop (stack))) + { + interval_tree_inherit_offset (tree, node); + if (node->right != &tree->nil) + { + if (node->begin > pos + length) + { + /* Shift right subtree to the left. */ + node->right->offset -= length; + ++tree->otick; + } + else + interval_stack_push (stack, node->right); + } + if (node->left != &tree->nil && pos <= node->left->limit) + interval_stack_push (stack, node->left); + + if (pos < node->begin) + node->begin = max (pos, node->begin - length); + if (node->end > pos) + { + node->end = max (pos , node->end - length); + interval_tree_propagate_limit (tree, node); + } + } + interval_stack_destroy (stack); +} + + + +/* +===================================================================================+ + * | Generator + * +===================================================================================+ */ + +/* Allocate a new generator for TREE. */ + +static struct interval_generator* +interval_generator_create (struct interval_tree *tree) +{ + struct interval_generator *g = xmalloc (sizeof *g); + const int size = interval_tree_max_height (tree) + 1; + + g->stack = interval_stack_create (size); + g->tree = tree; + interval_generator_reset (g, 1, 0, 0); + return g; +} + +/* Reset generator G such that it iterates over intervals intersecting + with [BEGIN, END) in the given ORDER. */ + +void +interval_generator_reset (struct interval_generator *g, + ptrdiff_t begin, ptrdiff_t end, + enum interval_tree_order order) +{ + if (! g) return; + + g->begin = begin; + g->end = end; + g->order = order; + interval_stack_clear (g->stack); + if (begin <= end && g->tree->size > 0) + interval_stack_push_flagged (g->stack, g->tree->root, false); +} + +/* Allocate enough space for the tree of G in its current shape. */ + +static inline void +interval_generator_ensure_space (struct interval_generator *g) +{ + interval_stack_ensure_space (g->stack, interval_tree_max_height (g->tree) + 1); +} + +/* Return true, if NODE's interval intersects with [BEGIN, END). */ + +static inline bool +interval_node_intersects (const struct interval_node *node, + ptrdiff_t begin, ptrdiff_t end) +{ + return (begin < node->end && node->begin < end) + || (node->begin == node->end && begin == node->begin); +} + +/* Return the next node of G, or NULL if there is none. */ + +inline struct interval_node* +interval_generator_next (struct interval_generator *g) +{ + if (! g) return NULL; + + struct interval_node * const nil = &g->tree->nil; + struct interval_node *node; + + do { + node = interval_stack_pop (g->stack); + + while (node && ! node->visited) + { + struct interval_node * const left = node->left; + struct interval_node * const right = node->right; + + interval_tree_inherit_offset (g->tree, node); + switch (g->order) + { + case ITREE_ASCENDING: + if (right != nil && node->begin <= g->end) + interval_stack_push_flagged (g->stack, right, false); + if (interval_node_intersects (node, g->begin, g->end)) + interval_stack_push_flagged (g->stack, node, true); + /* Node's children may still be off-set and we need to add it. */ + if (left != nil && g->begin <= left->limit + left->offset) + interval_stack_push_flagged (g->stack, left, false); + break; + case ITREE_DESCENDING: + if (left != nil && g->begin <= left->limit + left->offset) + interval_stack_push_flagged (g->stack, left, false); + if (interval_node_intersects (node, g->begin, g->end)) + interval_stack_push_flagged (g->stack, node, true); + if (right != nil && node->begin <= g->end) + interval_stack_push_flagged (g->stack, right, false); + break; + case ITREE_PRE_ORDER: + if (right != nil && node->begin <= g->end) + interval_stack_push_flagged (g->stack, right, false); + if (left != nil && g->begin <= left->limit + left->offset) + interval_stack_push_flagged (g->stack, left, false); + if (interval_node_intersects (node, g->begin, g->end)) + interval_stack_push_flagged (g->stack, node, true); + break; + } + node = interval_stack_pop (g->stack); + } + /* Node may have been invalidated by interval_generator_narrow + after it was pushed: Check if it still intersects. */ + } while (node && ! interval_node_intersects (node, g->begin, g->end)); + + return node; +} + +/* Limit G to the new interval [BEGIN, END), which must be a subset of + the current one. I.E. it can't grow on either side. */ + +static inline void +interval_generator_narrow (struct interval_generator *g, + ptrdiff_t begin, ptrdiff_t end) +{ + g->begin = max (begin, g->begin); + g->end = min (end, g->end); +} + +/* Free the memory allocated for G. */ + +void +interval_generator_destroy (struct interval_generator *g) +{ + if (! g) return; + if (g->stack) + interval_stack_destroy (g->stack); + xfree (g); +} + + +/* +===================================================================================+ + * | Stack + * +===================================================================================+ */ + +/* This is just a simple dynamic array with stack semantics. */ + +static struct interval_stack* +interval_stack_create (intmax_t initial_size) +{ + struct interval_stack *stack = xmalloc (sizeof (struct interval_stack)); + stack->size = max (0, initial_size); + stack->nodes = xmalloc (stack->size * sizeof (struct interval_node*)); + stack->length = 0; + return stack; +} + +static void +interval_stack_destroy (struct interval_stack *stack) +{ + if (! stack) + return; + if (stack->nodes) + xfree (stack->nodes); + xfree (stack); +} + +static void +interval_stack_clear (struct interval_stack *stack) +{ + stack->length = 0; +} + +static inline void +interval_stack_ensure_space (struct interval_stack *stack, intmax_t nelements) +{ + if (nelements > stack->size) + { + stack->size = (nelements + 1) * 2; + stack->nodes = xrealloc (stack->nodes, stack->size * sizeof (*stack->nodes)); + } +} + +static inline void +interval_stack_push (struct interval_stack *stack, struct interval_node *node) +{ + interval_stack_ensure_space (stack, stack->length + 1); + stack->nodes[stack->length] = node; + stack->length++; +} + +/* Push NODE on the STACK, while settings its visited flag to FLAG. */ + +static inline void +interval_stack_push_flagged (struct interval_stack *stack, + struct interval_node *node, bool flag) +{ + interval_stack_push (stack, node); + node->visited = flag; +} + +static inline struct interval_node* +interval_stack_pop (struct interval_stack *stack) +{ + if (stack->length == 0) + return NULL; + return stack->nodes[--stack->length]; +} + + +/* +===================================================================================+ + * | Internal Functions + * +===================================================================================+ */ + +/* Update NODE's limit attribute according to its children. */ + +static void +interval_tree_update_limit (const struct interval_tree *tree, + struct interval_node *node) +{ + if (node == &tree->nil) + return; + + node->limit = max (node->end, max (node->left->limit + node->left->offset, + node->right->limit + node->right->offset)); +} + +/* Apply NODE's offset to its begin, end and limit values and + propagate it to its children. + + Does nothing, if NODE is clean, i.e. NODE.otick = tree.otick . +*/ + +static void +interval_tree_inherit_offset (const struct interval_tree *tree, + struct interval_node *node) +{ + + if (node->otick == tree->otick) + return; + + node->begin += node->offset; + node->end += node->offset; + node->limit += node->offset; + if (node->left != &tree->nil) + node->left->offset += node->offset; + if (node->right != &tree->nil) + node->right->offset += node->offset; + node->offset = 0; + if (node == tree->root || node->parent->otick == tree->otick) + node->otick = tree->otick; +} + +/* Update limit of NODE and its ancestors. Stop when it becomes + stable, i.e. new_limit = old_limit. + + NODE may also be the nil node, in which case its parent is + used. (This feature is due to the RB algorithm.) +*/ + +static void +interval_tree_propagate_limit (const struct interval_tree *tree, + struct interval_node *node) +{ + if (node == &tree->nil) + node = node->parent; + if (node == &tree->nil) + return; + + while (1) { + ptrdiff_t newlimit = max (node->end, max (node->left->limit + node->left->offset, + node->right->limit + node->right->offset)); + if (newlimit == node->limit) + break; + node->limit = newlimit; + if (node == tree->root) + break; + node = node->parent; + } +} + +/* Perform the familiar left-rotation on node NODE. */ + +static void +interval_tree_rotate_left (struct interval_tree *tree, struct interval_node *node) +{ + eassert (node->right != &tree->nil); + + struct interval_node *right = node->right; + + interval_tree_inherit_offset (tree, node); + interval_tree_inherit_offset (tree, right); + + /* Turn right's left subtree into node's right subtree. */ + node->right = right->left; + if (right->left != &tree->nil) + right->left->parent = node; + + /* right's parent was node's parent. */ + if (right != &tree->nil) + right->parent = node->parent; + + /* Get the parent to point to right instead of node. */ + if (node != tree->root) + { + if (node == node->parent->left) + node->parent->left = right; + else + node->parent->right = right; + } + else + tree->root = right; + + /* Put node on right's left. */ + right->left = node; + if (node != &tree->nil) + node->parent = right; + + /* Order matters here. */ + interval_tree_update_limit (tree, node); + interval_tree_update_limit (tree, right); +} + +/* Perform the familiar right-rotation on node NODE. */ + +static void +interval_tree_rotate_right (struct interval_tree *tree, struct interval_node *node) +{ + eassert (tree && node && node->left != &tree->nil); + + struct interval_node *left = node->left; + + interval_tree_inherit_offset (tree, node); + interval_tree_inherit_offset (tree, left); + + node->left = left->right; + if (left->right != &tree->nil) + left->right->parent = node; + + if (left != &tree->nil) + left->parent = node->parent; + if (node != tree->root) + { + if (node == node->parent->right) + node->parent->right = left; + else + node->parent->left = left; + } + else + tree->root = left; + + left->right = node; + if (node != &tree->nil) + node->parent = left; + + interval_tree_update_limit (tree, left); + interval_tree_update_limit (tree, node); +} + +/* Repair the tree after an insertion. Part of the RB-Tree + algorithm. */ + +static void +interval_tree_insert_fix (struct interval_tree *tree, struct interval_node *node) +{ + while (node->parent->color == ITREE_RED) + { + /* NODE is red and its parent is red. This is a violation of + red-black tree property #3. */ + + if (node->parent == node->parent->parent->left) + { + /* We're on the left side of our grandparent, and OTHER is + our "uncle". */ + struct interval_node *uncle = node->parent->parent->right; + + if (uncle->color == ITREE_RED) /* case 1.a */ + { + /* Uncle and parent are red but should be black because + NODE is red. Change the colors accordingly and + proceed with the grandparent. */ + node->parent->color = ITREE_BLACK; + uncle->color = ITREE_BLACK; + node->parent->parent->color = ITREE_RED; + node = node->parent->parent; + } + else + { + /* Parent and uncle have different colors; parent is + red, uncle is black. */ + if (node == node->parent->right) /* case 2.a */ + { + node = node->parent; + interval_tree_rotate_left (tree, node); + } + /* case 3.a */ + node->parent->color = ITREE_BLACK; + node->parent->parent->color = ITREE_RED; + interval_tree_rotate_right (tree, node->parent->parent); + } + } + else + { + /* This is the symmetrical case of above. */ + struct interval_node *uncle = node->parent->parent->left; + + if (uncle->color == ITREE_RED) /* case 1.b */ + { + node->parent->color = ITREE_BLACK; + uncle->color = ITREE_BLACK; + node->parent->parent->color = ITREE_RED; + node = node->parent->parent; + } + else + { + if (node == node->parent->left) /* case 2.b */ + { + node = node->parent; + interval_tree_rotate_right (tree, node); + } + /* case 3.b */ + node->parent->color = ITREE_BLACK; + node->parent->parent->color = ITREE_RED; + interval_tree_rotate_left (tree, node->parent->parent); + } + } + } + + /* The root may have been changed to red due to the algorithm. Set + it to black so that property #5 is satisfied. */ + tree->root->color = ITREE_BLACK; +} + +/* Repair the tree after a deletion. Part of the RB-Tree + algorithm. */ + +static void +interval_tree_remove_fix (struct interval_tree *tree, struct interval_node *node) +{ + while (node != tree->root && node->color == ITREE_BLACK) + { + if (node == node->parent->left) + { + struct interval_node *other = node->parent->right; + + if (other->color == ITREE_RED) /* case 1.a */ + { + other->color = ITREE_BLACK; + node->parent->color = ITREE_RED; + interval_tree_rotate_left (tree, node->parent); + other = node->parent->right; + } + + if (other->left->color == ITREE_BLACK /* 2.a */ + && other->right->color == ITREE_BLACK) + { + other->color = ITREE_RED; + node = node->parent; + } + else + { + if (other->right->color == ITREE_BLACK) /* 3.a */ + { + other->left->color = ITREE_BLACK; + other->color = ITREE_RED; + interval_tree_rotate_right (tree, other); + other = node->parent->right; + } + other->color = node->parent->color; /* 4.a */ + node->parent->color = ITREE_BLACK; + other->right->color = ITREE_BLACK; + interval_tree_rotate_left (tree, node->parent); + node = tree->root; + } + } + else + { + struct interval_node *other = node->parent->left; + + if (other->color == ITREE_RED) /* 1.b */ + { + other->color = ITREE_BLACK; + node->parent->color = ITREE_RED; + interval_tree_rotate_right (tree, node->parent); + other = node->parent->left; + } + + if (other->right->color == ITREE_BLACK /* 2.b */ + && other->left->color == ITREE_BLACK) + { + other->color = ITREE_RED; + node = node->parent; + } + else + { + if (other->left->color == ITREE_BLACK) /* 3.b */ + { + other->right->color = ITREE_BLACK; + other->color = ITREE_RED; + interval_tree_rotate_left (tree, other); + other = node->parent->left; + } + + other->color = node->parent->color; /* 4.b */ + node->parent->color = ITREE_BLACK; + other->left->color = ITREE_BLACK; + interval_tree_rotate_right (tree, node->parent); + node = tree->root; + } + } + } + + node->color = ITREE_BLACK; +} + +/* Link node SOURCE in DEST's place. */ + +static void +interval_tree_transplant (struct interval_tree *tree, struct interval_node *source, + struct interval_node *dest) +{ + eassert (tree && source && dest && dest != &tree->nil); + + if (dest == tree->root) + tree->root = source; + else if (dest == dest->parent->left) + dest->parent->left = source; + else + dest->parent->right = source; + + source->parent = dest->parent; +} + + +static struct interval_node* +interval_tree_subtree_min (const struct interval_tree *tree, struct interval_node *node) +{ + if (node == &tree->nil) + return node; + while (node->left != &tree->nil) + node = node->left; + return node; +} + + +/* +===================================================================================+ + * | Debugging + * +===================================================================================+ */ + +/* See Foverlay_tree in buffer.c */ diff --git a/src/itree.h b/src/itree.h new file mode 100644 index 0000000000..d35c5afc24 --- /dev/null +++ b/src/itree.h @@ -0,0 +1,88 @@ +/* This file implements an efficient interval data-structure. + +Copyright (C) 2017 Andreas Politz (politza@hochschule-trier.de) + +This file is not part of GNU Emacs. + +GNU Emacs is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or (at +your option) any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs. If not, see . */ + +#ifndef ITREE_H +#define ITREE_H +#include +#include +#include + +/* The tree and node structs are mainly here, so they can be allocated. + + NOTE: The only time where it is safe to modify node.begin and + node.end directly, is while the node is not part of any tree. + + NOTE: It is safe to read node.begin and node.end directly, if the + node came from a generator, because it validates the nodes it + returns as a side-effect. +*/ + +struct interval_node; +struct interval_node +{ + enum { ITREE_RED, ITREE_BLACK } color; + struct interval_node *parent; + struct interval_node *left; + struct interval_node *right; + ptrdiff_t begin; /* The beginning of this interval. */ + ptrdiff_t end; /* The end of the interval. */ + ptrdiff_t limit; /* The maximum end in this subtree. */ + ptrdiff_t offset; /* The amount of shift to apply to this subtree. */ + uintmax_t otick; /* offset modified tick */ + Lisp_Object data; /* Exclusively used by the client. */ + bool_bf visited; /* For traversal via generator. */ + bool_bf rear_advance : 1; /* Same as for marker and overlays. */ + bool_bf front_advance : 1; /* Same as for marker and overlays. */ +}; + +struct interval_tree +{ + struct interval_node *root; + struct interval_node nil; /* The tree's version of NULL. */ + uintmax_t otick; /* offset tick, compared with node's otick. */ + intmax_t size; /* Number of nodes in the tree. */ + struct interval_generator *iter; + bool_bf iter_running; +}; + +enum interval_tree_order { + ITREE_ASCENDING = 0, + ITREE_DEFLT_ORDER = 0, + ITREE_DESCENDING, + ITREE_PRE_ORDER, +}; + +void interval_node_init(struct interval_node *, ptrdiff_t, ptrdiff_t, bool, bool, Lisp_Object); +ptrdiff_t interval_node_begin(struct interval_tree *, struct interval_node *); +ptrdiff_t interval_node_end(struct interval_tree *, struct interval_node *); +void interval_node_set_region(struct interval_tree *, struct interval_node *, ptrdiff_t, ptrdiff_t); +struct interval_tree *interval_tree_create(void); +void interval_tree_destroy(struct interval_tree *); +intmax_t interval_tree_size(struct interval_tree *); +void interval_tree_clear(struct interval_tree *); +void interval_tree_insert(struct interval_tree *, struct interval_node *); +bool interval_tree_contains(struct interval_tree *, struct interval_node *); +struct interval_node *interval_tree_remove(struct interval_tree *, struct interval_node *); +void interval_tree_iter_start(struct interval_tree *, ptrdiff_t, ptrdiff_t, enum interval_tree_order); +void interval_tree_iter_narrow(struct interval_tree *, ptrdiff_t, ptrdiff_t); +void interval_tree_iter_finish(struct interval_tree *); +struct interval_node *interval_tree_iter_next(struct interval_tree *); +void interval_tree_insert_gap(struct interval_tree *, ptrdiff_t, ptrdiff_t); +void interval_tree_delete_gap(struct interval_tree *, ptrdiff_t, ptrdiff_t); +#endif diff --git a/src/keyboard.c b/src/keyboard.c index e8701b8870..60cdaba9f0 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -1668,8 +1668,8 @@ adjust_point_for_property (ptrdiff_t last_pt, bool modified) && display_prop_intangible_p (val, overlay, PT, PT_BYTE) && (!OVERLAYP (overlay) ? get_property_and_range (PT, Qdisplay, &val, &beg, &end, Qnil) - : (beg = OVERLAY_POSITION (OVERLAY_START (overlay)), - end = OVERLAY_POSITION (OVERLAY_END (overlay)))) + : (beg = OVERLAY_START (overlay), + end = OVERLAY_END (overlay))) && (beg < PT /* && end > PT <- It's always the case. */ || (beg <= PT && STRINGP (val) && SCHARS (val) == 0))) { diff --git a/src/lisp.h b/src/lisp.h index 680c25d4c4..222a99950a 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -2217,15 +2217,14 @@ struct Lisp_Overlay - next fields of start and end markers (singly linked list of markers). I.e. 9words plus 2 bits, 3words of which are for external linked lists. */ - { - ENUM_BF (Lisp_Misc_Type) type : 16; /* = Lisp_Misc_Overlay */ - bool_bf gcmarkbit : 1; - unsigned spacer : 15; - struct Lisp_Overlay *next; - Lisp_Object start; - Lisp_Object end; - Lisp_Object plist; - }; +{ + ENUM_BF (Lisp_Misc_Type) type : 16; /* = Lisp_Misc_Overlay */ + bool_bf gcmarkbit : 1; + unsigned spacer : 15; + Lisp_Object plist; + struct buffer *buffer; /* eassert (live buffer || NULL). */ + struct interval_node *interval; +}; /* Number of bits needed to store one of the values SAVE_UNUSED..SAVE_OBJECT. */ @@ -3704,7 +3703,7 @@ extern Lisp_Object make_save_funcptr_ptr_obj (void (*) (void), void *, Lisp_Object); extern Lisp_Object make_save_memory (Lisp_Object *, ptrdiff_t); extern void free_save_value (Lisp_Object); -extern Lisp_Object build_overlay (Lisp_Object, Lisp_Object, Lisp_Object); +extern Lisp_Object build_overlay (ptrdiff_t, ptrdiff_t, bool, bool, Lisp_Object); extern void free_marker (Lisp_Object); extern void free_cons (struct Lisp_Cons *); extern void init_alloc_once (void); diff --git a/src/print.c b/src/print.c index f280616af8..a07baa3067 100644 --- a/src/print.c +++ b/src/print.c @@ -548,8 +548,7 @@ temp_output_buffer_setup (const char *bufname) bset_read_only (current_buffer, Qnil); bset_filename (current_buffer, Qnil); bset_undo_list (current_buffer, Qt); - eassert (current_buffer->overlays_before == NULL); - eassert (current_buffer->overlays_after == NULL); + eassert (current_buffer->overlays == NULL); bset_enable_multibyte_characters (current_buffer, BVAR (&buffer_defaults, enable_multibyte_characters)); specbind (Qinhibit_read_only, Qt); @@ -2074,7 +2073,7 @@ print_object (Lisp_Object obj, Lisp_Object printcharfun, bool escapeflag) obj = XCDR (obj); if (!(i & 1)) halftail = XCDR (halftail); - } + } /* OBJ non-nil here means it's the end of a dotted list. */ if (!NILP (obj)) @@ -2114,15 +2113,14 @@ print_object (Lisp_Object obj, Lisp_Object printcharfun, bool escapeflag) case Lisp_Misc_Overlay: print_c_string ("#buffer) + if (! OVERLAY_BUFFER (obj)) print_c_string ("in no buffer", printcharfun); else { int len = sprintf (buf, "from %"pD"d to %"pD"d in ", - marker_position (OVERLAY_START (obj)), - marker_position (OVERLAY_END (obj))); + OVERLAY_START (obj), OVERLAY_END (obj)); strout (buf, len, len, printcharfun); - print_string (BVAR (XMARKER (OVERLAY_START (obj))->buffer, name), + print_string (BVAR (OVERLAY_BUFFER (obj), name), printcharfun); } printchar ('>', printcharfun); diff --git a/src/textprop.c b/src/textprop.c index 513780c300..aebb6524e6 100644 --- a/src/textprop.c +++ b/src/textprop.c @@ -617,36 +617,42 @@ get_char_property_and_overlay (Lisp_Object position, register Lisp_Object prop, } if (BUFFERP (object)) { - ptrdiff_t noverlays; - Lisp_Object *overlay_vec; - struct buffer *obuf = current_buffer; + struct buffer *b = XBUFFER (object); + struct interval_node *node; + struct sortvec items[2]; + struct sortvec *result = NULL; + Lisp_Object result_tem = Qnil; - if (XINT (position) < BUF_BEGV (XBUFFER (object)) - || XINT (position) > BUF_ZV (XBUFFER (object))) + if (XINT (position) < BUF_BEGV (b) || XINT (position) > BUF_ZV (b)) xsignal1 (Qargs_out_of_range, position); - set_buffer_temp (XBUFFER (object)); - - USE_SAFE_ALLOCA; - GET_OVERLAYS_AT (XINT (position), overlay_vec, noverlays, NULL, false); - noverlays = sort_overlays (overlay_vec, noverlays, w); - - set_buffer_temp (obuf); + buffer_overlay_iter_start(b, XINT (position), XINT (position) + 1, + ITREE_ASCENDING); /* Now check the overlays in order of decreasing priority. */ - while (--noverlays >= 0) + while ((node = buffer_overlay_iter_next (b))) { - Lisp_Object tem = Foverlay_get (overlay_vec[noverlays], prop); - if (!NILP (tem)) - { - if (overlay) - /* Return the overlay we got the property from. */ - *overlay = overlay_vec[noverlays]; - SAFE_FREE (); - return tem; - } + Lisp_Object tem = Foverlay_get (node->data, prop); + struct sortvec *this; + + if (NILP (tem) || (w && ! overlay_matches_window (w, node->data))) + continue; + + this = (result == items ? items + 1 : items); + make_sortvec_item (this, node->data); + if (! result || (compare_overlays (result, this) < 0)) + { + result = this; + result_tem = tem; + } } - SAFE_FREE (); + buffer_overlay_iter_finish (b); + if (result) + { + if (overlay) + *overlay = result->overlay; + return result_tem; + } } if (overlay) diff --git a/src/window.h b/src/window.h index df7c23f824..324d30b57f 100644 --- a/src/window.h +++ b/src/window.h @@ -1128,6 +1128,16 @@ output_cursor_to (struct window *w, int vpos, int hpos, int y, int x) w->output_cursor.y = y; } +/* Return true, if overlay OV's properties should have an effect in + window W. */ +INLINE bool +overlay_matches_window (const struct window *w, Lisp_Object ov) +{ + eassert (OVERLAYP (ov)); + Lisp_Object window = Foverlay_get (ov, Qwindow); + return (! WINDOWP (window) || XWINDOW (window) == w); +} + INLINE_HEADER_END #endif /* not WINDOW_H_INCLUDED */ diff --git a/src/xdisp.c b/src/xdisp.c index 86164eb9f6..b3b9ecae37 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -873,7 +873,6 @@ static enum move_it_result static void get_visually_first_element (struct it *); static void compute_stop_pos (struct it *); static int face_before_or_after_it_pos (struct it *, bool); -static ptrdiff_t next_overlay_change (ptrdiff_t); static int handle_display_spec (struct it *, Lisp_Object, Lisp_Object, Lisp_Object, struct text_pos *, ptrdiff_t, bool); static int handle_single_display_spec (struct it *, Lisp_Object, Lisp_Object, @@ -3606,39 +3605,6 @@ compute_stop_pos (struct it *it) && it->stop_charpos >= IT_CHARPOS (*it))); } - -/* Return the position of the next overlay change after POS in - current_buffer. Value is point-max if no overlay change - follows. This is like `next-overlay-change' but doesn't use - xmalloc. */ - -static ptrdiff_t -next_overlay_change (ptrdiff_t pos) -{ - ptrdiff_t i, noverlays; - ptrdiff_t endpos; - Lisp_Object *overlays; - USE_SAFE_ALLOCA; - - /* Get all overlays at the given position. */ - GET_OVERLAYS_AT (pos, overlays, noverlays, &endpos, true); - - /* If any of these overlays ends before endpos, - use its ending point instead. */ - for (i = 0; i < noverlays; ++i) - { - Lisp_Object oend; - ptrdiff_t oendpos; - - oend = OVERLAY_END (overlays[i]); - oendpos = OVERLAY_POSITION (oend); - endpos = min (endpos, oendpos); - } - - SAFE_FREE (); - return endpos; -} - /* How many characters forward to search for a display property or display string. Searching too far forward makes the bidi display sluggish, especially in small windows. */ @@ -5071,7 +5037,7 @@ handle_single_display_spec (struct it *it, Lisp_Object spec, Lisp_Object object, overlay's display string/image twice. */ if (!NILP (overlay)) { - ptrdiff_t ovendpos = OVERLAY_POSITION (OVERLAY_END (overlay)); + ptrdiff_t ovendpos = OVERLAY_END (overlay); /* Some borderline-sane Lisp might call us with the current buffer narrowed so that overlay-end is outside the @@ -5785,13 +5751,14 @@ static void load_overlay_strings (struct it *it, ptrdiff_t charpos) { Lisp_Object overlay, window, str, invisible; - struct Lisp_Overlay *ov; ptrdiff_t start, end; ptrdiff_t n = 0, i, j; int invis; struct overlay_entry entriesbuf[20]; ptrdiff_t size = ARRAYELTS (entriesbuf); struct overlay_entry *entries = entriesbuf; + struct interval_node *node; + USE_SAFE_ALLOCA; if (charpos <= 0) @@ -5823,83 +5790,47 @@ load_overlay_strings (struct it *it, ptrdiff_t charpos) } \ while (false) - /* Process overlay before the overlay center. */ - for (ov = current_buffer->overlays_before; ov; ov = ov->next) + if (current_buffer->overlays) { - XSETMISC (overlay, ov); - eassert (OVERLAYP (overlay)); - start = OVERLAY_POSITION (OVERLAY_START (overlay)); - end = OVERLAY_POSITION (OVERLAY_END (overlay)); - - if (end < charpos) - break; - - /* Skip this overlay if it doesn't start or end at IT's current - position. */ - if (end != charpos && start != charpos) - continue; - - /* Skip this overlay if it doesn't apply to IT->w. */ - window = Foverlay_get (overlay, Qwindow); - if (WINDOWP (window) && XWINDOW (window) != it->w) - continue; - - /* If the text ``under'' the overlay is invisible, both before- - and after-strings from this overlay are visible; start and - end position are indistinguishable. */ - invisible = Foverlay_get (overlay, Qinvisible); - invis = TEXT_PROP_MEANS_INVISIBLE (invisible); - - /* If overlay has a non-empty before-string, record it. */ - if ((start == charpos || (end == charpos && invis != 0)) - && (str = Foverlay_get (overlay, Qbefore_string), STRINGP (str)) - && SCHARS (str)) - RECORD_OVERLAY_STRING (overlay, str, false); - - /* If overlay has a non-empty after-string, record it. */ - if ((end == charpos || (start == charpos && invis != 0)) - && (str = Foverlay_get (overlay, Qafter_string), STRINGP (str)) - && SCHARS (str)) - RECORD_OVERLAY_STRING (overlay, str, true); - } - - /* Process overlays after the overlay center. */ - for (ov = current_buffer->overlays_after; ov; ov = ov->next) - { - XSETMISC (overlay, ov); - eassert (OVERLAYP (overlay)); - start = OVERLAY_POSITION (OVERLAY_START (overlay)); - end = OVERLAY_POSITION (OVERLAY_END (overlay)); - - if (start > charpos) - break; - - /* Skip this overlay if it doesn't start or end at IT's current - position. */ - if (end != charpos && start != charpos) - continue; - - /* Skip this overlay if it doesn't apply to IT->w. */ - window = Foverlay_get (overlay, Qwindow); - if (WINDOWP (window) && XWINDOW (window) != it->w) - continue; - - /* If the text ``under'' the overlay is invisible, it has a zero - dimension, and both before- and after-strings apply. */ - invisible = Foverlay_get (overlay, Qinvisible); - invis = TEXT_PROP_MEANS_INVISIBLE (invisible); - - /* If overlay has a non-empty before-string, record it. */ - if ((start == charpos || (end == charpos && invis != 0)) - && (str = Foverlay_get (overlay, Qbefore_string), STRINGP (str)) - && SCHARS (str)) - RECORD_OVERLAY_STRING (overlay, str, false); - - /* If overlay has a non-empty after-string, record it. */ - if ((end == charpos || (start == charpos && invis != 0)) - && (str = Foverlay_get (overlay, Qafter_string), STRINGP (str)) - && SCHARS (str)) - RECORD_OVERLAY_STRING (overlay, str, true); + buffer_overlay_iter_start (current_buffer, + charpos - 1, charpos + 1, ITREE_DESCENDING); + /* Process overlays. */ + while ((node = buffer_overlay_iter_next (current_buffer))) + { + overlay = node->data; + eassert (OVERLAYP (overlay)); + start = node->begin; + end = node->end; + + /* Skip this overlay if it doesn't start or end at IT's current + position. */ + if (end != charpos && start != charpos) + continue; + + /* Skip this overlay if it doesn't apply to IT->w. */ + window = Foverlay_get (overlay, Qwindow); + if (WINDOWP (window) && XWINDOW (window) != it->w) + continue; + + /* If the text ``under'' the overlay is invisible, both before- + and after-strings from this overlay are visible; start and + end position are indistinguishable. */ + invisible = Foverlay_get (overlay, Qinvisible); + invis = TEXT_PROP_MEANS_INVISIBLE (invisible); + + /* If overlay has a non-empty before-string, record it. */ + if ((start == charpos || (end == charpos && invis != 0)) + && (str = Foverlay_get (overlay, Qbefore_string), STRINGP (str)) + && SCHARS (str)) + RECORD_OVERLAY_STRING (overlay, str, false); + + /* If overlay has a non-empty after-string, record it. */ + if ((end == charpos || (start == charpos && invis != 0)) + && (str = Foverlay_get (overlay, Qafter_string), STRINGP (str)) + && SCHARS (str)) + RECORD_OVERLAY_STRING (overlay, str, true); + } + buffer_overlay_iter_finish (current_buffer); } #undef RECORD_OVERLAY_STRING @@ -6463,7 +6394,7 @@ back_to_previous_visible_line_start (struct it *it) && !NILP (val = get_char_property_and_overlay (make_number (pos), Qdisplay, Qnil, &overlay)) && (OVERLAYP (overlay) - ? (beg = OVERLAY_POSITION (OVERLAY_START (overlay))) + ? (beg = OVERLAY_START (overlay)) : get_property_and_range (pos, Qdisplay, &val, &beg, &end, Qnil))) { RESTORE_IT (it, it, it2data); @@ -9568,7 +9499,6 @@ move_it_to (struct it *it, ptrdiff_t to_charpos, int to_x, int to_y, int to_vpos } /* Reset/increment for the next run. */ - recenter_overlay_lists (current_buffer, IT_CHARPOS (*it)); it->current_x = line_start_x; line_start_x = 0; it->hpos = 0; @@ -21212,13 +21142,6 @@ display_line (struct it *it, int cursor_vpos) row->starts_in_middle_of_char_p = it->starts_in_middle_of_char_p; it->starts_in_middle_of_char_p = false; - /* Arrange the overlays nicely for our purposes. Usually, we call - display_line on only one line at a time, in which case this - can't really hurt too much, or we call it on lines which appear - one after another in the buffer, in which case all calls to - recenter_overlay_lists but the first will be pretty cheap. */ - recenter_overlay_lists (current_buffer, IT_CHARPOS (*it)); - /* If we are going to display the cursor's line, account for the hscroll of that line. We subtract the window's min_hscroll, because that was already accounted for in init_iterator. */ @@ -31212,7 +31135,7 @@ note_mouse_highlight (struct frame *f, int x, int y) if (BUFFERP (object)) { /* Put all the overlays we want in a vector in overlay_vec. */ - GET_OVERLAYS_AT (pos, overlay_vec, noverlays, NULL, false); + GET_OVERLAYS_AT (pos, overlay_vec, noverlays, NULL); /* Sort overlays into increasing priority order. */ noverlays = sort_overlays (overlay_vec, noverlays, w); } diff --git a/src/xfaces.c b/src/xfaces.c index b309c16127..b1788725eb 100644 --- a/src/xfaces.c +++ b/src/xfaces.c @@ -5931,8 +5931,7 @@ face_at_buffer_position (struct window *w, ptrdiff_t pos, USE_SAFE_ALLOCA; { ptrdiff_t next_overlay; - - GET_OVERLAYS_AT (pos, overlay_vec, noverlays, &next_overlay, false); + GET_OVERLAYS_AT (pos, overlay_vec, noverlays, &next_overlay); if (next_overlay < endpos) endpos = next_overlay; } @@ -5975,7 +5974,6 @@ face_at_buffer_position (struct window *w, ptrdiff_t pos, { for (prop = Qnil, i = noverlays - 1; i >= 0 && NILP (prop); --i) { - Lisp_Object oend; ptrdiff_t oendpos; prop = Foverlay_get (overlay_vec[i], propname); @@ -5988,8 +5986,7 @@ face_at_buffer_position (struct window *w, ptrdiff_t pos, merge_face_ref (f, prop, attrs, true, 0); } - oend = OVERLAY_END (overlay_vec[i]); - oendpos = OVERLAY_POSITION (oend); + oendpos = OVERLAY_END (overlay_vec[i]); if (oendpos < endpos) endpos = oendpos; } @@ -5998,18 +5995,16 @@ face_at_buffer_position (struct window *w, ptrdiff_t pos, { for (i = 0; i < noverlays; i++) { - Lisp_Object oend; ptrdiff_t oendpos; prop = Foverlay_get (overlay_vec[i], propname); if (!NILP (prop)) merge_face_ref (f, prop, attrs, true, 0); - oend = OVERLAY_END (overlay_vec[i]); - oendpos = OVERLAY_POSITION (oend); - if (oendpos < endpos) - endpos = oendpos; - } + oendpos = OVERLAY_END (overlay_vec[i]); + if (oendpos < endpos) + endpos = oendpos; + } } *endptr = endpos; diff --git a/test/manual/noverlay/.gitignore b/test/manual/noverlay/.gitignore new file mode 100644 index 0000000000..ca7fc452b8 --- /dev/null +++ b/test/manual/noverlay/.gitignore @@ -0,0 +1 @@ +itree-tests diff --git a/test/manual/noverlay/Makefile.in b/test/manual/noverlay/Makefile.in new file mode 100644 index 0000000000..beef1dbc09 --- /dev/null +++ b/test/manual/noverlay/Makefile.in @@ -0,0 +1,32 @@ +PROGRAM = itree-tests +LIBS = check +top_srcdir = @top_srcdir@ +CFLAGS += -O0 -g3 $(shell pkg-config --cflags $(LIBS)) -I $(top_srcdir)/src +LDFLAGS += $(shell pkg-config --libs $(LIBS)) -lm +OBJECTS = itree-tests.o +CC = gcc +EMACS ?= ../../../src/emacs + +.PHONY: all check have-libcheck + +all: check + +have-libcheck: + pkg-config --cflags $(LIBS) + +check: have-libcheck $(PROGRAM) + ./check-sanitize.sh ./$(PROGRAM) + +itree-tests.o: emacs-compat.h itree-tests.c $(top_srcdir)/src/itree.c $(top_srcdir)/src/itree.h + +$(PROGRAM): $(OBJECTS) + $(CC) $(CFLAGS) $(LDFLAGS) $(OBJECTS) -o $(PROGRAM) + +perf: + -$(EMACS) -Q -l ./overlay-perf.el -f perf-run-batch + +clean: + rm -f -- $(OBJECTS) $(PROGRAM) + +distclean: clean + rm -f -- Makefile diff --git a/test/manual/noverlay/check-sanitize.sh b/test/manual/noverlay/check-sanitize.sh new file mode 100755 index 0000000000..03eedce8a6 --- /dev/null +++ b/test/manual/noverlay/check-sanitize.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +prog=$1 +shift + +[ -z "$prog" ] && { + echo "usage:$(basename $0) CHECK_PRGOGRAM"; + exit 1; +} + +"$prog" "$@" | sed -e 's/^\([^:]\+\):\([0-9]\+\):[PFE]:[^:]*:\([^:]*\):[^:]*: *\(.*\)/\1:\2:\3:\4/' diff --git a/test/manual/noverlay/emacs-compat.h b/test/manual/noverlay/emacs-compat.h new file mode 100644 index 0000000000..812f8e48a3 --- /dev/null +++ b/test/manual/noverlay/emacs-compat.h @@ -0,0 +1,52 @@ +#ifndef TEST_COMPAT_H +#define TEST_COMPAT_H + +#include +#include + +typedef int Lisp_Object; + +void * +xmalloc (size_t size) +{ + return malloc (size); +} + +void +xfree (void *ptr) +{ + free (ptr); +} + +void * +xrealloc (void *block, size_t size) +{ + return realloc (block, size); +} + +void +emacs_abort () +{ + fprintf (stderr, "Aborting...\n"); + exit (1); +} + +#ifndef eassert +#define eassert(cond) \ + do { \ + if (! (cond)) { \ + fprintf (stderr, "\n%s:%d:eassert condition failed: %s\n", \ + __FILE__, __LINE__ ,#cond); \ + exit (1); \ + } \ + } while (0) +#endif + +#ifndef max +#define max(x,y) ((x) >= (y) ? (x) : (y)) +#endif +#ifndef min +#define min(x,y) ((x) <= (y) ? (x) : (y)) +#endif + +#endif diff --git a/test/manual/noverlay/itree-tests.c b/test/manual/noverlay/itree-tests.c new file mode 100644 index 0000000000..a318389213 --- /dev/null +++ b/test/manual/noverlay/itree-tests.c @@ -0,0 +1,1381 @@ +#include +#include +#include +#include +#include "emacs-compat.h" + +#define EMACS_LISP_H /* lisp.h inclusion guard */ +#define ITREE_DEBUG 1 +#define ITREE_TESTING +#include "itree.c" + +/* Basic tests of the interval_tree data-structure. */ + +/* +===================================================================================+ + * | Insert + * +===================================================================================+ */ + +/* The graphs below display the trees after each insertion (as they + should be). See the source code for the different cases + applied. */ + +#define N_50 (n[0]) +#define N_30 (n[1]) +#define N_20 (n[2]) +#define N_10 (n[3]) +#define N_15 (n[4]) +#define N_05 (n[5]) + +#define DEF_TEST_SETUP() \ + struct interval_tree tree; \ + struct interval_node n[6]; \ + interval_tree_init (&tree); \ + const int values[] = {50, 30, 20, 10, 15, 5}; \ + for (int i = 0; i < 6; ++i) \ + { \ + n[i].begin = values[i]; \ + n[i].end = values[i]; \ + } + +START_TEST (test_insert_1) +{ + /* + * [50] + */ + + DEF_TEST_SETUP (); + interval_tree_insert (&tree, &N_50); + ck_assert (N_50.color == ITREE_BLACK); + ck_assert (&N_50 == tree.root); +} +END_TEST + +START_TEST (test_insert_2) +{ + /* + * [50] + * / + * (30) + */ + + DEF_TEST_SETUP (); + interval_tree_insert (&tree, &N_50); + interval_tree_insert (&tree, &N_30); + ck_assert (N_50.color == ITREE_BLACK); + ck_assert (N_30.color == ITREE_RED); + ck_assert (&N_50 == tree.root); + ck_assert (N_30.parent == &N_50); + ck_assert (N_50.left == &N_30); + ck_assert (N_50.right == &tree.nil); + ck_assert (N_30.left == &tree.nil); + ck_assert (N_30.right == &tree.nil); +} +END_TEST + +START_TEST (test_insert_3) +{ + /* case 3.a + * [30] + * / \ + * (20) (50) + */ + + DEF_TEST_SETUP (); + interval_tree_insert (&tree, &N_50); + interval_tree_insert (&tree, &N_30); + interval_tree_insert (&tree, &N_20); + ck_assert (N_50.color == ITREE_RED); + ck_assert (N_30.color == ITREE_BLACK); + ck_assert (N_20.color == ITREE_RED); + ck_assert (&N_30 == tree.root); + ck_assert (N_50.parent == &N_30); + ck_assert (N_30.right == &N_50); + ck_assert (N_30.left == &N_20); + ck_assert (N_20.left == &tree.nil); + ck_assert (N_20.right == &tree.nil); + ck_assert (N_20.parent == &N_30); +} +END_TEST + +START_TEST (test_insert_4) +{ + /* 1.a + * [30] + * / \ + * [20] [50] + * / + * (10) + */ + + DEF_TEST_SETUP (); + interval_tree_insert (&tree, &N_50); + interval_tree_insert (&tree, &N_30); + interval_tree_insert (&tree, &N_20); + interval_tree_insert (&tree, &N_10); + ck_assert (N_50.color == ITREE_BLACK); + ck_assert (N_30.color == ITREE_BLACK); + ck_assert (N_20.color == ITREE_BLACK); + ck_assert (N_10.color == ITREE_RED); + ck_assert (&N_30 == tree.root); + ck_assert (N_50.parent == &N_30); + ck_assert (N_30.right == &N_50); + ck_assert (N_30.left == &N_20); + ck_assert (N_20.left == &N_10); + ck_assert (N_20.right == &tree.nil); + ck_assert (N_20.parent == &N_30); + ck_assert (N_10.parent == &N_20); + ck_assert (N_20.left == &N_10); + ck_assert (N_10.right == &tree.nil); +} +END_TEST + +START_TEST (test_insert_5) +{ + /* 2.a + * [30] + * / \ + * [15] [50] + * / \ + * (10) (20) + */ + + DEF_TEST_SETUP (); + interval_tree_insert (&tree, &N_50); + interval_tree_insert (&tree, &N_30); + interval_tree_insert (&tree, &N_20); + interval_tree_insert (&tree, &N_10); + interval_tree_insert (&tree, &N_15); + ck_assert (N_50.color == ITREE_BLACK); + ck_assert (N_30.color == ITREE_BLACK); + ck_assert (N_20.color == ITREE_RED); + ck_assert (N_10.color == ITREE_RED); + ck_assert (N_15.color == ITREE_BLACK); + ck_assert (&N_30 == tree.root); + ck_assert (N_50.parent == &N_30); + ck_assert (N_30.right == &N_50); + ck_assert (N_30.left == &N_15); + ck_assert (N_20.left == &tree.nil); + ck_assert (N_20.right == &tree.nil); + ck_assert (N_20.parent == &N_15); + ck_assert (N_10.parent == &N_15); + ck_assert (N_20.left == &tree.nil); + ck_assert (N_10.right == &tree.nil); + ck_assert (N_15.right == &N_20); + ck_assert (N_15.left == &N_10); + ck_assert (N_15.parent == &N_30); + +} +END_TEST + +START_TEST (test_insert_6) +{ + /* 1.a + * [30] + * / \ + * (15) [50] + * / \ + * [10] [20] + * / + * (5) + */ + + DEF_TEST_SETUP (); + interval_tree_insert (&tree, &N_50); + interval_tree_insert (&tree, &N_30); + interval_tree_insert (&tree, &N_20); + interval_tree_insert (&tree, &N_10); + interval_tree_insert (&tree, &N_15); + interval_tree_insert (&tree, &N_05); + ck_assert (N_50.color == ITREE_BLACK); + ck_assert (N_30.color == ITREE_BLACK); + ck_assert (N_20.color == ITREE_BLACK); + ck_assert (N_10.color == ITREE_BLACK); + ck_assert (N_15.color == ITREE_RED); + ck_assert (N_05.color == ITREE_RED); + ck_assert (&N_30 == tree.root); + ck_assert (N_50.parent == &N_30); + ck_assert (N_30.right == &N_50); + ck_assert (N_30.left == &N_15); + ck_assert (N_20.left == &tree.nil); + ck_assert (N_20.right == &tree.nil); + ck_assert (N_20.parent == &N_15); + ck_assert (N_10.parent == &N_15); + ck_assert (N_20.left == &tree.nil); + ck_assert (N_10.right == &tree.nil); + ck_assert (N_15.right == &N_20); + ck_assert (N_15.left == &N_10); + ck_assert (N_15.parent == &N_30); + ck_assert (N_05.parent == &N_10); + ck_assert (N_10.left == &N_05); + ck_assert (N_05.right == &tree.nil); +} +END_TEST + +#undef N_50 +#undef N_30 +#undef N_20 +#undef N_10 +#undef N_15 +#undef N_05 +#undef DEF_TEST_SETUP + + + +/* These are the mirror cases to the above ones. */ + +#define N_50 (n[0]) +#define N_70 (n[1]) +#define N_80 (n[2]) +#define N_90 (n[3]) +#define N_85 (n[4]) +#define N_95 (n[5]) + +#define DEF_TEST_SETUP() \ + struct interval_tree tree; \ + struct interval_node n[6]; \ + interval_tree_init (&tree); \ + const int values[] = {50, 70, 80, 90, 85, 95}; \ + for (int i = 0; i < 6; ++i) \ + { \ + n[i].begin = values[i]; \ + n[i].end = values[i]; \ + } + +START_TEST (test_insert_7) +{ + /* + * [50] + */ + + DEF_TEST_SETUP (); + interval_tree_insert (&tree, &N_50); + ck_assert (N_50.color == ITREE_BLACK); + ck_assert (&N_50 == tree.root); +} +END_TEST + +START_TEST (test_insert_8) +{ + /* + * [50] + * \ + * (70) + */ + + DEF_TEST_SETUP (); + interval_tree_insert (&tree, &N_50); + interval_tree_insert (&tree, &N_70); + ck_assert (N_50.color == ITREE_BLACK); + ck_assert (N_70.color == ITREE_RED); + ck_assert (&N_50 == tree.root); + ck_assert (N_70.parent == &N_50); + ck_assert (N_50.right == &N_70); + ck_assert (N_50.left == &tree.nil); + ck_assert (N_70.right == &tree.nil); + ck_assert (N_70.left == &tree.nil); +} +END_TEST + +START_TEST (test_insert_9) +{ + /* 3.a + * [70] + * / \ + * (50) (80) + */ + + DEF_TEST_SETUP (); + interval_tree_insert (&tree, &N_50); + interval_tree_insert (&tree, &N_70); + interval_tree_insert (&tree, &N_80); + ck_assert (N_50.color == ITREE_RED); + ck_assert (N_70.color == ITREE_BLACK); + ck_assert (N_80.color == ITREE_RED); + ck_assert (&N_70 == tree.root); + ck_assert (N_50.parent == &N_70); + ck_assert (N_70.right == &N_80); + ck_assert (N_70.left == &N_50); + ck_assert (N_80.right == &tree.nil); + ck_assert (N_80.left == &tree.nil); + ck_assert (N_80.parent == &N_70); +} +END_TEST + +START_TEST (test_insert_10) +{ + /* 1.b + * [70] + * / \ + * [50] [80] + * \ + * (90) + */ + + DEF_TEST_SETUP (); + interval_tree_insert (&tree, &N_50); + interval_tree_insert (&tree, &N_70); + interval_tree_insert (&tree, &N_80); + interval_tree_insert (&tree, &N_90); + ck_assert (N_50.color == ITREE_BLACK); + ck_assert (N_70.color == ITREE_BLACK); + ck_assert (N_80.color == ITREE_BLACK); + ck_assert (N_90.color == ITREE_RED); + ck_assert (&N_70 == tree.root); + ck_assert (N_50.parent == &N_70); + ck_assert (N_70.right == &N_80); + ck_assert (N_70.left == &N_50); + ck_assert (N_80.right == &N_90); + ck_assert (N_80.left == &tree.nil); + ck_assert (N_80.parent == &N_70); + ck_assert (N_90.parent == &N_80); + ck_assert (N_80.right == &N_90); + ck_assert (N_90.left == &tree.nil); +} +END_TEST + +START_TEST (test_insert_11) +{ + /* 2.b + * [70] + * / \ + * [50] [85] + * / \ + * (80) (90) + */ + + DEF_TEST_SETUP (); + interval_tree_insert (&tree, &N_50); + interval_tree_insert (&tree, &N_70); + interval_tree_insert (&tree, &N_80); + interval_tree_insert (&tree, &N_90); + interval_tree_insert (&tree, &N_85); + ck_assert (N_50.color == ITREE_BLACK); + ck_assert (N_70.color == ITREE_BLACK); + ck_assert (N_80.color == ITREE_RED); + ck_assert (N_90.color == ITREE_RED); + ck_assert (N_85.color == ITREE_BLACK); + ck_assert (&N_70 == tree.root); + ck_assert (N_50.parent == &N_70); + ck_assert (N_70.right == &N_85); + ck_assert (N_70.left == &N_50); + ck_assert (N_80.right == &tree.nil); + ck_assert (N_80.left == &tree.nil); + ck_assert (N_80.parent == &N_85); + ck_assert (N_90.parent == &N_85); + ck_assert (N_80.right == &tree.nil); + ck_assert (N_90.left == &tree.nil); + ck_assert (N_85.right == &N_90); + ck_assert (N_85.left == &N_80); + ck_assert (N_85.parent == &N_70); + +} +END_TEST + +START_TEST (test_insert_12) +{ + /* 1.b + * [70] + * / \ + * [50] (85) + * / \ + * [80] [90] + * \ + * (95) + */ + + DEF_TEST_SETUP (); + interval_tree_insert (&tree, &N_50); + interval_tree_insert (&tree, &N_70); + interval_tree_insert (&tree, &N_80); + interval_tree_insert (&tree, &N_90); + interval_tree_insert (&tree, &N_85); + interval_tree_insert (&tree, &N_95); + ck_assert (N_50.color == ITREE_BLACK); + ck_assert (N_70.color == ITREE_BLACK); + ck_assert (N_80.color == ITREE_BLACK); + ck_assert (N_90.color == ITREE_BLACK); + ck_assert (N_85.color == ITREE_RED); + ck_assert (N_95.color == ITREE_RED); + ck_assert (&N_70 == tree.root); + ck_assert (N_50.parent == &N_70); + ck_assert (N_70.right == &N_85); + ck_assert (N_70.left == &N_50); + ck_assert (N_80.right == &tree.nil); + ck_assert (N_80.left == &tree.nil); + ck_assert (N_80.parent == &N_85); + ck_assert (N_90.parent == &N_85); + ck_assert (N_80.right == &tree.nil); + ck_assert (N_90.left == &tree.nil); + ck_assert (N_85.right == &N_90); + ck_assert (N_85.left == &N_80); + ck_assert (N_85.parent == &N_70); + ck_assert (N_95.parent == &N_90); + ck_assert (N_90.right == &N_95); + ck_assert (N_95.left == &tree.nil); +} +END_TEST + +#undef N_50 +#undef N_70 +#undef N_80 +#undef N_90 +#undef N_85 +#undef N_95 +#undef DEF_TEST_SETUP + +struct interval_tree* +test_get_tree4 (struct interval_node **n) +{ + static struct interval_tree tree; + static struct interval_node nodes[4]; + memset (&tree, 0, sizeof (struct interval_tree)); + memset (&nodes, 0, 4 * sizeof (struct interval_node)); + interval_tree_init (&tree); + for (int i = 0; i < 4; ++i) + { + nodes[i].begin = 10 * (i + 1); + nodes[i].end = nodes[i].begin; + interval_tree_insert (&tree, &nodes[i]); + } + *n = nodes; + return &tree; +} + +static void +shuffle (int *index, int n) +{ + for (int i = n - 1; i >= 0; --i) + { + int j = random () % (i + 1); + int h = index[j]; + index[j] = index[i]; + index[i] = h; + } +} + +#define N_10 (nodes[0]) +#define N_20 (nodes[1]) +#define N_30 (nodes[2]) +#define N_40 (nodes[3]) + +START_TEST (test_insert_13) +{ + struct interval_node *nodes = NULL; + struct interval_tree *tree = test_get_tree4 (&nodes); + + + ck_assert (tree->root == &N_20); + ck_assert (N_20.left == &N_10); + ck_assert (N_20.right == &N_30); + ck_assert (N_30.right == &N_40); + ck_assert (N_10.color == ITREE_BLACK); + ck_assert (N_20.color == ITREE_BLACK); + ck_assert (N_30.color == ITREE_BLACK); + ck_assert (N_40.color == ITREE_RED); +} +END_TEST + +START_TEST (test_insert_14) +{ + struct interval_tree tree; + struct interval_node nodes[3]; + + nodes[0].begin = nodes[1].begin = nodes[2].begin = 10; + nodes[0].end = nodes[1].end = nodes[2].end = 10; + + for (int i = 0; i < 3; ++i) + interval_tree_insert (&tree, &nodes[i]); + for (int i = 0; i < 3; ++i) + ck_assert (interval_tree_contains (&tree, &nodes[i])); +} +END_TEST + + + + +/* +===================================================================================+ + * | Remove + * +===================================================================================+ */ + +#define A (nodes[0]) +#define B (nodes[1]) +#define C (nodes[2]) +#define D (nodes[3]) +#define E (nodes[4]) + +/* Creating proper test trees for the formal tests via insertions is + way to tedious, so we just fake it and only test the + fix-routine. */ +#define DEF_TEST_SETUP() \ + struct interval_tree tree; \ + struct interval_node nodes[5]; \ + interval_tree_init (&tree); \ + tree.root = &B; \ + A.parent = &B; B.parent = &tree.nil; C.parent = &D; \ + D.parent = &B; E.parent = &D; \ + A.left = A.right = C.left = C.right = &tree.nil; \ + E.left = E.right = &tree.nil; \ + B.left = &A; B.right = &D; D.left = &C; D.right = &E \ + +/* 1.a -> 2.a + * [B] + * / \ + * [A] (D) + * / \ + * [C] [E] + */ + + +START_TEST (test_remove_1) +{ + DEF_TEST_SETUP (); + B.color = A.color = C.color = E.color = ITREE_BLACK; + D.color = ITREE_RED; + interval_tree_remove_fix (&tree, &A); + + ck_assert (A.color == ITREE_BLACK); + ck_assert (B.color == ITREE_BLACK); + ck_assert (C.color == ITREE_RED); + ck_assert (D.color == ITREE_BLACK); + ck_assert (E.color == ITREE_BLACK); + ck_assert (A.parent == &B); + ck_assert (B.left == &A); + ck_assert (B.right == &C); + ck_assert (C.parent == &B); + ck_assert (E.parent == &D); + ck_assert (D.right == &E); + ck_assert (D.left == &B); + ck_assert (tree.root == &D); +} +END_TEST + +/* 2.a */ +START_TEST (test_remove_2) +{ + DEF_TEST_SETUP (); + B.color = D.color = A.color = C.color = E.color = ITREE_BLACK; + interval_tree_remove_fix (&tree, &A); + + ck_assert (A.color == ITREE_BLACK); + ck_assert (B.color == ITREE_BLACK); + ck_assert (C.color == ITREE_BLACK); + ck_assert (D.color == ITREE_RED); + ck_assert (E.color == ITREE_BLACK); + ck_assert (A.parent == &B); + ck_assert (B.left == &A); + ck_assert (B.right == &D); + ck_assert (C.parent == &D); + ck_assert (E.parent == &D); + ck_assert (tree.root == &B); +} +END_TEST + +/* 3.a -> 4.a*/ +START_TEST (test_remove_3) +{ + DEF_TEST_SETUP (); + D.color = A.color = E.color = ITREE_BLACK; + B.color = C.color = ITREE_RED; + interval_tree_remove_fix (&tree, &A); + + ck_assert (A.color == ITREE_BLACK); + ck_assert (B.color == ITREE_BLACK); + ck_assert (C.color == ITREE_BLACK); + ck_assert (D.color == ITREE_BLACK); + ck_assert (E.color == ITREE_BLACK); + ck_assert (A.parent == &B); + ck_assert (B.left == &A); + ck_assert (B.right == &tree.nil); + ck_assert (&C == tree.root); + ck_assert (C.left == &B); + ck_assert (C.right == &D); + ck_assert (E.parent == &D); + ck_assert (D.left == &tree.nil); + +} +END_TEST + +/* 4.a */ +START_TEST (test_remove_4) +{ + DEF_TEST_SETUP (); + B.color = C.color = E.color = ITREE_RED; + A.color = D.color = ITREE_BLACK; + interval_tree_remove_fix (&tree, &A); + + ck_assert (A.color == ITREE_BLACK); + ck_assert (B.color == ITREE_BLACK); + ck_assert (C.color == ITREE_RED); + ck_assert (D.color == ITREE_BLACK); + ck_assert (E.color == ITREE_BLACK); + ck_assert (A.parent == &B); + ck_assert (B.left == &A); + ck_assert (B.right == &C); + ck_assert (C.parent == &B); + ck_assert (E.parent == &D); + ck_assert (tree.root == &D); +} +END_TEST + + +#undef A +#undef B +#undef C +#undef D +#undef E +#undef DEF_TEST_SETUP + + + +/* These are the mirrored cases. */ + +#define A (nodes[0]) +#define B (nodes[1]) +#define C (nodes[2]) +#define D (nodes[3]) +#define E (nodes[4]) + +#define DEF_TEST_SETUP() \ + struct interval_tree tree; \ + struct interval_node nodes[5]; \ + interval_tree_init (&tree); \ + tree.root = &B; \ + A.parent = &B; B.parent = &tree.nil; C.parent = &D; \ + D.parent = &B; E.parent = &D; \ + A.right = A.left = C.right = C.left = &tree.nil; \ + E.right = E.left = &tree.nil; \ + B.right = &A; B.left = &D; D.right = &C; D.left = &E \ + +/* 1.b -> 2.b + * [B] + * / \ + * [A] (D) + * / \ + * [C] [E] + */ + + +START_TEST (test_remove_5) +{ + DEF_TEST_SETUP (); + B.color = A.color = C.color = E.color = ITREE_BLACK; + D.color = ITREE_RED; + interval_tree_remove_fix (&tree, &A); + + ck_assert (A.color == ITREE_BLACK); + ck_assert (B.color == ITREE_BLACK); + ck_assert (C.color == ITREE_RED); + ck_assert (D.color == ITREE_BLACK); + ck_assert (E.color == ITREE_BLACK); + ck_assert (A.parent == &B); + ck_assert (B.right == &A); + ck_assert (B.left == &C); + ck_assert (C.parent == &B); + ck_assert (E.parent == &D); + ck_assert (D.left == &E); + ck_assert (D.right == &B); + ck_assert (tree.root == &D); +} +END_TEST + +/* 2.b */ +START_TEST (test_remove_6) +{ + DEF_TEST_SETUP (); + B.color = D.color = A.color = C.color = E.color = ITREE_BLACK; + interval_tree_remove_fix (&tree, &A); + + ck_assert (A.color == ITREE_BLACK); + ck_assert (B.color == ITREE_BLACK); + ck_assert (C.color == ITREE_BLACK); + ck_assert (D.color == ITREE_RED); + ck_assert (E.color == ITREE_BLACK); + ck_assert (A.parent == &B); + ck_assert (B.right == &A); + ck_assert (B.left == &D); + ck_assert (C.parent == &D); + ck_assert (E.parent == &D); + ck_assert (tree.root == &B); +} +END_TEST + +/* 3.b -> 4.b*/ +START_TEST (test_remove_7) +{ + DEF_TEST_SETUP (); + D.color = A.color = E.color = ITREE_BLACK; + B.color = C.color = ITREE_RED; + interval_tree_remove_fix (&tree, &A); + + ck_assert (A.color == ITREE_BLACK); + ck_assert (B.color == ITREE_BLACK); + ck_assert (C.color == ITREE_BLACK); + ck_assert (D.color == ITREE_BLACK); + ck_assert (E.color == ITREE_BLACK); + ck_assert (A.parent == &B); + ck_assert (B.right == &A); + ck_assert (B.left == &tree.nil); + ck_assert (&C == tree.root); + ck_assert (C.right == &B); + ck_assert (C.left == &D); + ck_assert (E.parent == &D); + ck_assert (D.right == &tree.nil); + +} +END_TEST + +/* 4.b */ +START_TEST (test_remove_8) +{ + DEF_TEST_SETUP (); + B.color = C.color = E.color = ITREE_RED; + A.color = D.color = ITREE_BLACK; + interval_tree_remove_fix (&tree, &A); + + ck_assert (A.color == ITREE_BLACK); + ck_assert (B.color == ITREE_BLACK); + ck_assert (C.color == ITREE_RED); + ck_assert (D.color == ITREE_BLACK); + ck_assert (E.color == ITREE_BLACK); + ck_assert (A.parent == &B); + ck_assert (B.right == &A); + ck_assert (B.left == &C); + ck_assert (C.parent == &B); + ck_assert (E.parent == &D); + ck_assert (tree.root == &D); +} +END_TEST + + +#undef A +#undef B +#undef C +#undef D +#undef E +#undef DEF_TEST_SETUP + + +START_TEST (test_remove_9) +{ + struct interval_node *nodes = NULL; + struct interval_tree *tree = test_get_tree4 (&nodes); + + ck_assert (tree->root == &N_20); + ck_assert (N_20.left == &N_10); + ck_assert (N_20.right == &N_30); + ck_assert (N_30.right == &N_40); + ck_assert (N_20.color == ITREE_BLACK); + ck_assert (N_10.color == ITREE_BLACK); + ck_assert (N_30.color == ITREE_BLACK); + ck_assert (N_40.color == ITREE_RED); + + interval_tree_remove (tree, &N_10); + + ck_assert (tree->root == &N_30); + ck_assert (N_30.parent == &tree->nil); + ck_assert (N_30.left == &N_20); + ck_assert (N_30.right == &N_40); + ck_assert (N_20.color == ITREE_BLACK); + ck_assert (N_30.color == ITREE_BLACK); + ck_assert (N_40.color == ITREE_BLACK); +} +END_TEST + +#define N 3 + +START_TEST (test_remove_10) +{ + struct interval_tree tree; + struct interval_node nodes[N]; + int index[N]; + + srand (42); + interval_tree_init (&tree); + for (int i = 0; i < N; ++i) + { + nodes[i].begin = (i + 1) * 10; + nodes[i].end = nodes[i].begin + 1; + index[i] = i; + } + shuffle (index, N); + for (int i = 0; i < N; ++i) + interval_tree_insert (&tree, &nodes[index[i]]); + + shuffle (index, N); + for (int i = 0; i < N; ++i) + { + ck_assert (interval_tree_contains (&tree, &nodes[index[i]])); + interval_tree_remove (&tree, &nodes[index[i]]); + } + ck_assert (tree.root == &tree.nil); + ck_assert (tree.size == 0); +} +END_TEST + + +/* +===================================================================================+ + * | Generator + * +===================================================================================+ */ + +START_TEST (test_generator_1) +{ + struct interval_tree tree; + struct interval_node node, *n; + struct interval_generator *g; + interval_tree_init (&tree); + node.begin = 10; + node.end = 20; + interval_tree_insert (&tree, &node); + g = interval_generator_create (&tree); + interval_generator_reset (g, 0, 30, ITREE_ASCENDING); + n = interval_generator_next (g); + ck_assert (n == &node); + ck_assert (n->begin == 10 && n->end == 20); + ck_assert (interval_generator_next (g) == NULL); + ck_assert (interval_generator_next (g) == NULL); + ck_assert (interval_generator_next (g) == NULL); + interval_generator_destroy (g); + + g = interval_generator_create (&tree); + interval_generator_reset (g, 30, 50, ITREE_ASCENDING); + ck_assert (interval_generator_next (g) == NULL); + ck_assert (interval_generator_next (g) == NULL); + ck_assert (interval_generator_next (g) == NULL); + interval_generator_destroy (g); +} +END_TEST + +void +test_check_generator (struct interval_tree *tree, + ptrdiff_t begin, ptrdiff_t end, + int n, ...) +{ + va_list ap; + struct interval_generator *g = interval_generator_create (tree); + interval_generator_reset (g, begin, end, ITREE_ASCENDING); + + va_start (ap, n); + for (int i = 0; i < n; ++i) + { + ptrdiff_t begin = va_arg (ap, ptrdiff_t); + struct interval_node *node = interval_generator_next (g); + ck_assert (node); + ck_assert_int_eq (node->begin, begin); + } + va_end (ap); + ck_assert (! interval_generator_next (g)); + ck_assert (! interval_generator_next (g)); + interval_generator_destroy (g); +} + +#define DEF_TEST_SETUP() \ + + +START_TEST (test_generator_2) +{ + struct interval_tree tree; + struct interval_node nodes[3]; + + interval_tree_init (&tree); + + for (int i = 0; i < 3; ++i) { + nodes[i].begin = 10 * (i + 1); + nodes[i].end = 10 * (i + 2); + interval_tree_insert (&tree, &nodes[i]); + } + + test_check_generator (&tree, 0, 50, 3, + 10, 20, 30); + test_check_generator (&tree, 0, 10, 0); + test_check_generator (&tree, 40, 50, 0); + test_check_generator (&tree, 15, 35, 3, + 10, 20, 30); + test_check_generator (&tree, -100, -50, 0); + test_check_generator (&tree, -100, -50, 0); + test_check_generator (&tree, 100, 50, 0); + test_check_generator (&tree, 100, 150, 0); + test_check_generator (&tree, 0, 0, 0); + test_check_generator (&tree, 40, 40, 0); + test_check_generator (&tree, 30, 30, 0); + test_check_generator (&tree, 35, 35, 1, + 30); +} +END_TEST + + +struct interval_node* +test_create_tree (struct interval_tree *tree, int n, + bool doshuffle, ...) +{ + va_list ap; + struct interval_node *nodes = calloc (n, sizeof (struct interval_node)); + int *index = calloc (n, sizeof (int)); + + interval_tree_init (tree); + va_start (ap, doshuffle); + for (int i = 0; i < n; ++i) + { + ptrdiff_t begin = va_arg (ap, ptrdiff_t); + ptrdiff_t end = va_arg (ap, ptrdiff_t); + nodes[i].begin = begin; + nodes[i].end = end; + index[i] = i; + } + va_end (ap); + srand (42); + if (doshuffle) + shuffle (index, n); + for (int i = 0; i < n; ++i) + interval_tree_insert (tree, &nodes[index[i]]); + free (index); + + return nodes; +} + +START_TEST (test_generator_3) +{ + struct interval_tree tree; + struct interval_node *nodes = NULL; + + nodes = test_create_tree (&tree, 3, true, + 10, 10, + 10, 10, + 10, 10); + test_check_generator (&tree, 0, 10, 0); + test_check_generator (&tree, 10, 10, 3, 10, 10, 10); + test_check_generator (&tree, 10, 20, 3, 10, 10, 10); + free (nodes); +} +END_TEST + +#define FOREACH(n, g) \ + for ((n) = interval_generator_next (g); (n) != NULL; \ + (n) = interval_generator_next (g)) + +START_TEST (test_generator_5) +{ + struct interval_tree tree; + struct interval_node *nodes; + struct interval_generator *g; + nodes = test_create_tree (&tree, 4, false, + 10, 30, + 20, 40, + 30, 50, + 40, 60); + g = interval_generator_create (&tree); + interval_generator_reset (g, 0, 100, ITREE_PRE_ORDER); + for (int i = 0; i < 4; ++i) + { + struct interval_node *n = interval_generator_next (g); + ck_assert (n); + switch (i) + { + case 0: ck_assert_int_eq (20, n->begin); break; + case 1: ck_assert_int_eq (10, n->begin); break; + case 2: ck_assert_int_eq (30, n->begin); break; + case 3: ck_assert_int_eq (40, n->begin); break; + } + } + interval_generator_destroy (g); + free (nodes); + +} +END_TEST + +START_TEST (test_generator_6) +{ + struct interval_tree tree; + struct interval_node *nodes; + struct interval_generator *g; + nodes = test_create_tree (&tree, 4, true, + 10, 30, + 20, 40, + 30, 50, + 40, 60); + g = interval_generator_create (&tree); + interval_generator_reset (g, 0, 100, ITREE_ASCENDING); + for (int i = 0; i < 4; ++i) + { + struct interval_node *n = interval_generator_next (g); + ck_assert (n); + switch (i) + { + case 0: ck_assert_int_eq (10, n->begin); break; + case 1: ck_assert_int_eq (20, n->begin); break; + case 2: ck_assert_int_eq (30, n->begin); break; + case 3: ck_assert_int_eq (40, n->begin); break; + } + } + interval_generator_destroy (g); + free (nodes); + +} +END_TEST + +START_TEST (test_generator_7) +{ + struct interval_tree tree; + struct interval_node *nodes; + struct interval_generator *g; + nodes = test_create_tree (&tree, 4, true, + 10, 30, + 20, 40, + 30, 50, + 40, 60); + g = interval_generator_create (&tree); + interval_generator_reset (g, 0, 100, ITREE_DESCENDING); + for (int i = 0; i < 4; ++i) + { + struct interval_node *n = interval_generator_next (g); + ck_assert (n); + switch (i) + { + case 0: ck_assert_int_eq (40, n->begin); break; + case 1: ck_assert_int_eq (30, n->begin); break; + case 2: ck_assert_int_eq (20, n->begin); break; + case 3: ck_assert_int_eq (10, n->begin); break; + } + } + interval_generator_destroy (g); + free (nodes); + +} +END_TEST + +START_TEST (test_generator_8) +{ + struct interval_tree tree; + struct interval_node *nodes, *n; + struct interval_generator *g; + nodes = test_create_tree (&tree, 2, false, + 20, 30, + 40, 50); + g = interval_generator_create (&tree); + interval_generator_reset (g, 1, 60, ITREE_DESCENDING); + n = interval_generator_next (g); + ck_assert_int_eq (n->begin, 40); + interval_generator_narrow (g, 50, 60); + n = interval_generator_next (g); + ck_assert (n == NULL); + free (nodes); +} +END_TEST + + +START_TEST (test_generator_9) +{ + struct interval_tree tree; + struct interval_node *nodes, *n; + struct interval_generator *g; + nodes = test_create_tree (&tree, 2, false, + 25, 25, + 20, 30); + g = interval_generator_create (&tree); + interval_generator_reset (g, 1, 30, ITREE_DESCENDING); + n = interval_generator_next (g); + ck_assert_int_eq (n->begin, 25); + interval_generator_narrow (g, 25, 35); + n = interval_generator_next (g); + ck_assert_int_eq (n->begin, 20); + free (nodes); +} +END_TEST + + +/* +===================================================================================+ + * | Insert Gap + * +===================================================================================+ */ + +static struct interval_tree gap_tree; +static struct interval_node gap_node; + +#define N_BEG (interval_tree_validate (&gap_tree, &gap_node)->begin) +#define N_END (interval_tree_validate (&gap_tree, &gap_node)->end) + +static void +test_setup_gap_node (ptrdiff_t begin, ptrdiff_t end, + bool front_advance, bool rear_advance) +{ + interval_tree_init (&gap_tree); + gap_node.begin = begin; + gap_node.end = end; + gap_node.front_advance = front_advance; + gap_node.rear_advance = rear_advance; + interval_tree_insert (&gap_tree, &gap_node); +} + +static void +test_setup_gap_node_noadvance (ptrdiff_t begin, ptrdiff_t end) +{ + test_setup_gap_node (begin, end, false, false); +} + +START_TEST (test_gap_insert_1) +{ + test_setup_gap_node (100, 200, false, false); + interval_tree_insert_gap (&gap_tree, 100 + 10, 20); + ck_assert_int_eq (N_BEG, 100); + ck_assert_int_eq (N_END, 200 + 20); +} +END_TEST + +START_TEST (test_gap_insert_2) +{ + test_setup_gap_node (100, 200, false, false); + interval_tree_insert_gap (&gap_tree, 300, 10); + ck_assert_int_eq (N_BEG, 100); + ck_assert_int_eq (N_END, 200); +} +END_TEST + +START_TEST (test_gap_insert_3) +{ + test_setup_gap_node (100, 200, false, false); + interval_tree_insert_gap (&gap_tree, 0, 15); + ck_assert_int_eq (N_BEG, 100 + 15); + ck_assert_int_eq (N_END, 200 + 15); +} +END_TEST + +START_TEST (test_gap_insert_4) +{ + test_setup_gap_node (100, 200, true, false); + interval_tree_insert_gap (&gap_tree, 100, 20); + ck_assert_int_eq (N_BEG, 100 + 20); + ck_assert_int_eq (N_END, 200 + 20); + +} +END_TEST + +START_TEST (test_gap_insert_5) +{ + test_setup_gap_node (100, 200, false, false); + interval_tree_insert_gap (&gap_tree, 100, 20); + ck_assert_int_eq (N_BEG, 100); + ck_assert_int_eq (N_END, 200 + 20); + +} +END_TEST + +START_TEST (test_gap_insert_6) +{ + test_setup_gap_node (100, 200, false, true); + interval_tree_insert_gap (&gap_tree, 200, 20); + ck_assert_int_eq (N_BEG, 100); + ck_assert_int_eq (N_END, 200 + 20); + +} +END_TEST + +START_TEST (test_gap_insert_7) +{ + test_setup_gap_node (100, 200, false, false); + interval_tree_insert_gap (&gap_tree, 200, 20); + ck_assert_int_eq (N_BEG, 100); + ck_assert_int_eq (N_END, 200); + +} +END_TEST + +START_TEST (test_gap_insert_8) +{ + test_setup_gap_node (100, 100, true, true); + interval_tree_insert_gap (&gap_tree, 100, 20); + ck_assert_int_eq (N_BEG, 100 + 20); + ck_assert_int_eq (N_END, 100 + 20); + +} +END_TEST + +START_TEST (test_gap_insert_9) +{ + test_setup_gap_node (100, 100, false, true); + interval_tree_insert_gap (&gap_tree, 100, 20); + ck_assert_int_eq (N_BEG, 100); + ck_assert_int_eq (N_END, 100 + 20); + +} +END_TEST + +START_TEST (test_gap_insert_10) +{ + test_setup_gap_node (100, 100, true, false); + interval_tree_insert_gap (&gap_tree, 100, 20); + ck_assert_int_eq (N_BEG, 100); + ck_assert_int_eq (N_END, 100); + +} +END_TEST + +START_TEST (test_gap_insert_11) +{ + test_setup_gap_node (100, 100, false, false); + interval_tree_insert_gap (&gap_tree, 100, 20); + ck_assert_int_eq (N_BEG, 100); + ck_assert_int_eq (N_END, 100); + +} +END_TEST + + +/* +===================================================================================+ + * | Delete Gap + * +===================================================================================+ */ + +START_TEST (test_gap_delete_1) +{ + test_setup_gap_node_noadvance (100, 200); + interval_tree_delete_gap (&gap_tree, 100 + 10, 20); + ck_assert_int_eq (N_BEG, 100); + ck_assert_int_eq (N_END, 200 - 20); + +} +END_TEST + +START_TEST (test_gap_delete_2) +{ + test_setup_gap_node_noadvance (100, 200); + interval_tree_delete_gap (&gap_tree, 200 + 10, 20); + ck_assert_int_eq (N_BEG, 100); + ck_assert_int_eq (N_END, 200); + +} +END_TEST + +START_TEST (test_gap_delete_3) +{ + test_setup_gap_node_noadvance (100, 200); + interval_tree_delete_gap (&gap_tree, 200, 20); + ck_assert_int_eq (N_BEG, 100); + ck_assert_int_eq (N_END, 200); + +} +END_TEST + +START_TEST (test_gap_delete_4) +{ + test_setup_gap_node_noadvance (100, 200); + interval_tree_delete_gap (&gap_tree, 100 - 20, 20); + ck_assert_int_eq (N_BEG, 100 - 20); + ck_assert_int_eq (N_END, 200 - 20); + +} +END_TEST + +START_TEST (test_gap_delete_5) +{ + test_setup_gap_node_noadvance (100, 200); + interval_tree_delete_gap (&gap_tree, 70, 20); + ck_assert_int_eq (N_BEG, 100 - 20); + ck_assert_int_eq (N_END, 200 - 20); + +} +END_TEST + +START_TEST (test_gap_delete_6) +{ + test_setup_gap_node_noadvance (100, 200); + interval_tree_delete_gap (&gap_tree, 80, 100); + ck_assert_int_eq (N_BEG, 80); + ck_assert_int_eq (N_END, 100); +} +END_TEST + +START_TEST (test_gap_delete_7) +{ + test_setup_gap_node_noadvance (100, 200); + interval_tree_delete_gap (&gap_tree, 120, 100); + ck_assert_int_eq (N_BEG, 100); + ck_assert_int_eq (N_END, 120); +} +END_TEST + +START_TEST (test_gap_delete_8) +{ + test_setup_gap_node_noadvance (100, 200); + interval_tree_delete_gap (&gap_tree, 100 - 20, 200 + 20); + ck_assert_int_eq (N_BEG, 100 - 20); + ck_assert_int_eq (N_END, 100 - 20); + +} +END_TEST + + + +Suite * basic_suite () +{ + Suite *s = suite_create ("basic_suite"); + TCase *tc = tcase_create ("basic_test"); + + tcase_add_test (tc, test_insert_1); + tcase_add_test (tc, test_insert_2); + tcase_add_test (tc, test_insert_3); + tcase_add_test (tc, test_insert_4); + tcase_add_test (tc, test_insert_5); + tcase_add_test (tc, test_insert_6); + tcase_add_test (tc, test_insert_7); + tcase_add_test (tc, test_insert_8); + tcase_add_test (tc, test_insert_9); + tcase_add_test (tc, test_insert_10); + tcase_add_test (tc, test_insert_11); + tcase_add_test (tc, test_insert_12); + tcase_add_test (tc, test_insert_13); + + tcase_add_test (tc, test_remove_1); + tcase_add_test (tc, test_remove_2); + tcase_add_test (tc, test_remove_3); + tcase_add_test (tc, test_remove_4); + tcase_add_test (tc, test_remove_5); + tcase_add_test (tc, test_remove_6); + tcase_add_test (tc, test_remove_7); + tcase_add_test (tc, test_remove_8); + tcase_add_test (tc, test_remove_9); + tcase_add_test (tc, test_remove_10); + + tcase_add_test (tc, test_generator_1); + tcase_add_test (tc, test_generator_2); + tcase_add_test (tc, test_generator_3); + tcase_add_test (tc, test_generator_5); + tcase_add_test (tc, test_generator_6); + tcase_add_test (tc, test_generator_7); + tcase_add_test (tc, test_generator_8); + tcase_add_test (tc, test_generator_9); + + tcase_add_test (tc, test_gap_insert_1); + tcase_add_test (tc, test_gap_insert_2); + tcase_add_test (tc, test_gap_insert_3); + tcase_add_test (tc, test_gap_insert_4); + tcase_add_test (tc, test_gap_insert_5); + tcase_add_test (tc, test_gap_insert_6); + tcase_add_test (tc, test_gap_insert_7); + tcase_add_test (tc, test_gap_insert_8); + tcase_add_test (tc, test_gap_insert_9); + tcase_add_test (tc, test_gap_insert_10); + tcase_add_test (tc, test_gap_insert_11); + + tcase_add_test (tc, test_gap_delete_1); + tcase_add_test (tc, test_gap_delete_2); + tcase_add_test (tc, test_gap_delete_3); + tcase_add_test (tc, test_gap_delete_4); + tcase_add_test (tc, test_gap_delete_5); + tcase_add_test (tc, test_gap_delete_6); + tcase_add_test (tc, test_gap_delete_7); + tcase_add_test (tc, test_gap_delete_8); + + /* tcase_set_timeout (tc, 120); */ + suite_add_tcase (s, tc); + return s; +} + +int +main (void) +{ + int nfailed; + Suite *s = basic_suite (); + SRunner *sr = srunner_create (s); + + srunner_run_all (sr, CK_NORMAL); + nfailed = srunner_ntests_failed (sr); + srunner_free (sr); + return (nfailed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/test/manual/noverlay/many-errors.py b/test/manual/noverlay/many-errors.py new file mode 100644 index 0000000000..fa4ef5f98d --- /dev/null +++ b/test/manual/noverlay/many-errors.py @@ -0,0 +1,2480 @@ +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass +def a(x, y, y): + return t; pass diff --git a/test/manual/noverlay/overlay-perf.el b/test/manual/noverlay/overlay-perf.el new file mode 100644 index 0000000000..e84941c08f --- /dev/null +++ b/test/manual/noverlay/overlay-perf.el @@ -0,0 +1,764 @@ +;; -*- lexical-binding:t -*- +(require 'cl-lib) +(require 'subr-x) +(require 'seq) +(require 'hi-lock) + + +;; +===================================================================================+ +;; | Framework +;; +===================================================================================+ + +(defmacro perf-define-constant-test (name &optional doc &rest body) + (declare (indent 1) (debug (symbol &optional string &rest form))) + `(progn + (put ',name 'perf-constant-test t) + (defun ,name nil ,doc ,@body))) + +(defmacro perf-define-variable-test (name args &optional doc &rest body) + (declare (indent 2) (debug defun)) + (unless (and (consp args) + (= (length args) 1)) + (error "Function %s should accept exactly one argument." name)) + `(progn + (put ',name 'perf-variable-test t) + (defun ,name ,args ,doc ,@body))) + +(defmacro perf-define-test-suite (name &rest tests) + (declare (indent 1)) + `(put ',name 'perf-test-suite + ,(cons 'list tests))) + +(defun perf-constant-test-p (test) + (get test 'perf-constant-test)) + +(defun perf-variable-test-p (test) + (get test 'perf-variable-test)) + +(defun perf-test-suite-p (suite) + (not (null (perf-test-suite-elements suite)))) + +(defun perf-test-suite-elements (suite) + (get suite 'perf-test-suite)) + +(defun perf-expand-suites (test-and-suites) + (apply #' append (mapcar (lambda (elt) + (if (perf-test-suite-p elt) + (perf-test-suite-elements elt) + (list elt))) + test-and-suites))) +(defun perf-test-p (symbol) + (or (perf-variable-test-p symbol) + (perf-constant-test-p symbol))) + +(defun perf-all-tests () + (let (result) + (mapatoms (lambda (symbol) + (when (and (fboundp symbol) + (perf-test-p symbol)) + (push symbol result)))) + (sort result #'string-lessp))) + +(defvar perf-default-test-argument 4096) + +(defun perf-run-1 (&optional k n &rest tests) + "Run TESTS K times using N as argument for non-constant ones. + +Return test-total elapsed time." + (random "") + (when (and n (not (numberp n))) + (push k tests) + (push n tests) + (setq n nil k nil)) + (when (and k (not (numberp k))) + (push k tests) + (setq k nil)) + (let* ((k (or k 1)) + (n (or n perf-default-test-argument)) + (tests (perf-expand-suites (or tests + (perf-all-tests)))) + (variable-tests (seq-filter #'perf-variable-test-p tests)) + (constant-tests (seq-filter #'perf-constant-test-p tests)) + (max-test-string-width (perf-max-symbol-length tests))) + (unless (seq-every-p #'perf-test-p tests) + (error "Some of these are not tests: %s" tests)) + (cl-labels ((format-result (result) + (cond + ((numberp result) (format "%.2f" result)) + ((stringp result) result) + ((null result) "N/A"))) + (format-test (fn) + (concat (symbol-name fn) + (make-string + (+ (- max-test-string-width + (length (symbol-name fn))) + 1) + ?\s))) + (format-summary (results _total) + (let ((min (apply #'min results)) + (max (apply #'max results)) + (avg (/ (apply #'+ results) (float (length results))))) + (format "n=%d min=%.2f avg=%.2f max=%.2f" (length results) min avg max))) + (run-test (fn) + (let ((total 0) results) + (dotimes (_ (max 0 k)) + (garbage-collect) + (princ (concat " " (format-test fn))) + (let ((result (condition-case-unless-debug err + (cond + ((perf-variable-test-p fn) + (random "") (car (funcall fn n))) + ((perf-constant-test-p fn) + (random "") (car (funcall fn))) + (t "skip")) + (error (error-message-string err))))) + (when (numberp result) + (cl-incf total result) + (push result results)) + (princ (format-result result)) + (terpri))) + (when (> (length results) 1) + (princ (concat "#" (format-test fn) + (format-summary results total))) + (terpri))))) + (when variable-tests + (terpri) + (dolist (fn variable-tests) + (run-test fn) + (terpri))) + (when constant-tests + (dolist (fn constant-tests) + (run-test fn) + (terpri)))))) + +(defun perf-run (&optional k n &rest tests) + (interactive + (let* ((n (if current-prefix-arg + (prefix-numeric-value current-prefix-arg) + perf-default-test-argument)) + (tests (mapcar #'intern + (completing-read-multiple + (format "Run tests (n=%d): " n) + (perf-all-tests) nil t nil 'perf-test-history)))) + (cons 1 (cons n tests)))) + (with-current-buffer (get-buffer-create "*perf-results*") + (let ((inhibit-read-only t) + (standard-output (current-buffer))) + (erase-buffer) + (apply #'perf-run-1 k n tests) + (display-buffer (current-buffer))))) + + +(defun perf-batch-parse-command-line (args) + (let ((k 1) + (n perf-default-test-argument) + tests) + (while args + (cond ((string-match-p "\\`-[cn]\\'" (car args)) + (unless (and (cdr args) + (string-match-p "\\`[0-9]+\\'" (cadr args))) + (error "%s expectes a natnum argument" (car args))) + (if (equal (car args) "-c") + (setq k (string-to-number (cadr args))) + (setq n (string-to-number (cadr args)))) + (setq args (cddr args))) + (t (push (intern (pop args)) tests)))) + (list k n tests))) + + +(defun perf-run-batch () + "Runs tests from `command-line-args-left' and kill emacs." + (let ((standard-output #'external-debugging-output)) + (condition-case err + (cl-destructuring-bind (k n tests) + (perf-batch-parse-command-line command-line-args-left) + (apply #'perf-run-1 k n tests) + (save-buffers-kill-emacs)) + (error + (princ (error-message-string err)) + (save-buffers-kill-emacs))))) + +(defconst perf-number-of-columns 70) + +(defun perf-insert-lines (n) + "Insert N lines into the current buffer." + (dotimes (i n) + (insert (make-string 70 (if (= (% i 2) 0) + ?. + ?O)) + ?\n))) + +(defun perf-switch-to-buffer-scroll-random (n &optional buffer) + (interactive) + (set-window-buffer nil (or buffer (current-buffer))) + (goto-char (point-min)) + (redisplay t) + (dotimes (_ n) + (goto-char (random (point-max))) + (recenter) + (redisplay t))) + +(defun perf-insert-overlays (n &optional create-callback random-p) + (if random-p + (perf-insert-overlays-random n create-callback) + (perf-insert-overlays-sequential n create-callback))) + +(defun perf-insert-overlays-sequential (n &optional create-callback) + "Insert an overlay every Nth line." + (declare (indent 1)) + (let ((i 0) + (create-callback (or create-callback #'ignore))) + (save-excursion + (goto-char (point-min)) + (while (not (eobp)) + (when (= 0 (% i n)) + (let ((ov (make-overlay (point-at-bol) (point-at-eol)))) + (funcall create-callback ov) + (overlay-put ov 'priority (random (buffer-size))))) + (cl-incf i) + (forward-line))))) + +(defun perf-insert-overlays-random (n &optional create-callback) + "Insert an overlay every Nth line." + (declare (indent 1)) + (let ((create-callback (or create-callback #'ignore))) + (save-excursion + (while (>= (cl-decf n) 0) + (let* ((beg (1+ (random (point-max)))) + (ov (make-overlay beg (+ beg (random 70))))) + (funcall create-callback ov) + (overlay-put ov 'priority (random (buffer-size)))))))) + +(defun perf-insert-overlays-hierarchical (n &optional create-callback) + (let ((create-callback (or create-callback #'ignore))) + (save-excursion + (goto-char (point-min)) + (let ((spacing (floor (/ (/ (count-lines (point-min) (point-max)) + (float 3)) + n)))) + (when (< spacing 1) + (error "Hierarchical overlay overflow !!")) + (dotimes (i n) + (funcall create-callback + (make-overlay (point) + (save-excursion + (goto-char (point-max)) + (forward-line (- (* spacing i))) + (point)))) + + (when (eobp) + (error "End of buffer in hierarchical overlays")) + (forward-line spacing)))))) + +(defun perf-overlay-ascii-chart (&optional buffer width) + (interactive) + (save-current-buffer + (when buffer (set-buffer buffer)) + (unless width (setq width 100)) + (let* ((ovl (sort (overlays-in (point-min) (point-max)) + (lambda (ov1 ov2) + (or (<= (overlay-start ov1) + (overlay-start ov2)) + (and + (= (overlay-start ov1) + (overlay-start ov2)) + (< (overlay-end ov1) + (overlay-end ov2))))))) + (ov-width (apply #'max (mapcar (lambda (ov) + (- (overlay-end ov) + (overlay-start ov))) + ovl))) + (ov-min (apply #'min (mapcar #'overlay-start ovl))) + (ov-max (apply #'max (mapcar #'overlay-end ovl))) + (scale (/ (float width) (+ ov-min ov-width)))) + (with-current-buffer (get-buffer-create "*overlay-ascii-chart*") + (let ((inhibit-read-only t)) + (erase-buffer) + (buffer-disable-undo) + (insert (format "%06d%s%06d\n" ov-min (make-string (- width 12) ?\s) ov-max)) + (dolist (ov ovl) + (let ((length (round (* scale (- (overlay-end ov) + (overlay-start ov)))))) + (insert (make-string (round (* scale (overlay-start ov))) ?\s)) + (cl-case length + (0 (insert "O")) + (1 (insert "|")) + (t (insert (format "|%s|" (make-string (- length 2) ?-))))) + (insert "\n"))) + (goto-char (point-min))) + (read-only-mode 1) + (pop-to-buffer (current-buffer)))))) + +(defconst perf-overlay-faces (mapcar #'intern (seq-take hi-lock-face-defaults 3))) + +(defun perf-overlay-face-callback (ov) + (overlay-put ov 'face (nth (random (length perf-overlay-faces)) + perf-overlay-faces))) + +(defun perf-overlay-invisible-callback (ov) + (overlay-put ov 'invisble (= 1 (random 2)))) + +(defun perf-overlay-display-callback (ov) + (overlay-put ov 'display (make-string 70 ?*))) + +(defmacro perf-define-display-test (overlay-type property-type scroll-type) + (let ((name (intern (format "perf-display-%s/%s/%s" + overlay-type property-type scroll-type))) + (arg (make-symbol "n"))) + + `(perf-define-variable-test ,name (,arg) + (with-temp-buffer + (perf-insert-lines ,arg) + (overlay-recenter (point-max)) + ,@(perf-define-display-test-1 arg overlay-type property-type scroll-type))))) + +(defun perf-define-display-test-1 (arg overlay-type property-type scroll-type) + (list (append (cl-case overlay-type + (sequential + (list 'perf-insert-overlays-sequential 2)) + (hierarchical + `(perf-insert-overlays-hierarchical (/ ,arg 10))) + (random + `(perf-insert-overlays-random (/ ,arg 2))) + (t (error "Invalid insert type: %s" overlay-type))) + (list + (cl-case property-type + (display '#'perf-overlay-display-callback) + (face '#'perf-overlay-face-callback) + (invisible '#'perf-overlay-invisible-callback) + (t (error "Invalid overlay type: %s" overlay-type))))) + (list 'benchmark-run 1 + (cl-case scroll-type + (scroll '(perf-switch-to-buffer-scroll-up-and-down)) + (random `(perf-switch-to-buffer-scroll-random (/ ,arg 50))) + (t (error "Invalid scroll type: %s" overlay-type)))))) + +(defun perf-max-symbol-length (symbols) + "Return the longest symbol in SYMBOLS, or -1 if symbols is nil." + (if (null symbols) + -1 + (apply #'max (mapcar + (lambda (elt) + (length (symbol-name elt))) + symbols)))) + +(defun perf-insert-text (n) + "Insert N character into the current buffer." + (let ((ncols 68) + (char ?.)) + (dotimes (_ (/ n ncols)) + (insert (make-string (1- ncols) char) ?\n)) + (when (> (% n ncols) 0) + (insert (make-string (1- (% n ncols)) char) ?\n)))) + +(defconst perf-insert-overlays-default-length 24) + +(defun perf-insert-overlays-scattered (n &optional length) + "Insert N overlays of max length 24 randomly." + (dotimes (_ n) + (let ((begin (random (1+ (point-max))))) + (make-overlay + begin (+ begin (random (1+ (or length perf-insert-overlays-default-length 0)))))))) + +(defvar perf-marker-gc-protection nil) + +(defun perf-insert-marker-scattered (n) + "Insert N marker randomly." + (setq perf-marker-gc-protection nil) + (dotimes (_ n) + (push (copy-marker (random (1+ (point-max)))) + perf-marker-gc-protection))) + +(defun perf-switch-to-buffer-scroll-up-and-down (&optional buffer) + (interactive) + (set-window-buffer nil (or buffer (current-buffer))) + (goto-char (point-min)) + (redisplay t) + (while (condition-case nil + (progn (scroll-up) t) + (end-of-buffer nil)) + (redisplay t)) + (while (condition-case nil + (progn (scroll-down) t) + (beginning-of-buffer nil)) + (redisplay t))) + +(defun perf-emacs-lisp-setup () + (add-to-list 'imenu-generic-expression + '(nil "^\\s-*(perf-define\\(?:\\w\\|\\s_\\)*\\s-*\\(\\(?:\\w\\|\\s_\\)+\\)" 1))) + +(add-hook 'emacs-lisp-mode 'perf-emacs-lisp-setup) + + +;; +===================================================================================+ +;; | Basic performance tests +;; +===================================================================================+ + +(perf-define-variable-test perf-make-overlay (n) + (with-temp-buffer + (overlay-recenter (point-min)) + (benchmark-run 1 + (dotimes (_ n) + (make-overlay 1 1))))) + +(perf-define-variable-test perf-make-overlay-continuous (n) + (with-temp-buffer + (perf-insert-text n) + (overlay-recenter (point-max)) + (benchmark-run 1 + (dotimes (i n) + (make-overlay i (1+ i)))))) + +(perf-define-variable-test perf-make-overlay-scatter (n) + (with-temp-buffer + (perf-insert-text n) + (benchmark-run 1 + (perf-insert-overlays-scattered n)))) + +(perf-define-variable-test perf-delete-overlay (n) + (with-temp-buffer + (let ((ovls (cl-loop for i from 1 to n + collect (make-overlay 1 1)))) + (overlay-recenter (point-min)) + (benchmark-run 1 + (mapc #'delete-overlay ovls))))) + +(perf-define-variable-test perf-delete-overlay-continuous (n) + (with-temp-buffer + (perf-insert-text n) + (let ((ovls (cl-loop for i from 1 to n + collect (make-overlay i (1+ i))))) + (overlay-recenter (point-min)) + (benchmark-run 1 + (mapc #'delete-overlay ovls))))) + +(perf-define-variable-test perf-delete-overlay-scatter (n) + (with-temp-buffer + (perf-insert-text n) + (let ((ovls (progn (perf-insert-overlays-scattered n) + (overlays-in (point-min) (point-max))))) + (benchmark-run 1 + (mapc #'delete-overlay ovls))))) + +(perf-define-variable-test perf-overlays-at (n) + (with-temp-buffer + (perf-insert-text n) + (perf-insert-overlays-scattered n) + (benchmark-run 1 + (dotimes (i (point-max)) + (overlays-at i))))) + +(perf-define-variable-test perf-overlays-in (n) + (with-temp-buffer + (perf-insert-text n) + (perf-insert-overlays-scattered n) + (let ((len perf-insert-overlays-default-length)) + (benchmark-run 1 + (dotimes (i (- (point-max) len)) + (overlays-in i (+ i len))))))) + +(perf-define-variable-test perf-insert-before (n) + (with-temp-buffer + (perf-insert-text n) + (perf-insert-overlays-scattered n) + (goto-char 1) + (overlay-recenter (point-min)) + (benchmark-run 1 + (dotimes (_ (/ n 2)) + (insert ?X))))) + +(perf-define-variable-test perf-insert-before-empty (n) + (let ((perf-insert-overlays-default-length 0)) + (perf-insert-before n))) +(perf-define-variable-test perf-insert-after-empty (n) + (let ((perf-insert-overlays-default-length 0)) + (perf-insert-after n))) +(perf-define-variable-test perf-insert-scatter-empty (n) + (let ((perf-insert-overlays-default-length 0)) + (perf-insert-scatter n))) +(perf-define-variable-test perf-delete-before-empty (n) + (let ((perf-insert-overlays-default-length 0)) + (perf-delete-before n))) +(perf-define-variable-test perf-delete-after-empty (n) + (let ((perf-insert-overlays-default-length 0)) + (perf-delete-after n))) +(perf-define-variable-test perf-delete-scatter-empty (n) + (let ((perf-insert-overlays-default-length 0)) + (perf-delete-scatter n))) + +(defmacro perf-define-marker-test (type where) + (let ((name (intern (format "perf-%s-%s-marker" type where)))) + `(perf-define-variable-test ,name (n) + (with-temp-buffer + (perf-insert-text n) + (perf-insert-marker-scattered n) + (goto-char ,(cl-case where + (after (list 'point-max)) + (t (list 'point-min)))) + (benchmark-run 1 + (dotimes (_ (/ n 2)) + ,@(when (eq where 'scatter) + (list '(goto-char (max 1 (random (point-max)))))) + ,(cl-case type + (insert (list 'insert ?X)) + (delete (list 'delete-char (if (eq where 'after) -1 1)))))))))) + +(perf-define-test-suite perf-marker-suite + (perf-define-marker-test insert before) + (perf-define-marker-test insert after) + (perf-define-marker-test insert scatter) + (perf-define-marker-test delete before) + (perf-define-marker-test delete after) + (perf-define-marker-test delete scatter)) + +(perf-define-variable-test perf-insert-after (n) + (with-temp-buffer + (perf-insert-text n) + (perf-insert-overlays-scattered n) + (goto-char (point-max)) + (overlay-recenter (point-max)) + (benchmark-run 1 + (dotimes (_ (/ n 2)) + (insert ?X))))) + +(perf-define-variable-test perf-insert-scatter (n) + (with-temp-buffer + (perf-insert-text n) + (perf-insert-overlays-scattered n) + (goto-char (point-max)) + (benchmark-run 1 + (dotimes (_ (/ n 2)) + (goto-char (1+ (random (point-max)))) + (insert ?X))))) + +(perf-define-variable-test perf-delete-before (n) + (with-temp-buffer + (perf-insert-text n) + (perf-insert-overlays-scattered n) + (goto-char 1) + (overlay-recenter (point-min)) + (benchmark-run 1 + (dotimes (_ (/ n 2)) + (delete-char 1))))) + +(perf-define-variable-test perf-delete-after (n) + (with-temp-buffer + (perf-insert-text n) + (perf-insert-overlays-scattered n) + (goto-char (point-max)) + (overlay-recenter (point-max)) + (benchmark-run 1 + (dotimes (_ (/ n 2)) + (delete-char -1))))) + +(perf-define-variable-test perf-delete-scatter (n) + (with-temp-buffer + (perf-insert-text n) + (perf-insert-overlays-scattered n) + (goto-char (point-max)) + (benchmark-run 1 + (dotimes (_ (/ n 2)) + (goto-char (max 1 (random (point-max)))) + (delete-char 1))))) + +(perf-define-test-suite perf-insert-delete-suite + 'perf-insert-before + 'perf-insert-after + 'perf-insert-scatter + 'perf-delete-before + 'perf-delete-after + 'perf-delete-scatter + ) + + +;; +===================================================================================+ +;; | Redisplay (new) +;; +===================================================================================+ + +;; 5000 +;; 25000 +;; 75000 + +;; Number of Overlays = N / 2 +;; +;; (except for the hierarchical case, where it is divided by 10.) + + ;; . scrolling through a buffer with lots of overlays that affect faces + ;; of characters in the buffer text + ;; . scrolling through a buffer with lots of overlays that define + ;; 'display' properties which are strings + ;; . scrolling through a buffer with lots of overlays that define + ;; 'invisible' properties + +(perf-define-test-suite perf-display-suite + (perf-define-display-test sequential display scroll) + (perf-define-display-test sequential display random) + (perf-define-display-test sequential face scroll) + (perf-define-display-test sequential face random) + (perf-define-display-test sequential invisible scroll) + (perf-define-display-test sequential invisible random) + (perf-define-display-test random display scroll) + (perf-define-display-test random display random) + (perf-define-display-test random face scroll) + (perf-define-display-test random face random) + (perf-define-display-test random invisible scroll) + (perf-define-display-test random invisible random)) + +;; |------------| +;; |--------| +;; |----| +(perf-define-display-test hierarchical face scroll) + + + + +;; +===================================================================================+ +;; | Real World +;; +===================================================================================+ + +(require 'python) + +(defconst perf-many-errors-file + (expand-file-name "many-errors.py" + (and load-file-name (file-name-directory load-file-name)))) + +(perf-define-constant-test perf-realworld-flycheck + (interactive) + (package-initialize) + (when (and (require 'flycheck nil t) + (file-exists-p perf-many-errors-file) + (or (executable-find "pylint") + (executable-find "flake8"))) + (setq flycheck-python-pylint-executable + (executable-find "pylint")) + (setq flycheck-python-flake8-executable + (executable-find "flake8")) + (setq python-indent-guess-indent-offset-verbose nil) + (setq flycheck-check-syntax-automatically nil) + (setq flycheck-checker-error-threshold nil) + (setq flycheck-display-errors-function nil) + (with-current-buffer (find-file-noselect perf-many-errors-file) + (let* ((done) + (flycheck-after-syntax-check-hook + (list (lambda () (setq done t))))) + (flycheck-mode 1) + (flycheck-buffer) + (benchmark-run 1 + (while (not done) + (accept-process-output)) + (perf-switch-to-buffer-scroll-up-and-down) + (flycheck-mode -1)))))) + +;; https://lists.gnu.org/archive/html/emacs-devel/2009-04/msg00242.html +(defun make-lines-invisible (regexp &optional arg) + "Make all lines matching a regexp invisible and intangible. +With a prefix arg, make it visible again. It is not necessary +that REGEXP matches the whole line; if a hit is found, the +affected line gets automatically selected. + +This command affects the whole buffer." + (interactive "MRegexp: \nP") + (let (ov + ovs + count) + (cond + ((equal arg '(4)) + (setq ovs (overlays-in (point-min) (point-max))) + (mapc (lambda (o) + (if (overlay-get o 'make-lines-invisible) + (delete-overlay o))) + ovs)) + (t + (save-excursion + (goto-char (point-min)) + (setq count 0) + (while (re-search-forward regexp nil t) + (setq count (1+ count)) + (if (= (% count 100) 0) + (message "%d" count)) + (setq ov (make-overlay (line-beginning-position) + (1+ (line-end-position)))) + (overlay-put ov 'make-lines-invisible t) + (overlay-put ov 'invisible t) + (overlay-put ov 'intangible t) + (goto-char (line-end-position)))))))) + +(perf-define-constant-test perf-realworld-make-lines-invisible + (with-temp-buffer + (insert-file-contents "/usr/share/dict/words") + (set-window-buffer nil (current-buffer)) + (redisplay t) + (overlay-recenter (point-max)) + (benchmark-run 1 + (make-lines-invisible "a")))) + +(perf-define-constant-test perf-realworld-line-numbering + (interactive) + (with-temp-buffer + (insert-file-contents "/usr/share/dict/words") + (overlay-recenter (point-max)) + (goto-char (point-min)) + (let* ((nlines (count-lines (point-min) (point-max))) + (line 1) + (width 0)) + (dotimes (i nlines) ;;-with-progress-reporter "Creating overlays" + (let ((ov (make-overlay (point) (point))) + (str (propertize (format "%04d" line) 'face 'shadow))) + (overlay-put ov 'before-string + (propertize " " 'display `((margin left-margin) ,str))) + (setq width (max width (length str))) + (cl-incf line) + (forward-line))) + (benchmark-run 1 + (let ((left-margin-width width)) + (perf-switch-to-buffer-scroll-up-and-down)))))) + +(perf-define-test-suite perf-realworld-suite + 'perf-realworld-flycheck + 'perf-realworld-make-lines-invisible + 'perf-realworld-line-numbering) + + +;; +===================================================================================+ +;; | next-overlay-change +;; +===================================================================================+ + +(perf-define-variable-test perf-noc-hierarchical/forward/linear (n) + "Search linear for the next change on every line." + (with-temp-buffer + (perf-insert-lines (* 3 n)) + (perf-insert-overlays-hierarchical n) + (goto-char (point-min)) + (benchmark-run 1 + (while (not (eobp)) + (next-overlay-change (point)) + (forward-line))))) + +(perf-define-variable-test perf-noc-sequential/forward/linear (n) + "Search linear for the next change on every line." + (with-temp-buffer + (perf-insert-lines (* 3 n)) + (perf-insert-overlays-sequential n) + (goto-char (point-min)) + (benchmark-run 1 + (while (not (eobp)) + (next-overlay-change (point)) + (forward-line))))) + +(perf-define-variable-test perf-noc-hierarchical/forward/backnforth (n) + "Search back and forth for the next change from `point-min' to `point-max'." + (with-temp-buffer + (perf-insert-lines (* 3 n)) + (overlay-recenter (point-max)) + (perf-insert-overlays-hierarchical n) + (goto-char (point-min)) + (benchmark-run 1 + (while (not (eobp)) + (next-overlay-change (point)) + (next-overlay-change (+ (point) 2)) + (forward-char))))) + +(perf-define-test-suite perf-noc-suite + 'perf-noc-hierarchical/forward/linear + 'perf-noc-hierarchical/forward/backnforth + 'perf-noc-hierarchical/forward/backnforth) diff --git a/test/src/buffer-tests.el b/test/src/buffer-tests.el index 834acaf66f..71f8705c6f 100644 --- a/test/src/buffer-tests.el +++ b/test/src/buffer-tests.el @@ -20,6 +20,7 @@ ;;; Code: (require 'ert) +(require 'seq) (ert-deftest overlay-modification-hooks-message-other-buf () "Test for bug#21824. @@ -50,4 +51,7522 @@ with parameters from the *Messages* buffer modification." (progn (get-buffer-create "nil") (generate-new-buffer-name "nil"))))) + +;; +===================================================================================+ +;; | Overlay test setup +;; +===================================================================================+ + +(eval-when-compile + (defun make-overlay-test-name (fn x y) + (intern (format "test-%s-%s-%s" fn x y)))) + +(defun unmake-ov-test-name (symbol) + (let ((name (if (stringp symbol) symbol (symbol-name symbol)))) + (when (string-match "\\`test-\\(.*\\)-\\(.*\\)-\\(.*\\)\\'" name) + (list (match-string 1 name) (match-string 2 name) (match-string 3 name))))) + +(defmacro deftest-make-overlay-1 (id args) + (declare (indent 1)) + `(ert-deftest ,(make-overlay-test-name 'make-overlay 1 id) () + (with-temp-buffer + (should ,(cons 'make-overlay args))))) + +(defmacro deftest-make-overlay-2 (id args condition) + (declare (indent 1)) + `(ert-deftest ,(make-overlay-test-name 'make-overlay 2 id) () + (with-temp-buffer + (should-error + ,(cons 'make-overlay args) + :type ',condition + :exclude-subtypes t)))) + +(defmacro deftest-overlay-start/end-1 (id start-end-args start-end-should) + (declare (indent 1)) + (cl-destructuring-bind (start end sstart send) + (append start-end-args start-end-should) + `(ert-deftest ,(make-overlay-test-name 'overlay-start/end 1 id) () + (with-temp-buffer + (insert (make-string 9 ?\n)) + (let ((ov (make-overlay ,start ,end))) + (should (equal ,sstart (overlay-start ov))) + (should (equal ,send (overlay-end ov)))))))) + +(defmacro deftest-overlay-buffer-1 (id arg-expr should-expr) + (declare (indent 1)) + `(ert-deftest ,(make-overlay-test-name 'overlay-buffer 1 id) () + (with-temp-buffer + (should (equal (overlay-buffer (make-overlay 1 1 ,arg-expr)) + ,should-expr))))) + +(defmacro deftest-overlayp-1 (id arg-expr should-expr) + (declare (indent 1)) + `(ert-deftest ,(make-overlay-test-name 'overlay-buffer 1 id) () + (with-temp-buffer + (should (equal ,should-expr (overlayp ,arg-expr)))))) + +(defmacro deftest-next-overlay-change-1 (id pos result &rest ov-tuple) + `(ert-deftest ,(make-overlay-test-name 'next-overlay-change 1 id) () + (let ((tuple (copy-sequence ',ov-tuple))) + (with-temp-buffer + (insert (make-string (max 100 (if tuple + (apply #'max + (mapcar + (lambda (m) (apply #'max m)) tuple)) + 0)) + ?\n)) + (dolist (tup tuple) + (make-overlay (car tup) (cadr tup))) + (should (equal (next-overlay-change ,pos) + ,result)))))) + +(defmacro deftest-previous-overlay-change-1 (id pos result &rest ov-tuple) + `(ert-deftest ,(make-overlay-test-name 'previous-overlay-change 1 id) () + (let ((tuple ',ov-tuple)) + (with-temp-buffer + (insert (make-string (max 100 (if tuple + (apply #'max + (mapcar + (lambda (m) (apply #'max m)) tuple)) + 0)) + ?\n)) + (dolist (tup tuple) + (make-overlay (car tup) (cadr tup))) + (should (equal (previous-overlay-change ,pos) + ,result)))))) + +(defmacro deftest-overlays-at-1 (id pos result &rest ov-triple) + `(ert-deftest ,(make-overlay-test-name 'overlays-at 1 id) () + (let ((pos* ,pos)) + (with-temp-buffer + (insert (make-string 100 ?\s)) + (should-not (memq nil ',result)) + (dolist (v ',ov-triple) + (cl-destructuring-bind (tag start end) + v + (overlay-put (make-overlay start end) 'tag tag))) + (let ((ovl (overlays-at pos*))) + (should (equal (length ovl) (length ',result))) + (dolist (ov ovl) + (should (memq (overlay-get ov 'tag) ',result)))))))) + +(defmacro deftest-overlays-in-1 (id beg end result &rest ov-triple) + `(ert-deftest ,(make-overlay-test-name 'overlays-in 1 id) () + (let ((beg* ,beg) + (end* ,end)) + (with-temp-buffer + (insert (make-string 100 ?\s)) + (should-not (memq nil ',result)) + (dolist (v ',ov-triple) + (cl-destructuring-bind (tag start end) + v + (overlay-put (make-overlay start end) 'tag tag))) + (let ((ovl (overlays-in beg* end*))) + (should (equal (length ovl) (length ',result))) + (dolist (ov ovl) + (should (memq (overlay-get ov 'tag) ',result)))))))) + +(defmacro test-with-overlay-in-buffer (symbol-beg-end-fa-ra &rest body) + (declare (indent 1)) + (cl-destructuring-bind (symbol beg end &optional fa ra) + symbol-beg-end-fa-ra + `(with-temp-buffer + (insert (make-string (max 1000 (1- ,end)) ?\s)) + (goto-char 1) + (let ((,symbol (make-overlay ,beg ,end nil ,fa ,ra))) + ,@body)))) + +(defmacro deftest-overlays-equal-1 (id result ov1-args ov2-args) + `(ert-deftest ,(make-overlay-test-name 'overlays-equal 1 id) () + (cl-labels ((create-overlay (args) + (cl-destructuring-bind (start end &optional fa ra &rest properties) + args + (let ((ov (make-overlay start end nil fa ra))) + (while properties + (overlay-put ov (pop properties) (pop properties))) + ov)))) + (with-temp-buffer + (insert (make-string 1024 ?\s)) + (should (,(if result 'identity 'not) + (equal (create-overlay ',ov1-args) + (create-overlay ',ov2-args)))))))) + + +(defun find-ert-overlay-test (name) + (let ((test (unmake-ov-test-name name))) + (or (and test + (cl-destructuring-bind (fn x y) + test + (let ((regexp (format "deftest-%s-%s +%s" fn x y))) + (re-search-forward regexp nil t)))) + (let ((find-function-regexp-alist + (cl-remove 'find-ert-overlay-test find-function-regexp-alist :key #'cdr))) + (find-function-do-it name 'ert-deftest 'switch-to-buffer-other-window))))) + +(add-to-list 'find-function-regexp-alist + '(ert-deftest . find-ert-overlay-test)) + + +;; +===================================================================================+ +;; | make-overlay +;; +===================================================================================+ + +;; Test if making an overlay succeeds. +(deftest-make-overlay-1 A (1 1)) +(deftest-make-overlay-1 B (7 26)) +(deftest-make-overlay-1 C (29 7)) +(deftest-make-overlay-1 D (most-positive-fixnum 1)) +(deftest-make-overlay-1 E (most-negative-fixnum 1)) +(deftest-make-overlay-1 F (1 most-positive-fixnum)) +(deftest-make-overlay-1 G (1 most-negative-fixnum)) +(deftest-make-overlay-1 H (1 1 nil t)) +(deftest-make-overlay-1 I (1 1 nil nil)) +(deftest-make-overlay-1 J (1 1 nil nil nil)) +(deftest-make-overlay-1 K (1 1 nil nil t)) +(deftest-make-overlay-1 L (1 1 nil t t)) +(deftest-make-overlay-1 M (1 1 nil "yes" "yes")) + +;; Test if trying to make an overlay signals conditions. +(deftest-make-overlay-2 A () wrong-number-of-arguments) +(deftest-make-overlay-2 B (1) wrong-number-of-arguments) +(deftest-make-overlay-2 C (1 2 3 4 5 6) wrong-number-of-arguments) +(deftest-make-overlay-2 D ("1") wrong-number-of-arguments) +(deftest-make-overlay-2 E ("1" "2") wrong-type-argument) +(deftest-make-overlay-2 F (1 2 "b") wrong-type-argument) +(deftest-make-overlay-2 G (1 2 3.14) wrong-type-argument) +(deftest-make-overlay-2 H (3.14 3) wrong-type-argument) +(deftest-make-overlay-2 I (1 [1]) wrong-type-argument) +(deftest-make-overlay-2 J (1 1 (with-temp-buffer + (current-buffer))) + error) + + +;; +===================================================================================+ +;; | overlay-start/end +;; +===================================================================================+ + +;; Test if the overlays return proper positions. point-max of the +;; buffer will equal 10. ARG RESULT +(deftest-overlay-start/end-1 A (1 1) (1 1)) +(deftest-overlay-start/end-1 B (2 7) (2 7)) +(deftest-overlay-start/end-1 C (7 2) (2 7)) +(deftest-overlay-start/end-1 D (1 10) (1 10)) +(deftest-overlay-start/end-1 E (1 11) (1 10)) +(deftest-overlay-start/end-1 F (1 most-positive-fixnum) (1 10)) +(deftest-overlay-start/end-1 G (most-positive-fixnum 1) (1 10)) +(deftest-overlay-start/end-1 H (most-positive-fixnum most-positive-fixnum) (10 10)) +(deftest-overlay-start/end-1 I (100 11) (10 10)) +(deftest-overlay-start/end-1 J (11 100) (10 10)) +(deftest-overlay-start/end-1 K (0 1) (1 1)) +(deftest-overlay-start/end-1 L (1 0) (1 1)) +(deftest-overlay-start/end-1 M (0 0) (1 1)) + +(ert-deftest test-overlay-start/end-2 () + (should-not (overlay-start (with-temp-buffer (make-overlay 1 1)))) + (should-not (overlay-end (with-temp-buffer (make-overlay 1 1))))) + + +;; +===================================================================================+ +;; | overlay-buffer +;; +===================================================================================+ + +;; Test if overlay-buffer returns appropriate values. +(deftest-overlay-buffer-1 A (current-buffer) (current-buffer)) +(deftest-overlay-buffer-1 B nil (current-buffer)) +(ert-deftest test-overlay-buffer-1-C () + (should-error (make-overlay + 1 1 (with-temp-buffer (current-buffer))))) + + +;; +===================================================================================+ +;; | overlayp +;; +===================================================================================+ + +;; Check the overlay predicate. +(deftest-overlayp-1 A (make-overlay 1 1) t) +(deftest-overlayp-1 B (with-temp-buffer (make-overlay 1 1)) t) +(deftest-overlayp-1 C nil nil) +(deftest-overlayp-1 D 'symbol nil) +(deftest-overlayp-1 E "string" nil) +(deftest-overlayp-1 F 42 nil) +(deftest-overlayp-1 G [1 2] nil) +(deftest-overlayp-1 H (symbol-function 'car) nil) +(deftest-overlayp-1 I float-pi nil) +(deftest-overlayp-1 J (cons 1 2) nil) +(deftest-overlayp-1 K (make-hash-table) nil) +(deftest-overlayp-1 L (symbol-function 'ert-deftest) nil) +(deftest-overlayp-1 M (current-buffer) nil) +(deftest-overlayp-1 N (selected-window) nil) +(deftest-overlayp-1 O (selected-frame) nil) + + +;; +===================================================================================+ +;; | overlay equality +;; +===================================================================================+ + +(deftest-overlays-equal-1 A t (1 1) (1 1)) +(deftest-overlays-equal-1 B t (5 10) (5 10)) +(deftest-overlays-equal-1 C nil (5 11) (5 10)) +(deftest-overlays-equal-1 D t (10 20 t) (10 20)) +(deftest-overlays-equal-1 E t (10 20 nil t) (10 20)) +(deftest-overlays-equal-1 F t (10 20 t t) (10 20 nil t)) +(deftest-overlays-equal-1 G t (10 20 t t) (10 20 t nil)) +(deftest-overlays-equal-1 H t (10 20 nil nil foo 42) (10 20 nil nil foo 42)) +(deftest-overlays-equal-1 I nil (10 20 nil nil foo 42) (10 20 nil nil foo 43)) + + +;; +===================================================================================+ +;; | overlay-lists +;; +===================================================================================+ + +;; Check whether overlay-lists returns something sensible. +(ert-deftest test-overlay-lists-1 () + (with-temp-buffer + (should (equal (cons nil nil) (overlay-lists))) + (dotimes (i 10) (make-overlay 1 i)) + (should (listp (car (overlay-lists)))) + (should (listp (cdr (overlay-lists)))) + (let ((list (append (car (overlay-lists)) + (cdr (overlay-lists))))) + (should (= 10 (length list))) + (should (seq-every-p #'overlayp list))))) + + +;; +===================================================================================+ +;; | overlay-put/get/properties +;; +===================================================================================+ + +;; Test if overlay-put properties can be retrieved by overlay-get and +;; overlay-properties. +(ert-deftest test-overlay-props-1 () + (with-temp-buffer + (let* ((keys '(:k1 :k2 :k3)) + (values '(1 "v2" v3)) + (ov (make-overlay 1 1)) + (n (length keys))) + (should (equal (length keys) (length values))) + (should (null (overlay-properties ov))) + ;; Insert keys and values. + (dotimes (i n) + (should (equal (overlay-put ov (nth i keys) (nth i values)) + (nth i values)))) + ;; Compare with what overlay-get says. + (dotimes (i n) + (should (equal (overlay-get ov (nth i keys)) + (nth i values)))) + ;; Test if overlay-properties is a superset. + (dotimes (i n) + (should (equal (plist-get (overlay-properties ov) + (nth i keys)) + (nth i values)))) + ;; Check if overlay-properties is a subset. + (should (= (length (overlay-properties ov)) (* n 2)))))) + + +;; +===================================================================================+ +;; | next-overlay-change +;; +===================================================================================+ + +;; Test if next-overlay-change returns RESULT if called with POS in a +;; buffer with overlays corresponding to OVS and point-max >= 100. +;; (POS RESULT &rest OVS) +;; 0 overlays +(deftest-next-overlay-change-1 A (point-min) (point-max)) +(deftest-next-overlay-change-1 B (point-max) (point-max)) +;; 1 non-empty overlay +(deftest-next-overlay-change-1 C 1 10 (10 20)) +(deftest-next-overlay-change-1 D 10 20 (10 20)) +(deftest-next-overlay-change-1 E 15 20 (10 20)) +(deftest-next-overlay-change-1 F 20 (point-max) (10 20)) +(deftest-next-overlay-change-1 G 30 (point-max) (10 20)) +;; 1 empty overlay +(deftest-next-overlay-change-1 H 1 10 (10 10)) +(deftest-next-overlay-change-1 I 10 (point-max) (10 10)) +(deftest-next-overlay-change-1 J 20 (point-max) (10 10)) +;; 2 non-empty, non-intersecting +(deftest-next-overlay-change-1 D 10 20 (20 30) (40 50)) +(deftest-next-overlay-change-1 E 35 40 (20 30) (40 50)) +(deftest-next-overlay-change-1 F 60 (point-max) (20 30) (40 50)) +(deftest-next-overlay-change-1 G 30 40 (20 30) (40 50)) +(deftest-next-overlay-change-1 H 50 (point-max) (20 30) (40 50)) +;; 2 non-empty, intersecting +(deftest-next-overlay-change-1 I 10 20 (20 30) (25 35)) +(deftest-next-overlay-change-1 J 20 25 (20 30) (25 35)) +(deftest-next-overlay-change-1 K 23 25 (20 30) (25 35)) +(deftest-next-overlay-change-1 L 25 30 (20 30) (25 35)) +(deftest-next-overlay-change-1 M 28 30 (20 30) (25 35)) +(deftest-next-overlay-change-1 N 30 35 (20 30) (25 35)) +(deftest-next-overlay-change-1 O 35 (point-max) (20 30) (25 35)) +(deftest-next-overlay-change-1 P 50 (point-max) (20 30) (25 35)) +;; 2 non-empty, continuous +(deftest-next-overlay-change-1 Q 10 20 (20 30) (30 40)) +(deftest-next-overlay-change-1 R 20 30 (20 30) (30 40)) +(deftest-next-overlay-change-1 S 25 30 (20 30) (30 40)) +(deftest-next-overlay-change-1 T 30 40 (20 30) (30 40)) +(deftest-next-overlay-change-1 U 35 40 (20 30) (30 40)) +(deftest-next-overlay-change-1 V 40 (point-max) (20 30) (30 40)) +(deftest-next-overlay-change-1 W 50 (point-max) (20 30) (30 40)) +;; 1 empty, 1 non-empty, non-in +(deftest-next-overlay-change-1 a 10 20 (20 20) (30 40)) +(deftest-next-overlay-change-1 b 20 30 (20 30) (30 40)) +(deftest-next-overlay-change-1 c 25 30 (20 30) (30 40)) +(deftest-next-overlay-change-1 d 30 40 (20 30) (30 40)) +(deftest-next-overlay-change-1 e 35 40 (20 30) (30 40)) +(deftest-next-overlay-change-1 f 40 (point-max) (20 30) (30 40)) +(deftest-next-overlay-change-1 g 50 (point-max) (20 30) (30 40)) +;; 1 empty, 1 non-empty, intersecting at begin +(deftest-next-overlay-change-1 h 10 20 (20 20) (20 30)) +(deftest-next-overlay-change-1 i 20 30 (20 20) (20 30)) +(deftest-next-overlay-change-1 j 25 30 (20 20) (20 30)) +(deftest-next-overlay-change-1 k 30 (point-max) (20 20) (20 30)) +(deftest-next-overlay-change-1 l 40 (point-max) (20 20) (20 30)) +;; 1 empty, 1 non-empty, intersecting at end +(deftest-next-overlay-change-1 h 10 20 (30 30) (20 30)) +(deftest-next-overlay-change-1 i 20 30 (30 30) (20 30)) +(deftest-next-overlay-change-1 j 25 30 (30 30) (20 30)) +(deftest-next-overlay-change-1 k 30 (point-max) (20 20) (20 30)) +(deftest-next-overlay-change-1 l 40 (point-max) (20 20) (20 30)) +;; 1 empty, 1 non-empty, intersecting in the middle +(deftest-next-overlay-change-1 m 10 20 (25 25) (20 30)) +(deftest-next-overlay-change-1 n 20 25 (25 25) (20 30)) +(deftest-next-overlay-change-1 o 25 30 (25 25) (20 30)) +(deftest-next-overlay-change-1 p 30 (point-max) (25 25) (20 30)) +(deftest-next-overlay-change-1 q 40 (point-max) (25 25) (20 30)) +;; 2 empty, intersecting +(deftest-next-overlay-change-1 r 10 20 (20 20) (20 20)) +(deftest-next-overlay-change-1 s 20 (point-max) (20 20) (20 20)) +(deftest-next-overlay-change-1 t 30 (point-max) (20 20) (20 20)) +;; 2 empty, non-intersecting +(deftest-next-overlay-change-1 u 10 20 (20 20) (30 30)) +(deftest-next-overlay-change-1 v 20 30 (20 20) (30 30)) +(deftest-next-overlay-change-1 w 25 30 (20 20) (30 30)) +(deftest-next-overlay-change-1 x 30 (point-max) (20 20) (30 30)) +(deftest-next-overlay-change-1 y 50 (point-max) (20 20) (30 30)) +;; 10 random +(deftest-next-overlay-change-1 aa 1 5 + (58 66) (41 10) (9 67) (28 88) (27 43) + (24 27) (48 36) (5 90) (61 9)) +(deftest-next-overlay-change-1 ab (point-max) (point-max) + (58 66) (41 10) (9 67) (28 88) (27 43) + (24 27) (48 36) (5 90) (61 9)) +(deftest-next-overlay-change-1 ac 67 88 + (58 66) (41 10) (9 67) (28 88) (27 43) + (24 27) (48 36) (5 90) (61 9)) + + +;; +===================================================================================+ +;; | previous-overlay-change. +;; +===================================================================================+ + +;; Same for previous-overlay-change. +;; 1 non-empty overlay +(deftest-previous-overlay-change-1 A (point-max) 1) +(deftest-previous-overlay-change-1 B 1 1) +(deftest-previous-overlay-change-1 C 1 1 (10 20)) +(deftest-previous-overlay-change-1 D 10 1 (10 20)) +(deftest-previous-overlay-change-1 E 15 10 (10 20)) +(deftest-previous-overlay-change-1 F 20 10 (10 20)) +(deftest-previous-overlay-change-1 G 30 20 (10 20)) +;; 1 empty overlay +(deftest-previous-overlay-change-1 H 1 1 (10 10)) +(deftest-previous-overlay-change-1 I 10 1 (10 10)) +(deftest-previous-overlay-change-1 J 20 10 (10 10)) +;; 2 non-empty, non-intersecting +(deftest-previous-overlay-change-1 D 10 1 (20 30) (40 50)) +(deftest-previous-overlay-change-1 E 35 30 (20 30) (40 50)) +(deftest-previous-overlay-change-1 F 60 50 (20 30) (40 50)) +(deftest-previous-overlay-change-1 G 30 20 (20 30) (40 50)) +(deftest-previous-overlay-change-1 H 50 40 (20 30) (40 50)) +;; 2 non-empty, intersecting +(deftest-previous-overlay-change-1 I 10 1 (20 30) (25 35)) +(deftest-previous-overlay-change-1 J 20 1 (20 30) (25 35)) +(deftest-previous-overlay-change-1 K 23 20 (20 30) (25 35)) +(deftest-previous-overlay-change-1 L 25 20 (20 30) (25 35)) +(deftest-previous-overlay-change-1 M 28 25 (20 30) (25 35)) +(deftest-previous-overlay-change-1 N 30 25 (20 30) (25 35)) +(deftest-previous-overlay-change-1 O 35 30 (20 30) (25 35)) +(deftest-previous-overlay-change-1 P 50 35 (20 30) (25 35)) +;; 2 non-empty, continuous +(deftest-previous-overlay-change-1 Q 10 1 (20 30) (30 40)) +(deftest-previous-overlay-change-1 R 20 1 (20 30) (30 40)) +(deftest-previous-overlay-change-1 S 25 20 (20 30) (30 40)) +(deftest-previous-overlay-change-1 T 30 20 (20 30) (30 40)) +(deftest-previous-overlay-change-1 U 35 30 (20 30) (30 40)) +(deftest-previous-overlay-change-1 V 40 30 (20 30) (30 40)) +(deftest-previous-overlay-change-1 W 50 40 (20 30) (30 40)) +;; 1 empty, 1 non-empty, non-intersecting +(deftest-previous-overlay-change-1 a 10 1 (20 20) (30 40)) +(deftest-previous-overlay-change-1 b 20 1 (20 30) (30 40)) +(deftest-previous-overlay-change-1 c 25 20 (20 30) (30 40)) +(deftest-previous-overlay-change-1 d 30 20 (20 30) (30 40)) +(deftest-previous-overlay-change-1 e 35 30 (20 30) (30 40)) +(deftest-previous-overlay-change-1 f 40 30 (20 30) (30 40)) +(deftest-previous-overlay-change-1 g 50 40 (20 30) (30 40)) +;; 1 empty, 1 non-empty, intersecting at begin +(deftest-previous-overlay-change-1 h 10 1 (20 20) (20 30)) +(deftest-previous-overlay-change-1 i 20 1 (20 20) (20 30)) +(deftest-previous-overlay-change-1 j 25 20 (20 20) (20 30)) +(deftest-previous-overlay-change-1 k 30 20 (20 20) (20 30)) +(deftest-previous-overlay-change-1 l 40 30 (20 20) (20 30)) +;; 1 empty, 1 non-empty, intersecting at end +(deftest-previous-overlay-change-1 m 10 1 (30 30) (20 30)) +(deftest-previous-overlay-change-1 n 20 1 (30 30) (20 30)) +(deftest-previous-overlay-change-1 o 25 20 (30 30) (20 30)) +(deftest-previous-overlay-change-1 p 30 20 (20 20) (20 30)) +(deftest-previous-overlay-change-1 q 40 30 (20 20) (20 30)) +;; 1 empty, 1 non-empty, intersectig in the middle +(deftest-previous-overlay-change-1 r 10 1 (25 25) (20 30)) +(deftest-previous-overlay-change-1 s 20 1 (25 25) (20 30)) +(deftest-previous-overlay-change-1 t 25 20 (25 25) (20 30)) +(deftest-previous-overlay-change-1 u 30 25 (25 25) (20 30)) +(deftest-previous-overlay-change-1 v 40 30 (25 25) (20 30)) +;; 2 empty, intersecting +(deftest-previous-overlay-change-1 w 10 1 (20 20) (20 20)) +(deftest-previous-overlay-change-1 x 20 1 (20 20) (20 20)) +(deftest-previous-overlay-change-1 y 30 20 (20 20) (20 20)) +;; 2 empty, non-intersecting +(deftest-previous-overlay-change-1 z 10 1 (20 20) (30 30)) +(deftest-previous-overlay-change-1 aa 20 1 (20 20) (30 30)) +(deftest-previous-overlay-change-1 ab 25 20 (20 20) (30 30)) +(deftest-previous-overlay-change-1 ac 30 20 (20 20) (30 30)) +(deftest-previous-overlay-change-1 ad 50 30 (20 20) (30 30)) +;; 10 random +(deftest-previous-overlay-change-1 ae 100 90 + (58 66) (41 10) (9 67) (28 88) (27 43) + (24 27) (48 36) (5 90) (61 9)) +(deftest-previous-overlay-change-1 af (point-min) (point-min) + (58 66) (41 10) (9 67) (28 88) (27 43) + (24 27) (48 36) (5 90) (61 9)) +(deftest-previous-overlay-change-1 ag 29 28 + (58 66) (41 10) (9 67) (28 88) (27 43) + (24 27) (48 36) (5 90) (61 9)) + + +;; +===================================================================================+ +;; | overlays-at +;; +===================================================================================+ + + +;; Test whether overlay-at returns RESULT at POS after overlays OVL were +;; created in a buffer. POS RES OVL +(deftest-overlays-at-1 A 1 ()) +;; 1 overlay +(deftest-overlays-at-1 B 10 (a) (a 10 20)) +(deftest-overlays-at-1 C 15 (a) (a 10 20)) +(deftest-overlays-at-1 D 19 (a) (a 10 20)) +(deftest-overlays-at-1 E 20 () (a 10 20)) +(deftest-overlays-at-1 F 1 () (a 10 20)) + +;; 2 non-empty overlays non-intersecting +(deftest-overlays-at-1 G 1 () (a 10 20) (b 30 40)) +(deftest-overlays-at-1 H 10 (a) (a 10 20) (b 30 40)) +(deftest-overlays-at-1 I 15 (a) (a 10 20) (b 30 40)) +(deftest-overlays-at-1 K 20 () (a 10 20) (b 30 40)) +(deftest-overlays-at-1 L 25 () (a 10 20) (b 30 40)) +(deftest-overlays-at-1 M 30 (b) (a 10 20) (b 30 40)) +(deftest-overlays-at-1 N 35 (b) (a 10 20) (b 30 40)) +(deftest-overlays-at-1 O 40 () (a 10 20) (b 30 40)) +(deftest-overlays-at-1 P 50 () (a 10 20) (b 30 40)) + +;; 2 non-empty overlays intersecting +(deftest-overlays-at-1 G 1 () (a 10 30) (b 20 40)) +(deftest-overlays-at-1 H 10 (a) (a 10 30) (b 20 40)) +(deftest-overlays-at-1 I 15 (a) (a 10 30) (b 20 40)) +(deftest-overlays-at-1 K 20 (a b) (a 10 30) (b 20 40)) +(deftest-overlays-at-1 L 25 (a b) (a 10 30) (b 20 40)) +(deftest-overlays-at-1 M 30 (b) (a 10 30) (b 20 40)) +(deftest-overlays-at-1 N 35 (b) (a 10 30) (b 20 40)) +(deftest-overlays-at-1 O 40 () (a 10 30) (b 20 40)) +(deftest-overlays-at-1 P 50 () (a 10 30) (b 20 40)) + +;; 2 non-empty overlays continuous +(deftest-overlays-at-1 G 1 () (a 10 20) (b 20 30)) +(deftest-overlays-at-1 H 10 (a) (a 10 20) (b 20 30)) +(deftest-overlays-at-1 I 15 (a) (a 10 20) (b 20 30)) +(deftest-overlays-at-1 K 20 (b) (a 10 20) (b 20 30)) +(deftest-overlays-at-1 L 25 (b) (a 10 20) (b 20 30)) +(deftest-overlays-at-1 M 30 () (a 10 20) (b 20 30)) + +;; overlays-at never returns empty overlays. +(deftest-overlays-at-1 N 1 (a) (a 1 60) (c 1 1) (b 30 30) (d 50 50)) +(deftest-overlays-at-1 O 20 (a) (a 1 60) (c 1 1) (b 30 30) (d 50 50)) +(deftest-overlays-at-1 P 30 (a) (a 1 60) (c 1 1) (b 30 30) (d 50 50)) +(deftest-overlays-at-1 Q 40 (a) (a 1 60) (c 1 1) (b 30 30) (d 50 50)) +(deftest-overlays-at-1 R 50 (a) (a 1 60) (c 1 1) (b 30 30) (d 50 50)) +(deftest-overlays-at-1 S 60 () (a 1 60) (c 1 1) (b 30 30) (d 50 50)) + +;; behaviour at point-min and point-max +(ert-deftest test-overlays-at-2 () + (cl-macrolet ((should-length (n list) + `(should (= ,n (length ,list))))) + (with-temp-buffer + (insert (make-string 100 ?\s)) + (make-overlay 1 (point-max)) + (make-overlay 10 10) + (make-overlay 20 20) + (make-overlay (point-max) (point-max)) + (should-length 1 (overlays-at 1)) + (should-length 1 (overlays-at 10)) + (should-length 1 (overlays-at 20)) + (should-length 0 (overlays-at (point-max))) + (narrow-to-region 10 20) + (should-length 1 (overlays-at (point-min))) + (should-length 1 (overlays-at 15)) + (should-length 1 (overlays-at (point-max)))))) + + +;; +===================================================================================+ +;; | overlay-in +;; +===================================================================================+ + + +;; Test whether overlays-in returns RES in BEG,END after overlays OVL were +;; created in a buffer. + +(deftest-overlays-in-1 A 1 (point-max) ());;POS RES OVL +;; 1 overlay +(deftest-overlays-in-1 B 1 10 () (a 10 20)) +(deftest-overlays-in-1 C 5 10 () (a 10 20)) +(deftest-overlays-in-1 D 5 15 (a) (a 10 20)) +(deftest-overlays-in-1 E 10 15 (a) (a 10 20)) +(deftest-overlays-in-1 F 10 20 (a) (a 10 20)) +(deftest-overlays-in-1 G 15 20 (a) (a 10 20)) +(deftest-overlays-in-1 H 15 25 (a) (a 10 20)) +(deftest-overlays-in-1 I 20 25 () (a 10 20)) +(deftest-overlays-in-1 J 30 50 () (a 10 20)) + +;; 2 non-empty overlays non-intersecting +(deftest-overlays-in-1 K 1 5 () (a 10 20) (b 30 40)) +(deftest-overlays-in-1 L 5 10 () (a 10 20) (b 30 40)) +(deftest-overlays-in-1 M 5 15 (a) (a 10 20) (b 30 40)) +(deftest-overlays-in-1 N 10 15 (a) (a 10 20) (b 30 40)) +(deftest-overlays-in-1 O 15 20 (a) (a 10 20) (b 30 40)) +(deftest-overlays-in-1 P 15 25 (a) (a 10 20) (b 30 40)) +(deftest-overlays-in-1 Q 20 25 () (a 10 20) (b 30 40)) +(deftest-overlays-in-1 R 20 30 () (a 10 20) (b 30 40)) +(deftest-overlays-in-1 S 25 30 () (a 10 20) (b 30 40)) +(deftest-overlays-in-1 T 25 35 (b) (a 10 20) (b 30 40)) +(deftest-overlays-in-1 U 30 35 (b) (a 10 20) (b 30 40)) +(deftest-overlays-in-1 V 40 50 () (a 10 20) (b 30 40)) +(deftest-overlays-in-1 W 50 60 () (a 10 20) (b 30 40)) +(deftest-overlays-in-1 X 1 50 (a b) (a 10 20) (b 30 40)) +(deftest-overlays-in-1 Y 10 40 (a b) (a 10 20) (b 30 40)) +(deftest-overlays-in-1 Z 10 41 (a b) (a 10 20) (b 30 40)) + +;; 2 non-empty overlays intersecting +(deftest-overlays-in-1 a 1 5 () (a 10 30) (b 20 40)) +(deftest-overlays-in-1 b 5 10 () (a 10 30) (b 20 40)) +(deftest-overlays-in-1 c 5 15 (a) (a 10 30) (b 20 40)) +(deftest-overlays-in-1 d 10 15 (a) (a 10 30) (b 20 40)) +(deftest-overlays-in-1 e 10 20 (a) (a 10 30) (b 20 40)) +(deftest-overlays-in-1 f 15 20 (a) (a 10 30) (b 20 40)) +(deftest-overlays-in-1 g 20 30 (a b) (a 10 30) (b 20 40)) +(deftest-overlays-in-1 h 20 40 (a b) (a 10 30) (b 20 40)) +(deftest-overlays-in-1 i 25 30 (a b) (a 10 30) (b 20 40)) +(deftest-overlays-in-1 j 30 30 (b) (a 10 30) (b 20 40)) +(deftest-overlays-in-1 k 30 35 (b) (a 10 30) (b 20 40)) +(deftest-overlays-in-1 l 35 40 (b) (a 10 30) (b 20 40)) +(deftest-overlays-in-1 m 40 45 () (a 10 30) (b 20 40)) +(deftest-overlays-in-1 n 41 45 () (a 10 30) (b 20 40)) +(deftest-overlays-in-1 o 50 60 () (a 10 30) (b 20 40)) + +;; 2 non-empty overlays continuous +(deftest-overlays-in-1 p 1 5 () (a 10 20) (b 20 30)) +(deftest-overlays-in-1 q 5 10 () (a 10 20) (b 20 30)) +(deftest-overlays-in-1 r 15 20 (a) (a 10 20) (b 20 30)) +(deftest-overlays-in-1 s 15 25 (a b) (a 10 20) (b 20 30)) +(deftest-overlays-in-1 t 20 25 (b) (a 10 20) (b 20 30)) +(deftest-overlays-in-1 u 25 30 (b) (a 10 20) (b 20 30)) +(deftest-overlays-in-1 v 29 35 (b) (a 10 20) (b 20 30)) +(deftest-overlays-in-1 w 30 35 () (a 10 20) (b 20 30)) +(deftest-overlays-in-1 x 35 50 () (a 10 20) (b 20 30)) +(deftest-overlays-in-1 y 1 50 (a b) (a 10 20) (b 20 30)) +(deftest-overlays-in-1 z 15 50 (a b) (a 10 20) (b 20 30)) +(deftest-overlays-in-1 aa 1 25 (a b) (a 10 20) (b 20 30)) + +;; 1 empty overlay +(deftest-overlays-in-1 ab 1 10 () (a 10 10)) +(deftest-overlays-in-1 ac 10 10 (a) (a 10 10)) +(deftest-overlays-in-1 ad 9 10 () (a 10 10)) +(deftest-overlays-in-1 ae 9 11 (a) (a 10 10)) +(deftest-overlays-in-1 af 10 11 (a) (a 10 10)) + + +;; behaviour at point-max +(ert-deftest test-overlays-in-2 () + (cl-macrolet ((should-length (n list) + `(should (= ,n (length ,list))))) + (with-temp-buffer + (insert (make-string 100 ?\s)) + (make-overlay (point-max) (point-max)) + (make-overlay 50 50) + (should-length 1 (overlays-in 50 50)) + (should-length 2 (overlays-in 1 (point-max))) + (should-length 1 (overlays-in (point-max) (point-max))) + (narrow-to-region 1 50) + (should-length 0 (overlays-in 1 (point-max))) + (should-length 1 (overlays-in (point-max) (point-max)))))) + + +;; +===================================================================================+ +;; | overlay-recenter +;; +===================================================================================+ + +;; This function is a noop in the overlay tree branch. +(ert-deftest test-overlay-recenter () + (with-temp-buffer + (should-not (overlay-recenter 1)) + (insert (make-string 100 ?\s)) + (dotimes (i 10) + (make-overlay i (1+ i)) + (should-not (overlay-recenter i))))) + + +;; +===================================================================================+ +;; | move-overlay +;; +===================================================================================+ + +;; buffer nil with live overlay +(ert-deftest test-move-overlay-1 () + (test-with-overlay-in-buffer (ov 1 100) + (move-overlay ov 50 60) + (should (= 50 (overlay-start ov))) + (should (= 60 (overlay-end ov))) + (should (eq (current-buffer) (overlay-buffer ov))))) + +;; buffer nil, dead overlay +(ert-deftest test-move-overlay-2 () + (with-temp-buffer + (let ((ov (test-with-overlay-in-buffer (ov 1 100) ov))) + (insert (make-string 100 ?\s)) + (move-overlay ov 50 60) + (should (= 50 (overlay-start ov))) + (should (= 60 (overlay-end ov))) + (should (eq (current-buffer) (overlay-buffer ov)))))) + +;; buffer non-nil, live overlay +(ert-deftest test-move-overlay-3 () + (test-with-overlay-in-buffer (ov 10 100) + (with-temp-buffer + (move-overlay ov 1 1 (current-buffer)) + (should (= 1 (overlay-start ov))) + (should (= 1 (overlay-end ov))) + (should (eq (current-buffer) (overlay-buffer ov)))) + (should-not (overlay-start ov)) + (should-not (overlay-end ov)) + (should-not (overlay-buffer ov)))) + +;; buffer non-nil, dead overlay +(ert-deftest test-move-overlay-4 () + (let ((ov (test-with-overlay-in-buffer (ov 1 1) ov))) + (with-temp-buffer + (move-overlay ov 1 1 (current-buffer)) + (should (= 1 (overlay-start ov))) + (should (= 1 (overlay-end ov))) + (should (eq (current-buffer) (overlay-buffer ov)))) + (should-not (overlay-start ov)) + (should-not (overlay-end ov)) + (should-not (overlay-buffer ov)))) + +;; This used to fail. +(ert-deftest test-move-overlay-5 () + (skip-unless (fboundp 'overlay-tree)) + (with-temp-buffer + (insert (make-string 1 ?.)) + (let ((other (make-overlay 1 1))) + (make-overlay 1 1) + (insert "()") + (move-overlay other (point-max) (1+ (point-max)) (current-buffer)) + (delete-overlay other)) + (should (= (plist-get (car (with-no-warnings (overlay-tree))) :limit) + 1)))) + + +;; +===================================================================================+ +;; | delete-(all-)overlay +;; +===================================================================================+ + +;; delete live overlay +(ert-deftest test-delete-overlay-1 () + (test-with-overlay-in-buffer (ov 10 100) + (should (buffer-live-p (overlay-buffer ov))) + (delete-overlay ov) + (should-not (overlay-start ov)) + (should-not (overlay-end ov)) + (should-not (overlay-buffer ov)))) + +;; delete dead overlay +(ert-deftest test-delete-overlay-2 () + (let ((ov (test-with-overlay-in-buffer (ov 10 100) ov))) + (should-not (overlay-start ov)) + (should-not (overlay-end ov)) + (should-not (overlay-buffer ov)) + (should-not (delete-overlay ov)) + (should-not (overlay-start ov)) + (should-not (overlay-end ov)) + (should-not (overlay-buffer ov)))) + +(ert-deftest test-delete-all-overlay-1 () + (with-temp-buffer + (should-not (delete-all-overlays)) + (should-not (delete-all-overlays (current-buffer))) + (insert (make-string 100 ?\s)) + (dotimes (i 10) (make-overlay i (1+ i))) + (should-not (delete-all-overlays (current-buffer))) + (should-not (delete-all-overlays)))) + + +;; +===================================================================================+ +;; | get-char-property(-and-overlay) +;; +===================================================================================+ + +;; FIXME: TBD + + +;; +===================================================================================+ +;; | Moving by insertions +;; +===================================================================================+ + +(defmacro deftest-moving-insert-1 (id beg-end insert sbeg-send fa ra) + (cl-destructuring-bind (beg end ipos ilen sbeg send fa ra) + (append beg-end insert sbeg-send (list fa ra) nil) + `(ert-deftest ,(make-overlay-test-name 'moving-insert 1 id) () + (test-with-overlay-in-buffer (ov ,beg ,end ,fa ,ra) + (should (= ,beg (overlay-start ov))) + (should (= ,end (overlay-end ov))) + (goto-char ,ipos) + (insert (make-string ,ilen ?x)) + (should (= ,sbeg (overlay-start ov))) + (should (= ,send (overlay-end ov))))))) + +;; non-empty, no fa, no ra +;; -------------------- OV INS RESULT +(deftest-moving-insert-1 A (10 20) (15 3) (10 23) nil nil) +(deftest-moving-insert-1 B (10 20) (20 4) (10 20) nil nil) +(deftest-moving-insert-1 C (10 20) (5 5) (15 25) nil nil) +(deftest-moving-insert-1 D (10 20) (10 3) (10 23) nil nil) +(deftest-moving-insert-1 E (10 20) (20 4) (10 20) nil nil) + +;; non-empty no fa, ra +(deftest-moving-insert-1 F (10 20) (15 3) (10 23) nil t) +(deftest-moving-insert-1 G (10 20) (20 4) (10 24) nil t) +(deftest-moving-insert-1 H (10 20) (5 5) (15 25) nil t) +(deftest-moving-insert-1 I (10 20) (10 3) (10 23) nil t) +(deftest-moving-insert-1 J (10 20) (20 4) (10 24) nil t) + +;; non-empty, fa, no r +(deftest-moving-insert-1 K (10 20) (15 3) (10 23) t nil) +(deftest-moving-insert-1 L (10 20) (20 4) (10 20) t nil) +(deftest-moving-insert-1 M (10 20) (5 5) (15 25) t nil) +(deftest-moving-insert-1 N (10 20) (10 3) (13 23) t nil) +(deftest-moving-insert-1 O (10 20) (20 4) (10 20) t nil) + +;; This used to fail. +(ert-deftest test-moving-insert-2-a () + (with-temp-buffer + (insert (make-string 1 ?.)) + (let ((ov (make-overlay 1 1 nil t nil))) + (insert "()") + (should (= 1 (overlay-end ov)))))) + +;; non-empty, fa, ra +(deftest-moving-insert-1 P (10 20) (15 3) (10 23) t t) +(deftest-moving-insert-1 Q (10 20) (20 4) (10 24) t t) +(deftest-moving-insert-1 R (10 20) (5 5) (15 25) t t) +(deftest-moving-insert-1 S (10 20) (10 3) (13 23) t t) +(deftest-moving-insert-1 T (10 20) (20 4) (10 24) t t) + +;; empty, no fa, no ra +(deftest-moving-insert-1 U (15 15) (20 4) (15 15) nil nil) +(deftest-moving-insert-1 V (15 15) (5 5) (20 20) nil nil) +(deftest-moving-insert-1 W (15 15) (15 3) (15 15) nil nil) + +;; empty no fa, ra +(deftest-moving-insert-1 X (15 15) (20 4) (15 15) nil t) +(deftest-moving-insert-1 Y (15 15) (5 5) (20 20) nil t) +(deftest-moving-insert-1 Z (15 15) (15 3) (15 18) nil t) + +;; empty, fa, no ra +(deftest-moving-insert-1 a (15 15) (20 4) (15 15) t nil) +(deftest-moving-insert-1 b (15 15) (5 5) (20 20) t nil) +(deftest-moving-insert-1 c (15 15) (15 3) (15 15) t nil) + +;; empty, fa, ra +(deftest-moving-insert-1 d (15 15) (20 4) (15 15) t t) +(deftest-moving-insert-1 e (15 15) (5 5) (20 20) t t) +(deftest-moving-insert-1 f (15 15) (15 3) (18 18) t t) + +;; Try to trigger a pathological case where the tree could become +;; unordered due to an insert operation. + +(ert-deftest test-moving-insert-2 () + (with-temp-buffer + (insert (make-string 1000 ?x)) + (let ((root (make-overlay 50 75 nil nil 'rear-advance)) + (left (make-overlay 25 50 nil 'front-advance 'rear-advance)) + (right (make-overlay 75 100 nil nil nil))) + ;; [50] <--- start + ;; / \ + ;; (25) (75) + (delete-region 25 75) + ;; [25] + ;; / \ + ;; (25) (25) + (should (= 25 (overlay-start root))) + (should (= 25 (overlay-end root))) + (should (= 25 (overlay-start left))) + (should (= 25 (overlay-end left))) + (should (= 25 (overlay-start right))) + (should (= 50 (overlay-end right))) + ;; Inserting at start should make left advance while right and + ;; root stay, thus we would have left > right . + (goto-char 25) + (insert (make-string 25 ?x)) + ;; [25] + ;; / \ + ;; (50) (25) + (should (= 25 (overlay-start root))) + (should (= 50 (overlay-end root))) + (should (= 50 (overlay-start left))) + (should (= 50 (overlay-end left))) + (should (= 25 (overlay-start right))) + (should (= 75 (overlay-end right))) + ;; Try to detect the error, by removing left. The should fail + ;; an eassert, since it won't be found by a reular tree + ;; traversal - in theory. + (delete-overlay left) + (should (= 2 (length (overlays-in 1 (point-max)))))))) + + + +;; +===================================================================================+ +;; | Moving by deletions +;; +===================================================================================+ + +(defmacro deftest-moving-delete-1 (id beg-end delete sbeg-send fa ra) + (cl-destructuring-bind (beg end dpos dlen sbeg send fa ra) + (append beg-end delete sbeg-send (list fa ra) nil) + `(ert-deftest ,(make-overlay-test-name 'moving-delete 1 id) () + (test-with-overlay-in-buffer (ov ,beg ,end ,fa ,ra) + (should (= ,beg (overlay-start ov))) + (should (= ,end (overlay-end ov))) + (delete-region ,dpos (+ ,dpos ,dlen)) + (should (= ,sbeg (overlay-start ov))) + (should (= ,send (overlay-end ov))))))) + +;; non-empty, no fa, no ra +;; -------------------- OV DEL RESULT +(deftest-moving-delete-1 A (10 20) (15 3) (10 17) nil nil) +(deftest-moving-delete-1 B (10 20) (20 4) (10 20) nil nil) +(deftest-moving-delete-1 C (10 20) (5 5) (5 15) nil nil) +(deftest-moving-delete-1 D (10 20) (10 3) (10 17) nil nil) +(deftest-moving-delete-1 E (10 20) (20 4) (10 20) nil nil) + +;; non-empty no fa, ra +(deftest-moving-delete-1 F (10 20) (15 3) (10 17) nil t) +(deftest-moving-delete-1 G (10 20) (20 4) (10 20) nil t) +(deftest-moving-delete-1 H (10 20) (5 5) (5 15) nil t) +(deftest-moving-delete-1 I (10 20) (10 3) (10 17) nil t) +(deftest-moving-delete-1 J (10 20) (20 4) (10 20) nil t) + +;; non-empty, fa, no ra +(deftest-moving-delete-1 K (10 20) (15 3) (10 17) t nil) +(deftest-moving-delete-1 L (10 20) (20 4) (10 20) t nil) +(deftest-moving-delete-1 M (10 20) (5 5) (5 15) t nil) +(deftest-moving-delete-1 N (10 20) (10 3) (10 17) t nil) +(deftest-moving-delete-1 O (10 20) (20 4) (10 20) t nil) + +;; non-empty, fa, ra +(deftest-moving-delete-1 P (10 20) (15 3) (10 17) t t) +(deftest-moving-delete-1 Q (10 20) (20 4) (10 20) t t) +(deftest-moving-delete-1 R (10 20) (5 5) (5 15) t t) +(deftest-moving-delete-1 S (10 20) (10 3) (10 17) t t) +(deftest-moving-delete-1 T (10 20) (20 4) (10 20) t t) + +;; empty, no fa, no ra +(deftest-moving-delete-1 U (15 15) (20 4) (15 15) nil nil) +(deftest-moving-delete-1 V (15 15) (5 5) (10 10) nil nil) +(deftest-moving-delete-1 W (15 15) (15 3) (15 15) nil nil) + +;; empty no fa, ra +(deftest-moving-delete-1 X (15 15) (20 4) (15 15) nil t) +(deftest-moving-delete-1 Y (15 15) (5 5) (10 10) nil t) +(deftest-moving-delete-1 Z (15 15) (15 3) (15 15) nil t) + +;; empty, fa, no ra +(deftest-moving-delete-1 a (15 15) (20 4) (15 15) t nil) +(deftest-moving-delete-1 b (15 15) (5 5) (10 10) t nil) +(deftest-moving-delete-1 c (15 15) (15 3) (15 15) t nil) + +;; empty, fa, ra +(deftest-moving-delete-1 d (15 15) (20 4) (15 15) t t) +(deftest-moving-delete-1 e (15 15) (5 5) (10 10) t t) +(deftest-moving-delete-1 f (15 15) (15 3) (15 15) t t) + + +;; +===================================================================================+ +;; | make-indirect-buffer +;; +===================================================================================+ + +;; Check if overlays are cloned/seperate from indirect buffer. +(ert-deftest test-make-indirect-buffer-1 () + (with-temp-buffer + (dotimes (_ 10) (make-overlay 1 1)) + (let (indirect clone) + (unwind-protect + (progn + (setq indirect (make-indirect-buffer + (current-buffer) "indirect")) + (with-current-buffer indirect + (should-not (overlays-in (point-min) (point-max))) + (dotimes (_ 20) (make-overlay 1 1)) + (should (= 20 (length (overlays-in (point-min) (point-max))))) + (delete-all-overlays) + (should-not (overlays-in (point-min) (point-max)))) + (should (= 10 (length (overlays-in (point-min) (point-max))))) + (setq clone (make-indirect-buffer + (current-buffer) "clone" 'clone)) + (with-current-buffer clone + (should (= 10 (length (overlays-in (point-min) (point-max))))) + (dotimes (_ 30) (make-overlay 1 1)) + (should (= 40 (length (overlays-in (point-min) (point-max)))))) + ;; back in temp buffer + (should (= 10 (length (overlays-in (point-min) (point-max))))) + (with-current-buffer clone + (mapc #'delete-overlay + (seq-take (overlays-in (point-min) (point-max)) 10)) + (should (= 30 (length (overlays-in (point-min) (point-max)))))) + (should (= 10 (length (overlays-in (point-min) (point-max))))) + (delete-all-overlays) + (with-current-buffer clone + (should (= 30 (length (overlays-in (point-min) (point-max))))))) + (when (buffer-live-p clone) + (kill-buffer clone)) + (when (buffer-live-p indirect) + (kill-buffer indirect)))))) + + + +;; +===================================================================================+ +;; | buffer-swap-text +;; +===================================================================================+ + +(defmacro test-with-temp-buffers (vars &rest body) + (declare (indent 1) (debug (sexp &rest form))) + (if (null vars) + `(progn ,@body) + `(with-temp-buffer + (let ((,(car vars) (current-buffer))) + (test-with-temp-buffers ,(cdr vars) ,@body))))) + +;; basic +(ert-deftest test-buffer-swap-text-1 () + (test-with-temp-buffers (buffer other) + (with-current-buffer buffer + (let ((ov (make-overlay 1 1))) + (buffer-swap-text other) + (should-not (overlays-in 1 1)) + (with-current-buffer other + (should (overlays-in 1 1)) + (should (eq ov (car (overlays-in 1 1))))))))) + +;; properties +(ert-deftest test-buffer-swap-text-1 () + (test-with-temp-buffers (buffer other) + (with-current-buffer other + (overlay-put (make-overlay 1 1) 'buffer 'other)) + (with-current-buffer buffer + (overlay-put (make-overlay 1 1) 'buffer 'buffer) + (buffer-swap-text other) + (should (= 1 (length (overlays-in 1 1)))) + (should (eq (overlay-get (car (overlays-in 1 1)) 'buffer) 'other))) + (with-current-buffer other + (should (= 1 (length (overlays-in 1 1)))) + (should (eq (overlay-get (car (overlays-in 1 1)) 'buffer) 'buffer))))) + + +;; +===================================================================================+ +;; | priorities +;; +===================================================================================+ + +(ert-deftest test-overlay-priorities-1 () + (with-temp-buffer + (insert " ") + (dotimes (i 10) + (let ((ov (make-overlay 1 2))) + (overlay-put ov 'priority i) + (overlay-put ov 'value i))) + (should (eq 9 (get-char-property 1 'value))))) + +(ert-deftest test-overlay-priorities-2 () + (with-temp-buffer + (insert " ") + (dotimes (j 10) + (let* ((i (- 9 j)) + (ov (make-overlay 1 2))) + (overlay-put ov 'priority i) + (overlay-put ov 'value i))) + (should (eq 9 (get-char-property 1 'value))))) + + +;; +===================================================================================+ +;; | Other +;; +===================================================================================+ + +(defun test-overlay-regions () + (sort (mapcar (lambda (ov) + (cons (overlay-start ov) + (overlay-end ov))) + (overlays-in (point-min) + (point-max))) + (lambda (o1 o2) + (or (< (car o1) (car o2)) + (and (= (car o1) (car o2)) + (< (cdr o1) (cdr o2))))))) + +;; This test used to fail. +(ert-deftest overlay-complex-delete-with-offset () + (with-temp-buffer + (let (todelete) + (insert (make-string 1000 ?\s)) + (make-overlay 1 2 nil t nil) + (make-overlay 2 3 nil t nil) + (make-overlay 3 4 nil t nil) + (make-overlay 4 5 nil t nil) + (setq todelete (make-overlay 280 287 nil t nil)) + (make-overlay 265 275 nil t nil) + (make-overlay 329 386 nil t nil) + (make-overlay 386 390 nil t nil) + (goto-char 50) + (delete-char 50) + (goto-char 1) + (delete-char 2) + (delete-overlay todelete) + (should (equal (test-overlay-regions) + '((1 . 1) (1 . 1) (1 . 2) (2 . 3) (213 . 223) (277 . 334) (334 . 338))))))) + +;; This test used to fail. +(ert-deftest overlay-complex-insert-1 () + (with-temp-buffer + (insert " ") + (make-overlay 8 11 nil nil t) + (make-overlay 2 7 nil nil nil) + (make-overlay 2 4 nil t nil) + (goto-char 1) + (insert " ") + (should (equal (test-overlay-regions) + '((7 . 9) + (7 . 12) + (13 . 16)))))) + +;; This test used to fail. +(ert-deftest overlay-complex-insert-2 () + (with-temp-buffer + (insert (make-string 100 ?\s)) + (make-overlay 77 7 nil nil t) + (make-overlay 21 53 nil t t) + (make-overlay 84 14 nil nil nil) + (make-overlay 38 69 nil t nil) + (make-overlay 93 15 nil nil t) + (make-overlay 73 48 nil t t) + (make-overlay 96 51 nil t t) + (make-overlay 6 43 nil t t) + (make-overlay 15 100 nil t t) + (make-overlay 22 17 nil nil nil) + (make-overlay 72 45 nil t nil) + (make-overlay 2 74 nil nil t) + (make-overlay 15 29 nil t t) + (make-overlay 17 34 nil t t) + (make-overlay 101 66 nil t nil) + (make-overlay 94 24 nil nil nil) + (goto-char 78) + (insert " ") + (narrow-to-region 47 19) + (goto-char 46) + (widen) + (narrow-to-region 13 3) + (goto-char 9) + (delete-char 0) + (goto-char 11) + (insert " ") + (goto-char 3) + (insert " ") + (goto-char 8) + (insert " ") + (goto-char 26) + (insert " ") + (goto-char 14) + (widen) + (narrow-to-region 71 35) + (should + (equal (test-overlay-regions) + '((2 . 104) + (23 . 73) + (24 . 107) + (44 . 125) + (45 . 59) + (45 . 134) + (45 . 141) + (47 . 52) + (47 . 64) + (51 . 83) + (54 . 135) + (68 . 99)))))) + + +;; +===================================================================================+ +;; | Autogenerated insert/delete/narrow tests +;; +===================================================================================+ + + +;; (defun test-overlay-generate-test (name) +;; (interactive) +;; (with-temp-buffer +;; (let ((forms nil) +;; (buffer-size 64) +;; (noverlays 16) +;; (nforms 32) +;; (dist '(0.5 0.4 0.1))) +;; (cl-labels ((brand () +;; (+ (point-min) +;; (random (1+ (- (point-max) (point-min))))))) +;; (cl-macrolet ((push-eval (form) +;; `(cl-destructuring-bind (&rest args) +;; (list ,@(cdr form)) +;; (push (cons ',(car form) args) forms) +;; (apply #',(car form) args)))) +;; (push-eval (insert (make-string buffer-size ?.))) +;; (dotimes (_ noverlays) +;; (push-eval (make-overlay (brand) (brand) +;; nil +;; (= 0 (random 2)) +;; (= 0 (random 2))))) +;; (dotimes (_ nforms) +;; (push-eval (goto-char (brand))) +;; (pcase (/ (random 100) 100.0) +;; ((and x (guard (< x (nth 0 dist)))) +;; (push-eval (insert (make-string (random 16) ?.)))) +;; ((and x (guard (< x (+ (nth 0 dist) (nth 1 dist))))) +;; (push-eval (delete-char (random (1+ (- (point-max) (point))))))) +;; (_ +;; (push-eval (widen)) +;; (push-eval (narrow-to-region (brand) (brand)))))) +;; `(ert-deftest ,name () +;; (with-temp-buffer +;; ,@(nreverse forms) +;; (should (equal (test-overlay-regions) +;; ',(test-overlay-regions)))))))))) + +;; (defun test-overlay-generate-tests (n) +;; (let ((namefmt "overlay-autogenerated-test-%d") +;; (standard-output (current-buffer)) +;; (print-length nil) +;; (print-level nil) +;; (print-quoted t)) +;; (dotimes (i n) +;; (pp (test-overlay-generate-test (intern (format namefmt i)))) +;; (terpri)))) + +;; (progn (random "4711") (test-overlay-generate-tests 64)) + +(ert-deftest overlay-autogenerated-test-0 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 63 7 nil t t) + (make-overlay 47 9 nil nil nil) + (make-overlay 50 43 nil nil nil) + (make-overlay 20 53 nil nil t) + (make-overlay 62 4 nil nil t) + (make-overlay 40 27 nil t t) + (make-overlay 58 44 nil t t) + (make-overlay 46 38 nil nil nil) + (make-overlay 51 28 nil t nil) + (make-overlay 12 53 nil t t) + (make-overlay 52 60 nil nil nil) + (make-overlay 13 47 nil nil nil) + (make-overlay 16 31 nil nil nil) + (make-overlay 9 48 nil t t) + (make-overlay 43 29 nil nil t) + (make-overlay 48 13 nil t nil) + (goto-char 44) + (delete-char 15) + (goto-char 19) + (widen) + (narrow-to-region 20 8) + (goto-char 9) + (delete-char 3) + (goto-char 16) + (insert "..............") + (goto-char 12) + (delete-char 15) + (goto-char 12) + (delete-char 4) + (goto-char 12) + (delete-char 0) + (goto-char 12) + (insert "......") + (goto-char 13) + (delete-char 5) + (goto-char 8) + (insert "...") + (goto-char 10) + (insert ".............") + (goto-char 14) + (insert ".......") + (goto-char 25) + (delete-char 4) + (goto-char 26) + (insert "...............") + (goto-char 27) + (insert "...") + (goto-char 29) + (delete-char 7) + (goto-char 24) + (insert "...") + (goto-char 30) + (insert "..........") + (goto-char 29) + (widen) + (narrow-to-region 34 41) + (goto-char 40) + (delete-char 0) + (goto-char 35) + (delete-char 4) + (goto-char 36) + (widen) + (narrow-to-region 80 66) + (goto-char 74) + (delete-char 5) + (goto-char 69) + (delete-char 5) + (goto-char 70) + (widen) + (narrow-to-region 50 71) + (goto-char 66) + (insert "...............") + (goto-char 54) + (insert "...............") + (goto-char 84) + (insert "....") + (goto-char 72) + (insert "...........") + (goto-char 84) + (insert "..........") + (goto-char 102) + (insert "") + (goto-char 80) + (delete-char 25) + (should + (equal + (test-overlay-regions) + '((4 . 99) + (7 . 100) + (48 . 99) + (48 . 99) + (48 . 99) + (49 . 99) + (49 . 99) + (51 . 80) + (51 . 99) + (80 . 99) + (80 . 99) + (80 . 99) + (99 . 99) + (99 . 99) + (99 . 99) + (99 . 99)))))) + +(ert-deftest overlay-autogenerated-test-1 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 17 27 nil nil nil) + (make-overlay 13 28 nil nil t) + (make-overlay 8 56 nil nil nil) + (make-overlay 34 64 nil nil nil) + (make-overlay 51 4 nil t t) + (make-overlay 1 19 nil nil nil) + (make-overlay 53 59 nil nil t) + (make-overlay 25 13 nil nil nil) + (make-overlay 19 28 nil t nil) + (make-overlay 33 23 nil t nil) + (make-overlay 10 46 nil t t) + (make-overlay 18 39 nil nil nil) + (make-overlay 1 49 nil t nil) + (make-overlay 57 21 nil t t) + (make-overlay 10 58 nil t t) + (make-overlay 39 49 nil nil t) + (goto-char 37) + (delete-char 9) + (goto-char 3) + (insert "......") + (goto-char 38) + (delete-char 14) + (goto-char 18) + (insert "..........") + (goto-char 53) + (insert "....") + (goto-char 49) + (delete-char 10) + (goto-char 11) + (delete-char 12) + (goto-char 17) + (delete-char 22) + (goto-char 8) + (insert ".") + (goto-char 16) + (insert "........") + (goto-char 16) + (delete-char 5) + (goto-char 11) + (delete-char 0) + (goto-char 22) + (insert ".......") + (goto-char 18) + (delete-char 11) + (goto-char 16) + (delete-char 0) + (goto-char 9) + (insert "...........") + (goto-char 7) + (insert "...............") + (goto-char 2) + (insert ".......") + (goto-char 21) + (delete-char 11) + (goto-char 13) + (insert "..............") + (goto-char 17) + (delete-char 3) + (goto-char 21) + (insert "......") + (goto-char 15) + (delete-char 32) + (goto-char 10) + (insert "........") + (goto-char 25) + (widen) + (narrow-to-region 15 20) + (goto-char 17) + (insert ".............") + (goto-char 22) + (insert "............") + (goto-char 21) + (delete-char 8) + (goto-char 36) + (delete-char 1) + (goto-char 32) + (delete-char 2) + (goto-char 21) + (insert ".....") + (goto-char 31) + (insert "......") + (should + (equal + (test-overlay-regions) + '((1 . 58) + (1 . 58)))))) + +(ert-deftest overlay-autogenerated-test-2 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 15 59 nil t t) + (make-overlay 56 16 nil nil nil) + (make-overlay 65 51 nil t nil) + (make-overlay 14 24 nil t nil) + (make-overlay 28 9 nil t nil) + (make-overlay 58 50 nil t t) + (make-overlay 13 32 nil t t) + (make-overlay 12 21 nil t nil) + (make-overlay 60 23 nil t nil) + (make-overlay 39 38 nil nil t) + (make-overlay 15 64 nil t nil) + (make-overlay 17 21 nil nil t) + (make-overlay 46 23 nil t t) + (make-overlay 19 40 nil t nil) + (make-overlay 13 48 nil nil t) + (make-overlay 35 11 nil t nil) + (goto-char 41) + (delete-char 19) + (goto-char 45) + (insert "......") + (goto-char 3) + (delete-char 32) + (goto-char 19) + (insert "") + (goto-char 16) + (insert "...............") + (goto-char 2) + (insert "") + (goto-char 30) + (delete-char 0) + (goto-char 18) + (delete-char 17) + (goto-char 2) + (insert "...............") + (goto-char 12) + (insert "...") + (goto-char 2) + (insert ".............") + (goto-char 16) + (insert ".......") + (goto-char 15) + (insert ".......") + (goto-char 43) + (insert "......") + (goto-char 22) + (insert ".........") + (goto-char 25) + (delete-char 1) + (goto-char 38) + (insert "...............") + (goto-char 76) + (delete-char 3) + (goto-char 12) + (delete-char 5) + (goto-char 70) + (delete-char 9) + (goto-char 36) + (delete-char 4) + (goto-char 18) + (insert "...............") + (goto-char 52) + (delete-char 14) + (goto-char 23) + (insert "..........") + (goto-char 64) + (insert "...........") + (goto-char 68) + (delete-char 21) + (goto-char 71) + (insert "........") + (goto-char 28) + (delete-char 43) + (goto-char 25) + (insert "....") + (goto-char 2) + (insert "...............") + (goto-char 40) + (insert "....") + (goto-char 56) + (delete-char 2) + (should + (equal + (test-overlay-regions) + '((51 . 51) + (51 . 51) + (51 . 51) + (51 . 51) + (51 . 51) + (51 . 51) + (51 . 51) + (51 . 51) + (51 . 51) + (51 . 51) + (51 . 51) + (51 . 51) + (51 . 51) + (51 . 51) + (51 . 51) + (51 . 58)))))) + +(ert-deftest overlay-autogenerated-test-3 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 53 38 nil t nil) + (make-overlay 17 40 nil t t) + (make-overlay 64 26 nil t t) + (make-overlay 48 24 nil t nil) + (make-overlay 21 18 nil nil nil) + (make-overlay 2 20 nil nil t) + (make-overlay 43 26 nil t t) + (make-overlay 56 28 nil t nil) + (make-overlay 19 51 nil nil nil) + (make-overlay 39 61 nil t nil) + (make-overlay 59 12 nil t nil) + (make-overlay 65 7 nil t nil) + (make-overlay 41 7 nil t nil) + (make-overlay 62 50 nil t nil) + (make-overlay 7 10 nil t t) + (make-overlay 45 28 nil t nil) + (goto-char 13) + (insert "...") + (goto-char 37) + (widen) + (narrow-to-region 2 10) + (goto-char 8) + (delete-char 1) + (goto-char 3) + (delete-char 6) + (goto-char 2) + (insert "...........") + (goto-char 5) + (widen) + (narrow-to-region 55 70) + (goto-char 55) + (insert "......") + (goto-char 64) + (delete-char 12) + (goto-char 61) + (insert ".....") + (goto-char 64) + (insert "..............") + (goto-char 72) + (delete-char 6) + (goto-char 63) + (delete-char 12) + (goto-char 63) + (delete-char 2) + (goto-char 57) + (insert "..............") + (goto-char 68) + (insert "........") + (goto-char 77) + (delete-char 6) + (goto-char 77) + (insert ".............") + (goto-char 67) + (delete-char 0) + (goto-char 84) + (insert "........") + (goto-char 74) + (delete-char 12) + (goto-char 78) + (insert "...") + (goto-char 80) + (insert "............") + (goto-char 69) + (insert "......") + (goto-char 89) + (insert ".") + (goto-char 56) + (insert "....") + (goto-char 100) + (insert ".............") + (goto-char 114) + (delete-char 0) + (goto-char 61) + (widen) + (narrow-to-region 94 50) + (goto-char 55) + (insert "............") + (goto-char 53) + (insert ".............") + (goto-char 116) + (delete-char 3) + (goto-char 81) + (insert "...............") + (should + (equal + (test-overlay-regions) + '((14 . 166) + (16 . 164) + (26 . 164) + (31 . 68) + (33 . 165) + (35 . 52) + (35 . 164) + (45 . 164) + (46 . 164)))))) + +(ert-deftest overlay-autogenerated-test-4 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 25 15 nil nil t) + (make-overlay 8 13 nil nil nil) + (make-overlay 45 49 nil t t) + (make-overlay 22 13 nil t t) + (make-overlay 34 17 nil nil t) + (make-overlay 42 15 nil nil t) + (make-overlay 43 28 nil t t) + (make-overlay 3 28 nil t nil) + (make-overlay 32 61 nil nil t) + (make-overlay 30 64 nil t t) + (make-overlay 21 39 nil nil t) + (make-overlay 32 62 nil t nil) + (make-overlay 25 29 nil t nil) + (make-overlay 34 43 nil t nil) + (make-overlay 9 11 nil t nil) + (make-overlay 21 65 nil nil t) + (goto-char 21) + (delete-char 4) + (goto-char 25) + (insert "..") + (goto-char 53) + (insert "..") + (goto-char 2) + (insert "...............") + (goto-char 42) + (delete-char 36) + (goto-char 23) + (delete-char 12) + (goto-char 22) + (widen) + (narrow-to-region 30 32) + (goto-char 30) + (delete-char 0) + (goto-char 31) + (delete-char 1) + (goto-char 31) + (widen) + (narrow-to-region 28 27) + (goto-char 27) + (delete-char 1) + (goto-char 27) + (delete-char 0) + (goto-char 27) + (delete-char 0) + (goto-char 27) + (insert ".") + (goto-char 28) + (insert "......") + (goto-char 34) + (delete-char 0) + (goto-char 27) + (delete-char 5) + (goto-char 27) + (delete-char 1) + (goto-char 27) + (insert ".............") + (goto-char 30) + (insert "..............") + (goto-char 37) + (delete-char 15) + (goto-char 32) + (delete-char 2) + (goto-char 36) + (delete-char 1) + (goto-char 34) + (delete-char 0) + (goto-char 34) + (delete-char 1) + (goto-char 32) + (widen) + (narrow-to-region 24 19) + (goto-char 21) + (delete-char 1) + (goto-char 21) + (widen) + (narrow-to-region 11 38) + (goto-char 27) + (widen) + (narrow-to-region 20 22) + (goto-char 20) + (delete-char 1) + (goto-char 20) + (widen) + (narrow-to-region 36 4) + (goto-char 26) + (delete-char 9) + (should + (equal + (test-overlay-regions) + '((18 . 25) + (21 . 21) + (21 . 21) + (21 . 22) + (21 . 22) + (21 . 27) + (21 . 27) + (22 . 25) + (22 . 27) + (22 . 28) + (26 . 27)))))) + +(ert-deftest overlay-autogenerated-test-5 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 64 1 nil nil nil) + (make-overlay 38 43 nil nil nil) + (make-overlay 42 19 nil t nil) + (make-overlay 22 12 nil nil nil) + (make-overlay 12 30 nil t t) + (make-overlay 38 46 nil nil nil) + (make-overlay 18 23 nil nil nil) + (make-overlay 58 65 nil nil t) + (make-overlay 52 41 nil nil nil) + (make-overlay 12 26 nil nil nil) + (make-overlay 39 4 nil nil nil) + (make-overlay 20 1 nil nil t) + (make-overlay 36 60 nil nil nil) + (make-overlay 24 18 nil t nil) + (make-overlay 9 50 nil nil nil) + (make-overlay 19 17 nil t nil) + (goto-char 40) + (insert "") + (goto-char 64) + (insert ".............") + (goto-char 32) + (delete-char 40) + (goto-char 25) + (insert "...") + (goto-char 31) + (delete-char 1) + (goto-char 8) + (delete-char 14) + (goto-char 20) + (delete-char 5) + (goto-char 20) + (insert "...........") + (goto-char 20) + (insert ".........") + (goto-char 17) + (widen) + (narrow-to-region 11 21) + (goto-char 14) + (widen) + (narrow-to-region 9 24) + (goto-char 24) + (insert ".............") + (goto-char 30) + (widen) + (narrow-to-region 47 45) + (goto-char 47) + (insert ".") + (goto-char 46) + (widen) + (narrow-to-region 30 42) + (goto-char 32) + (delete-char 0) + (goto-char 34) + (insert ".......") + (goto-char 42) + (delete-char 4) + (goto-char 39) + (delete-char 6) + (goto-char 31) + (delete-char 6) + (goto-char 31) + (insert "............") + (goto-char 30) + (insert "......") + (goto-char 50) + (delete-char 0) + (goto-char 30) + (insert "....") + (goto-char 53) + (insert "............") + (goto-char 41) + (delete-char 12) + (goto-char 52) + (insert ".......") + (goto-char 56) + (insert "...........") + (goto-char 68) + (insert ".......") + (goto-char 52) + (insert "......") + (goto-char 71) + (delete-char 10) + (goto-char 47) + (insert "") + (should + (equal + (test-overlay-regions) + '((20 . 89)))))) + +(ert-deftest overlay-autogenerated-test-6 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 28 59 nil nil nil) + (make-overlay 36 21 nil t t) + (make-overlay 60 19 nil t nil) + (make-overlay 26 30 nil t nil) + (make-overlay 47 27 nil nil t) + (make-overlay 8 25 nil t t) + (make-overlay 57 43 nil t t) + (make-overlay 28 61 nil nil t) + (make-overlay 42 31 nil nil t) + (make-overlay 15 44 nil t nil) + (make-overlay 56 38 nil nil nil) + (make-overlay 39 44 nil nil t) + (make-overlay 50 6 nil t nil) + (make-overlay 6 19 nil t nil) + (make-overlay 50 44 nil t t) + (make-overlay 34 60 nil nil t) + (goto-char 27) + (insert "...............") + (goto-char 23) + (insert "..............") + (goto-char 50) + (widen) + (narrow-to-region 53 67) + (goto-char 60) + (delete-char 0) + (goto-char 54) + (insert "......") + (goto-char 64) + (delete-char 1) + (goto-char 66) + (delete-char 3) + (goto-char 58) + (insert ".............") + (goto-char 58) + (insert ".........") + (goto-char 76) + (insert "...........") + (goto-char 57) + (insert "....") + (goto-char 106) + (widen) + (narrow-to-region 5 45) + (goto-char 31) + (delete-char 8) + (goto-char 36) + (insert "...") + (goto-char 6) + (insert "........") + (goto-char 33) + (insert ".............") + (goto-char 38) + (delete-char 3) + (goto-char 28) + (delete-char 6) + (goto-char 42) + (widen) + (narrow-to-region 17 25) + (goto-char 19) + (insert "..............") + (goto-char 37) + (delete-char 1) + (goto-char 22) + (delete-char 9) + (goto-char 28) + (insert "..............") + (goto-char 37) + (delete-char 3) + (goto-char 18) + (insert "...............") + (goto-char 30) + (widen) + (narrow-to-region 68 25) + (goto-char 38) + (delete-char 22) + (goto-char 43) + (widen) + (narrow-to-region 47 96) + (goto-char 86) + (insert ".") + (goto-char 63) + (insert "......") + (goto-char 78) + (widen) + (narrow-to-region 61 27) + (goto-char 43) + (delete-char 8) + (should + (equal + (test-overlay-regions) + '((14 . 38) + (14 . 132) + (16 . 43) + (38 . 118) + (38 . 126) + (38 . 142) + (44 . 115) + (45 . 129)))))) + +(ert-deftest overlay-autogenerated-test-7 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 13 50 nil t nil) + (make-overlay 28 44 nil nil t) + (make-overlay 56 27 nil t nil) + (make-overlay 8 34 nil nil nil) + (make-overlay 22 8 nil nil t) + (make-overlay 8 28 nil t nil) + (make-overlay 65 31 nil nil t) + (make-overlay 44 8 nil nil nil) + (make-overlay 52 64 nil nil t) + (make-overlay 52 27 nil t t) + (make-overlay 47 32 nil nil nil) + (make-overlay 18 62 nil nil nil) + (make-overlay 18 24 nil t t) + (make-overlay 33 46 nil nil t) + (make-overlay 20 8 nil t nil) + (make-overlay 51 51 nil t nil) + (goto-char 2) + (delete-char 46) + (goto-char 12) + (delete-char 5) + (goto-char 2) + (delete-char 12) + (goto-char 2) + (insert "..") + (goto-char 2) + (widen) + (narrow-to-region 2 4) + (goto-char 4) + (insert "......") + (goto-char 4) + (widen) + (narrow-to-region 4 6) + (goto-char 5) + (insert "") + (goto-char 6) + (insert "...............") + (goto-char 9) + (insert "...") + (goto-char 7) + (delete-char 13) + (goto-char 8) + (delete-char 1) + (goto-char 9) + (insert "...............") + (goto-char 24) + (delete-char 1) + (goto-char 15) + (insert "...............") + (goto-char 16) + (insert "............") + (goto-char 17) + (delete-char 8) + (goto-char 36) + (widen) + (narrow-to-region 47 38) + (goto-char 43) + (delete-char 0) + (goto-char 46) + (delete-char 0) + (goto-char 40) + (delete-char 4) + (goto-char 39) + (insert ".......") + (goto-char 50) + (delete-char 0) + (goto-char 47) + (insert "...........") + (goto-char 45) + (insert ".....") + (goto-char 38) + (delete-char 3) + (goto-char 59) + (delete-char 1) + (goto-char 42) + (insert "...............") + (goto-char 65) + (insert "...........") + (goto-char 73) + (delete-char 13) + (goto-char 72) + (insert "....") + (goto-char 47) + (insert "..") + (should + (equal + (test-overlay-regions) + '((2 . 81) + (2 . 81) + (2 . 81) + (2 . 81) + (2 . 81) + (81 . 81) + (81 . 81)))))) + +(ert-deftest overlay-autogenerated-test-8 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 20 6 nil t nil) + (make-overlay 48 13 nil t nil) + (make-overlay 58 65 nil nil t) + (make-overlay 63 65 nil nil nil) + (make-overlay 42 40 nil t t) + (make-overlay 40 6 nil nil t) + (make-overlay 37 46 nil t nil) + (make-overlay 4 14 nil nil nil) + (make-overlay 58 44 nil t t) + (make-overlay 14 16 nil nil t) + (make-overlay 31 61 nil t nil) + (make-overlay 34 3 nil nil nil) + (make-overlay 11 16 nil t nil) + (make-overlay 19 42 nil nil t) + (make-overlay 30 9 nil nil t) + (make-overlay 63 52 nil t t) + (goto-char 57) + (delete-char 2) + (goto-char 8) + (insert "........") + (goto-char 30) + (insert "...........") + (goto-char 35) + (insert "...........") + (goto-char 66) + (insert "...............") + (goto-char 53) + (delete-char 15) + (goto-char 75) + (delete-char 10) + (goto-char 62) + (delete-char 21) + (goto-char 52) + (delete-char 10) + (goto-char 10) + (insert "............") + (goto-char 42) + (insert "...........") + (goto-char 68) + (insert ".............") + (goto-char 12) + (insert "........") + (goto-char 1) + (insert "...............") + (goto-char 89) + (insert "") + (goto-char 94) + (insert ".............") + (goto-char 57) + (insert "...........") + (goto-char 130) + (insert "...") + (goto-char 69) + (insert "..") + (goto-char 101) + (insert "......") + (goto-char 128) + (delete-char 13) + (goto-char 19) + (delete-char 100) + (goto-char 22) + (insert "..") + (goto-char 13) + (widen) + (narrow-to-region 30 16) + (goto-char 19) + (insert "..........") + (goto-char 22) + (delete-char 3) + (goto-char 19) + (insert ".........") + (goto-char 17) + (insert "..") + (goto-char 16) + (insert "............") + (goto-char 47) + (insert ".") + (goto-char 50) + (insert "..........") + (goto-char 70) + (delete-char 1) + (should + (equal + (test-overlay-regions) + '((32 . 75) + (33 . 33) + (33 . 33) + (33 . 33) + (33 . 60) + (33 . 75) + (33 . 75) + (33 . 75) + (60 . 75)))))) + +(ert-deftest overlay-autogenerated-test-9 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 58 13 nil nil nil) + (make-overlay 29 4 nil nil t) + (make-overlay 3 53 nil nil nil) + (make-overlay 31 9 nil t t) + (make-overlay 48 30 nil nil nil) + (make-overlay 43 50 nil nil nil) + (make-overlay 7 27 nil nil t) + (make-overlay 30 59 nil nil nil) + (make-overlay 42 25 nil nil t) + (make-overlay 15 13 nil t t) + (make-overlay 39 11 nil t t) + (make-overlay 21 62 nil t t) + (make-overlay 35 2 nil t nil) + (make-overlay 60 53 nil nil t) + (make-overlay 64 8 nil nil t) + (make-overlay 58 59 nil t t) + (goto-char 28) + (insert ".............") + (goto-char 28) + (insert "...............") + (goto-char 71) + (insert ".......") + (goto-char 65) + (insert "......") + (goto-char 3) + (delete-char 12) + (goto-char 79) + (delete-char 11) + (goto-char 65) + (widen) + (narrow-to-region 12 53) + (goto-char 38) + (insert ".......") + (goto-char 20) + (insert ".........") + (goto-char 27) + (insert "...........") + (goto-char 75) + (insert "........") + (goto-char 85) + (insert "............") + (goto-char 52) + (insert "..........") + (goto-char 16) + (delete-char 8) + (goto-char 15) + (insert "...............") + (goto-char 112) + (insert "") + (goto-char 61) + (insert "..") + (goto-char 29) + (delete-char 34) + (goto-char 52) + (delete-char 32) + (goto-char 43) + (insert "........") + (goto-char 45) + (insert "..") + (goto-char 35) + (insert "...........") + (goto-char 29) + (insert ".......") + (goto-char 75) + (widen) + (narrow-to-region 69 55) + (goto-char 67) + (delete-char 2) + (goto-char 66) + (delete-char 0) + (goto-char 62) + (delete-char 1) + (goto-char 61) + (delete-char 3) + (goto-char 63) + (insert ".") + (goto-char 56) + (insert ".....") + (goto-char 67) + (insert ".............") + (goto-char 76) + (delete-char 3) + (should + (equal + (test-overlay-regions) + '((2 . 90) + (3 . 90) + (3 . 90) + (3 . 99) + (3 . 117) + (3 . 117) + (3 . 120) + (9 . 118) + (13 . 102)))))) + +(ert-deftest overlay-autogenerated-test-10 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 16 60 nil nil nil) + (make-overlay 36 53 nil nil nil) + (make-overlay 44 39 nil t t) + (make-overlay 61 47 nil t t) + (make-overlay 58 39 nil nil t) + (make-overlay 23 54 nil nil t) + (make-overlay 65 59 nil t t) + (make-overlay 13 57 nil nil t) + (make-overlay 22 64 nil nil t) + (make-overlay 16 19 nil nil nil) + (make-overlay 16 1 nil nil t) + (make-overlay 28 21 nil t t) + (make-overlay 10 62 nil nil nil) + (make-overlay 12 18 nil nil t) + (make-overlay 15 5 nil nil t) + (make-overlay 36 31 nil nil t) + (goto-char 42) + (insert "...") + (goto-char 25) + (delete-char 28) + (goto-char 30) + (delete-char 10) + (goto-char 8) + (delete-char 9) + (goto-char 5) + (insert "........") + (goto-char 6) + (delete-char 2) + (goto-char 4) + (insert "") + (goto-char 21) + (insert ".............") + (goto-char 6) + (delete-char 33) + (goto-char 1) + (delete-char 1) + (goto-char 6) + (insert "..........") + (goto-char 8) + (insert "...........") + (goto-char 21) + (insert "........") + (goto-char 16) + (delete-char 18) + (goto-char 5) + (insert "...") + (goto-char 5) + (delete-char 8) + (goto-char 11) + (insert ".") + (goto-char 1) + (insert ".......") + (goto-char 9) + (delete-char 9) + (goto-char 5) + (insert "") + (goto-char 8) + (delete-char 0) + (goto-char 11) + (insert "..............") + (goto-char 12) + (insert "") + (goto-char 11) + (delete-char 8) + (goto-char 7) + (delete-char 3) + (goto-char 5) + (delete-char 3) + (goto-char 1) + (delete-char 8) + (goto-char 1) + (insert "....") + (goto-char 1) + (insert "..") + (goto-char 7) + (insert "...") + (goto-char 8) + (widen) + (narrow-to-region 9 11) + (goto-char 11) + (delete-char 0) + (should + (equal + (test-overlay-regions) + '((1 . 10) + (1 . 10) + (1 . 10) + (1 . 10) + (1 . 10) + (1 . 12) + (1 . 12) + (1 . 12) + (10 . 10) + (10 . 10) + (10 . 12)))))) + +(ert-deftest overlay-autogenerated-test-11 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 33 18 nil nil nil) + (make-overlay 56 38 nil t nil) + (make-overlay 2 45 nil nil t) + (make-overlay 19 55 nil nil t) + (make-overlay 28 42 nil t t) + (make-overlay 50 29 nil t nil) + (make-overlay 40 63 nil nil nil) + (make-overlay 13 2 nil nil t) + (make-overlay 26 7 nil t t) + (make-overlay 22 25 nil nil nil) + (make-overlay 14 14 nil t nil) + (make-overlay 15 39 nil t t) + (make-overlay 51 22 nil t t) + (make-overlay 58 5 nil t nil) + (make-overlay 16 10 nil nil nil) + (make-overlay 32 33 nil t nil) + (goto-char 40) + (delete-char 20) + (goto-char 45) + (delete-char 0) + (goto-char 6) + (insert "..") + (goto-char 45) + (insert "...") + (goto-char 26) + (insert "...............") + (goto-char 27) + (insert "...........") + (goto-char 38) + (insert "......") + (goto-char 62) + (insert "...............") + (goto-char 18) + (insert "...........") + (goto-char 99) + (widen) + (narrow-to-region 37 17) + (goto-char 29) + (delete-char 2) + (goto-char 28) + (delete-char 2) + (goto-char 17) + (insert ".....") + (goto-char 21) + (widen) + (narrow-to-region 34 96) + (goto-char 44) + (delete-char 22) + (goto-char 39) + (insert "..") + (goto-char 53) + (insert "...............") + (goto-char 58) + (insert ".............") + (goto-char 93) + (insert ".........") + (goto-char 78) + (widen) + (narrow-to-region 27 104) + (goto-char 93) + (delete-char 11) + (goto-char 59) + (insert "....") + (goto-char 59) + (insert "..............") + (goto-char 74) + (delete-char 5) + (goto-char 70) + (insert ".") + (goto-char 37) + (insert "...........") + (goto-char 34) + (delete-char 46) + (goto-char 49) + (insert "......") + (goto-char 55) + (insert "...") + (goto-char 42) + (insert "...") + (goto-char 70) + (delete-char 8) + (goto-char 48) + (delete-char 28) + (should + (equal + (test-overlay-regions) + '((2 . 62) + (5 . 62) + (9 . 34) + (22 . 61) + (33 . 55) + (33 . 62) + (34 . 34) + (34 . 62)))))) + +(ert-deftest overlay-autogenerated-test-12 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 18 50 nil nil nil) + (make-overlay 63 3 nil nil t) + (make-overlay 44 20 nil t t) + (make-overlay 58 38 nil nil t) + (make-overlay 3 17 nil t nil) + (make-overlay 31 62 nil t nil) + (make-overlay 12 17 nil t nil) + (make-overlay 17 52 nil nil nil) + (make-overlay 9 35 nil nil nil) + (make-overlay 17 38 nil nil nil) + (make-overlay 53 54 nil nil t) + (make-overlay 65 34 nil t nil) + (make-overlay 12 33 nil t nil) + (make-overlay 54 58 nil nil nil) + (make-overlay 42 26 nil t nil) + (make-overlay 2 4 nil t nil) + (goto-char 4) + (delete-char 26) + (goto-char 39) + (insert ".") + (goto-char 2) + (delete-char 14) + (goto-char 16) + (widen) + (narrow-to-region 19 1) + (goto-char 7) + (delete-char 9) + (goto-char 6) + (insert ".........") + (goto-char 6) + (insert "..........") + (goto-char 16) + (insert ".............") + (goto-char 36) + (delete-char 1) + (goto-char 4) + (insert "..........") + (goto-char 49) + (delete-char 2) + (goto-char 16) + (insert "............") + (goto-char 52) + (widen) + (narrow-to-region 36 38) + (goto-char 37) + (delete-char 1) + (goto-char 37) + (insert ".............") + (goto-char 46) + (insert ".") + (goto-char 40) + (delete-char 5) + (goto-char 45) + (delete-char 0) + (goto-char 46) + (delete-char 0) + (goto-char 40) + (insert "..........") + (goto-char 39) + (delete-char 4) + (goto-char 39) + (delete-char 3) + (goto-char 40) + (widen) + (narrow-to-region 8 9) + (goto-char 8) + (delete-char 1) + (goto-char 8) + (delete-char 0) + (goto-char 8) + (widen) + (narrow-to-region 45 15) + (goto-char 40) + (insert "...............") + (goto-char 29) + (delete-char 7) + (goto-char 30) + (delete-char 6) + (goto-char 21) + (delete-char 9) + (goto-char 22) + (insert "...............") + (goto-char 51) + (insert "..............") + (should + (equal + (test-overlay-regions) + '((2 . 92) + (2 . 92) + (2 . 93) + (2 . 96) + (2 . 97) + (2 . 99)))))) + +(ert-deftest overlay-autogenerated-test-13 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 18 30 nil t t) + (make-overlay 54 37 nil nil t) + (make-overlay 16 61 nil nil t) + (make-overlay 58 7 nil nil t) + (make-overlay 27 39 nil nil t) + (make-overlay 39 31 nil nil t) + (make-overlay 11 47 nil nil nil) + (make-overlay 47 40 nil t t) + (make-overlay 27 18 nil nil nil) + (make-overlay 33 26 nil nil t) + (make-overlay 55 4 nil t t) + (make-overlay 62 50 nil t t) + (make-overlay 47 65 nil t t) + (make-overlay 17 23 nil nil t) + (make-overlay 30 31 nil t nil) + (make-overlay 10 37 nil t nil) + (goto-char 8) + (delete-char 6) + (goto-char 56) + (delete-char 0) + (goto-char 28) + (insert ".........") + (goto-char 19) + (insert "..............") + (goto-char 4) + (delete-char 28) + (goto-char 49) + (delete-char 4) + (goto-char 2) + (insert "............") + (goto-char 10) + (delete-char 37) + (goto-char 19) + (delete-char 2) + (goto-char 20) + (delete-char 0) + (goto-char 16) + (insert "..") + (goto-char 8) + (widen) + (narrow-to-region 12 3) + (goto-char 10) + (delete-char 2) + (goto-char 9) + (insert "..") + (goto-char 12) + (insert "...............") + (goto-char 25) + (insert ".....") + (goto-char 10) + (widen) + (narrow-to-region 42 18) + (goto-char 20) + (insert ".......") + (goto-char 18) + (insert ".........") + (goto-char 55) + (delete-char 3) + (goto-char 48) + (insert ".......") + (goto-char 52) + (delete-char 6) + (goto-char 45) + (delete-char 11) + (goto-char 27) + (delete-char 13) + (goto-char 22) + (insert "...........") + (goto-char 19) + (delete-char 15) + (goto-char 20) + (delete-char 0) + (goto-char 23) + (widen) + (narrow-to-region 12 25) + (goto-char 16) + (insert "..........") + (goto-char 25) + (widen) + (narrow-to-region 2 38) + (goto-char 34) + (delete-char 0) + (goto-char 31) + (insert "...............") + (should + (equal + (test-overlay-regions) + '((12 . 12) + (12 . 12) + (12 . 12) + (12 . 12) + (12 . 53) + (12 . 53) + (12 . 53) + (12 . 53) + (12 . 53) + (12 . 53) + (12 . 55)))))) + +(ert-deftest overlay-autogenerated-test-14 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 29 37 nil t nil) + (make-overlay 15 44 nil nil nil) + (make-overlay 31 34 nil nil t) + (make-overlay 35 33 nil t t) + (make-overlay 4 27 nil t t) + (make-overlay 37 5 nil nil t) + (make-overlay 58 19 nil nil t) + (make-overlay 57 47 nil nil t) + (make-overlay 49 5 nil t t) + (make-overlay 21 59 nil t t) + (make-overlay 42 33 nil t nil) + (make-overlay 22 16 nil t t) + (make-overlay 9 51 nil t nil) + (make-overlay 20 24 nil nil t) + (make-overlay 21 7 nil t t) + (make-overlay 58 52 nil t t) + (goto-char 39) + (widen) + (narrow-to-region 55 54) + (goto-char 54) + (insert ".............") + (goto-char 55) + (insert "............") + (goto-char 66) + (delete-char 10) + (goto-char 62) + (insert "...............") + (goto-char 82) + (delete-char 2) + (goto-char 82) + (delete-char 0) + (goto-char 76) + (insert "..............") + (goto-char 60) + (insert ".............") + (goto-char 71) + (insert "...............") + (goto-char 122) + (delete-char 0) + (goto-char 93) + (delete-char 3) + (goto-char 108) + (delete-char 1) + (goto-char 121) + (insert "........") + (goto-char 92) + (insert "") + (goto-char 103) + (insert "..........") + (goto-char 85) + (delete-char 13) + (goto-char 116) + (delete-char 7) + (goto-char 103) + (widen) + (narrow-to-region 60 27) + (goto-char 28) + (delete-char 16) + (goto-char 35) + (insert ".......") + (goto-char 47) + (insert "........") + (goto-char 38) + (delete-char 1) + (goto-char 43) + (insert "..........") + (goto-char 59) + (insert "........") + (goto-char 57) + (insert "........") + (goto-char 36) + (insert "...........") + (goto-char 82) + (delete-char 11) + (goto-char 67) + (insert "..........") + (goto-char 46) + (delete-char 1) + (goto-char 47) + (insert "......") + (goto-char 69) + (delete-char 7) + (should + (equal + (test-overlay-regions) + '((5 . 28) + (5 . 33) + (9 . 35) + (15 . 28) + (19 . 154) + (21 . 155) + (28 . 28) + (28 . 28) + (28 . 28) + (28 . 28) + (31 . 153) + (58 . 154)))))) + +(ert-deftest overlay-autogenerated-test-15 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 3 19 nil t t) + (make-overlay 11 18 nil t nil) + (make-overlay 28 51 nil nil t) + (make-overlay 29 15 nil t t) + (make-overlay 46 57 nil t t) + (make-overlay 26 24 nil nil nil) + (make-overlay 29 43 nil nil nil) + (make-overlay 54 29 nil nil nil) + (make-overlay 34 52 nil t nil) + (make-overlay 10 32 nil nil nil) + (make-overlay 28 34 nil nil t) + (make-overlay 11 43 nil nil nil) + (make-overlay 18 50 nil t t) + (make-overlay 28 39 nil nil nil) + (make-overlay 62 62 nil t t) + (make-overlay 30 62 nil t nil) + (goto-char 30) + (widen) + (narrow-to-region 6 22) + (goto-char 9) + (insert "..") + (goto-char 12) + (insert ".............") + (goto-char 29) + (insert "..............") + (goto-char 47) + (insert "........") + (goto-char 46) + (insert ".............") + (goto-char 55) + (insert "..........") + (goto-char 62) + (insert "...............") + (goto-char 47) + (delete-char 49) + (goto-char 11) + (insert "...........") + (goto-char 40) + (delete-char 1) + (goto-char 27) + (insert "..............") + (goto-char 51) + (insert "......") + (goto-char 60) + (delete-char 10) + (goto-char 37) + (insert ".........") + (goto-char 69) + (insert ".") + (goto-char 36) + (insert "............") + (goto-char 75) + (insert ".............") + (goto-char 21) + (widen) + (narrow-to-region 44 21) + (goto-char 37) + (insert ".............") + (goto-char 55) + (widen) + (narrow-to-region 84 28) + (goto-char 58) + (widen) + (narrow-to-region 96 49) + (goto-char 62) + (delete-char 0) + (goto-char 72) + (delete-char 24) + (goto-char 61) + (widen) + (narrow-to-region 105 83) + (goto-char 96) + (widen) + (narrow-to-region 109 46) + (goto-char 95) + (delete-char 4) + (goto-char 81) + (insert ".") + (goto-char 51) + (delete-char 8) + (goto-char 52) + (insert ".") + (goto-char 60) + (delete-char 10) + (goto-char 50) + (insert "......") + (should + (equal + (test-overlay-regions) + '((3 . 81) + (23 . 88) + (66 . 99) + (69 . 81) + (78 . 85) + (81 . 106) + (84 . 85) + (85 . 90) + (85 . 95) + (85 . 99) + (85 . 107) + (85 . 110) + (86 . 118) + (90 . 108)))))) + +(ert-deftest overlay-autogenerated-test-16 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 3 55 nil t nil) + (make-overlay 45 47 nil nil nil) + (make-overlay 23 57 nil t t) + (make-overlay 64 55 nil nil nil) + (make-overlay 37 26 nil t t) + (make-overlay 29 38 nil nil t) + (make-overlay 33 3 nil t t) + (make-overlay 49 16 nil t nil) + (make-overlay 35 56 nil t t) + (make-overlay 9 39 nil nil nil) + (make-overlay 2 61 nil nil nil) + (make-overlay 59 26 nil nil t) + (make-overlay 5 50 nil t t) + (make-overlay 19 19 nil nil t) + (make-overlay 64 21 nil t nil) + (make-overlay 21 8 nil nil t) + (goto-char 17) + (insert ".....") + (goto-char 29) + (insert "............") + (goto-char 42) + (delete-char 38) + (goto-char 24) + (insert "") + (goto-char 9) + (delete-char 2) + (goto-char 20) + (insert "..") + (goto-char 27) + (delete-char 8) + (goto-char 25) + (delete-char 6) + (goto-char 8) + (delete-char 21) + (goto-char 9) + (insert "..............") + (goto-char 3) + (insert "....") + (goto-char 8) + (delete-char 18) + (goto-char 6) + (widen) + (narrow-to-region 5 8) + (goto-char 5) + (delete-char 3) + (goto-char 5) + (insert "...") + (goto-char 8) + (insert "..........") + (goto-char 5) + (insert "") + (goto-char 7) + (delete-char 8) + (goto-char 8) + (widen) + (narrow-to-region 2 2) + (goto-char 2) + (delete-char 0) + (goto-char 2) + (delete-char 0) + (goto-char 2) + (delete-char 0) + (goto-char 2) + (delete-char 0) + (goto-char 2) + (widen) + (narrow-to-region 10 3) + (goto-char 8) + (delete-char 2) + (goto-char 7) + (insert ".......") + (goto-char 8) + (delete-char 3) + (goto-char 12) + (insert "..") + (goto-char 9) + (delete-char 2) + (goto-char 7) + (insert "......") + (goto-char 15) + (insert "..........") + (goto-char 4) + (insert "........") + (should + (equal + (test-overlay-regions) + '((2 . 13) + (13 . 13) + (13 . 13) + (13 . 13) + (13 . 13) + (13 . 13) + (13 . 13) + (13 . 36) + (13 . 36) + (13 . 36) + (13 . 36)))))) + +(ert-deftest overlay-autogenerated-test-17 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 15 37 nil t nil) + (make-overlay 40 3 nil t t) + (make-overlay 61 19 nil t t) + (make-overlay 46 9 nil nil t) + (make-overlay 64 39 nil nil t) + (make-overlay 50 58 nil nil t) + (make-overlay 21 30 nil t nil) + (make-overlay 44 54 nil t nil) + (make-overlay 32 2 nil t nil) + (make-overlay 14 9 nil t t) + (make-overlay 41 40 nil t nil) + (make-overlay 17 26 nil t nil) + (make-overlay 57 50 nil t t) + (make-overlay 16 65 nil nil t) + (make-overlay 13 61 nil t t) + (make-overlay 39 64 nil nil t) + (goto-char 37) + (widen) + (narrow-to-region 12 1) + (goto-char 12) + (insert "......") + (goto-char 8) + (delete-char 4) + (goto-char 11) + (delete-char 3) + (goto-char 6) + (insert ".....") + (goto-char 6) + (widen) + (narrow-to-region 53 48) + (goto-char 48) + (delete-char 5) + (goto-char 48) + (widen) + (narrow-to-region 59 58) + (goto-char 59) + (delete-char 0) + (goto-char 58) + (insert "...") + (goto-char 60) + (insert "...............") + (goto-char 58) + (insert ".............") + (goto-char 67) + (insert ".....") + (goto-char 73) + (insert "") + (goto-char 68) + (insert ".....") + (goto-char 64) + (insert "....") + (goto-char 62) + (insert "..") + (goto-char 91) + (insert "..........") + (goto-char 80) + (insert "............") + (goto-char 100) + (delete-char 21) + (goto-char 74) + (insert "...") + (goto-char 60) + (delete-char 30) + (goto-char 64) + (widen) + (narrow-to-region 71 23) + (goto-char 53) + (delete-char 11) + (goto-char 23) + (delete-char 21) + (goto-char 39) + (delete-char 0) + (goto-char 35) + (insert "") + (goto-char 35) + (insert ".........") + (goto-char 30) + (insert "...........") + (goto-char 35) + (insert "..") + (goto-char 37) + (delete-char 1) + (goto-char 28) + (delete-char 3) + (should + (equal + (test-overlay-regions) + '((13 . 27) + (17 . 67) + (20 . 71) + (23 . 23) + (23 . 24) + (23 . 67) + (23 . 70) + (23 . 70) + (27 . 41) + (28 . 41) + (28 . 41)))))) + +(ert-deftest overlay-autogenerated-test-18 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 43 52 nil nil t) + (make-overlay 27 29 nil nil t) + (make-overlay 24 18 nil nil nil) + (make-overlay 39 52 nil nil nil) + (make-overlay 33 62 nil t t) + (make-overlay 16 7 nil t nil) + (make-overlay 47 39 nil nil t) + (make-overlay 59 41 nil nil nil) + (make-overlay 22 55 nil nil t) + (make-overlay 60 16 nil t t) + (make-overlay 55 20 nil nil t) + (make-overlay 25 12 nil nil t) + (make-overlay 26 2 nil nil t) + (make-overlay 17 35 nil nil t) + (make-overlay 46 41 nil t nil) + (make-overlay 57 53 nil t t) + (goto-char 52) + (insert "") + (goto-char 4) + (delete-char 21) + (goto-char 17) + (insert "") + (goto-char 35) + (insert "...............") + (goto-char 8) + (insert "...............") + (goto-char 9) + (insert "........") + (goto-char 73) + (delete-char 9) + (goto-char 62) + (insert "...............") + (goto-char 27) + (widen) + (narrow-to-region 34 84) + (goto-char 81) + (insert "...........") + (goto-char 48) + (insert "...") + (goto-char 74) + (insert ".......") + (goto-char 41) + (widen) + (narrow-to-region 37 105) + (goto-char 75) + (insert "...............") + (goto-char 47) + (insert "..........") + (goto-char 99) + (delete-char 13) + (goto-char 105) + (delete-char 4) + (goto-char 94) + (delete-char 5) + (goto-char 96) + (insert "..............") + (goto-char 74) + (insert "") + (goto-char 121) + (insert "...") + (goto-char 102) + (insert "...") + (goto-char 64) + (insert "......") + (goto-char 67) + (insert "...") + (goto-char 95) + (delete-char 19) + (goto-char 37) + (insert "..........") + (goto-char 50) + (widen) + (narrow-to-region 67 96) + (goto-char 88) + (insert "..........") + (goto-char 91) + (insert ".............") + (goto-char 70) + (delete-char 8) + (goto-char 111) + (widen) + (narrow-to-region 72 103) + (goto-char 101) + (insert "...............") + (should + (equal + (test-overlay-regions) + '((4 . 119) + (4 . 119) + (4 . 162) + (35 . 162) + (51 . 78) + (53 . 162) + (55 . 78) + (79 . 162)))))) + +(ert-deftest overlay-autogenerated-test-19 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 19 31 nil t t) + (make-overlay 40 5 nil nil nil) + (make-overlay 13 41 nil t t) + (make-overlay 41 43 nil nil t) + (make-overlay 7 60 nil t nil) + (make-overlay 40 23 nil t nil) + (make-overlay 32 15 nil t t) + (make-overlay 12 45 nil nil nil) + (make-overlay 18 1 nil nil nil) + (make-overlay 58 32 nil t t) + (make-overlay 30 3 nil t t) + (make-overlay 43 61 nil t nil) + (make-overlay 54 57 nil nil t) + (make-overlay 34 14 nil t t) + (make-overlay 26 49 nil nil t) + (make-overlay 54 49 nil nil t) + (goto-char 28) + (insert "........") + (goto-char 32) + (insert "...........") + (goto-char 78) + (delete-char 6) + (goto-char 37) + (delete-char 0) + (goto-char 49) + (insert ".........") + (goto-char 40) + (widen) + (narrow-to-region 8 30) + (goto-char 20) + (delete-char 4) + (goto-char 23) + (delete-char 1) + (goto-char 10) + (insert ".") + (goto-char 22) + (delete-char 2) + (goto-char 22) + (insert "......") + (goto-char 17) + (insert "..........") + (goto-char 34) + (delete-char 0) + (goto-char 21) + (insert "............") + (goto-char 45) + (delete-char 7) + (goto-char 39) + (insert "...............") + (goto-char 29) + (insert "........") + (goto-char 9) + (delete-char 3) + (goto-char 63) + (delete-char 1) + (goto-char 33) + (insert "........") + (goto-char 16) + (delete-char 36) + (goto-char 20) + (delete-char 2) + (goto-char 28) + (delete-char 0) + (goto-char 24) + (insert "...........") + (goto-char 43) + (insert "..........") + (goto-char 30) + (delete-char 1) + (goto-char 40) + (delete-char 13) + (goto-char 22) + (delete-char 19) + (goto-char 10) + (delete-char 8) + (goto-char 14) + (delete-char 0) + (goto-char 12) + (delete-char 2) + (goto-char 11) + (delete-char 0) + (should + (equal + (test-overlay-regions) + '((1 . 12) + (3 . 40) + (5 . 50) + (7 . 69) + (10 . 42) + (10 . 44) + (10 . 51) + (10 . 55)))))) + +(ert-deftest overlay-autogenerated-test-20 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 44 42 nil t t) + (make-overlay 47 1 nil nil nil) + (make-overlay 24 48 nil nil nil) + (make-overlay 62 50 nil nil t) + (make-overlay 54 38 nil nil nil) + (make-overlay 3 9 nil nil nil) + (make-overlay 61 28 nil t nil) + (make-overlay 33 33 nil nil t) + (make-overlay 37 37 nil t nil) + (make-overlay 20 13 nil nil t) + (make-overlay 54 36 nil t nil) + (make-overlay 18 58 nil nil t) + (make-overlay 55 3 nil nil t) + (make-overlay 23 21 nil t t) + (make-overlay 47 55 nil t t) + (make-overlay 50 12 nil nil nil) + (goto-char 11) + (delete-char 46) + (goto-char 7) + (delete-char 3) + (goto-char 14) + (delete-char 1) + (goto-char 14) + (insert "......") + (goto-char 14) + (delete-char 4) + (goto-char 12) + (widen) + (narrow-to-region 11 12) + (goto-char 11) + (insert "...") + (goto-char 13) + (delete-char 1) + (goto-char 14) + (insert ".") + (goto-char 13) + (delete-char 2) + (goto-char 11) + (delete-char 2) + (goto-char 11) + (insert "") + (goto-char 11) + (delete-char 0) + (goto-char 11) + (delete-char 0) + (goto-char 11) + (delete-char 0) + (goto-char 11) + (insert ".") + (goto-char 11) + (insert ".") + (goto-char 12) + (insert "......") + (goto-char 14) + (delete-char 2) + (goto-char 11) + (delete-char 2) + (goto-char 14) + (insert "............") + (goto-char 19) + (insert "..............") + (goto-char 29) + (insert ".....") + (goto-char 42) + (delete-char 1) + (goto-char 22) + (insert ".....") + (goto-char 19) + (insert "..............") + (goto-char 42) + (insert ".....") + (goto-char 63) + (widen) + (narrow-to-region 26 42) + (goto-char 36) + (insert "..........") + (goto-char 40) + (delete-char 11) + (goto-char 26) + (delete-char 13) + (goto-char 28) + (delete-char 0) + (should + (equal + (test-overlay-regions) + '((8 . 56)))))) + +(ert-deftest overlay-autogenerated-test-21 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 65 15 nil nil nil) + (make-overlay 52 31 nil nil nil) + (make-overlay 12 51 nil t t) + (make-overlay 42 20 nil nil t) + (make-overlay 51 48 nil nil nil) + (make-overlay 59 28 nil t t) + (make-overlay 51 53 nil t nil) + (make-overlay 50 59 nil nil t) + (make-overlay 24 40 nil t nil) + (make-overlay 51 61 nil nil nil) + (make-overlay 12 58 nil nil t) + (make-overlay 64 17 nil t t) + (make-overlay 26 38 nil t t) + (make-overlay 23 36 nil nil nil) + (make-overlay 57 50 nil nil nil) + (make-overlay 42 15 nil nil t) + (goto-char 14) + (insert "............") + (goto-char 37) + (insert ".") + (goto-char 73) + (insert "..........") + (goto-char 17) + (delete-char 31) + (goto-char 21) + (delete-char 35) + (goto-char 9) + (delete-char 0) + (goto-char 7) + (delete-char 2) + (goto-char 1) + (insert "") + (goto-char 5) + (insert ".......") + (goto-char 8) + (insert "....") + (goto-char 27) + (delete-char 0) + (goto-char 10) + (insert ".............") + (goto-char 24) + (delete-char 16) + (goto-char 14) + (insert ".............") + (goto-char 25) + (delete-char 11) + (goto-char 3) + (insert "........") + (goto-char 38) + (insert "............") + (goto-char 41) + (insert "..............") + (goto-char 56) + (delete-char 3) + (goto-char 15) + (widen) + (narrow-to-region 16 53) + (goto-char 19) + (widen) + (narrow-to-region 18 33) + (goto-char 32) + (insert "......") + (goto-char 38) + (delete-char 1) + (goto-char 19) + (widen) + (narrow-to-region 11 11) + (goto-char 11) + (insert ".........") + (goto-char 11) + (insert ".........") + (goto-char 20) + (widen) + (narrow-to-region 22 69) + (goto-char 49) + (insert ".........") + (goto-char 54) + (delete-char 22) + (goto-char 44) + (insert "........") + (goto-char 40) + (delete-char 7) + (goto-char 29) + (delete-char 22) + (should + (equal + (test-overlay-regions) + '((33 . 33) + (33 . 33) + (33 . 33) + (33 . 33) + (33 . 33) + (33 . 33) + (33 . 33) + (33 . 33) + (33 . 33) + (33 . 33) + (33 . 33) + (33 . 33) + (33 . 33) + (33 . 33) + (33 . 33) + (33 . 33)))))) + +(ert-deftest overlay-autogenerated-test-22 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 12 14 nil nil t) + (make-overlay 54 7 nil nil t) + (make-overlay 8 3 nil nil nil) + (make-overlay 42 32 nil nil nil) + (make-overlay 10 27 nil t t) + (make-overlay 50 28 nil t t) + (make-overlay 39 35 nil nil nil) + (make-overlay 12 4 nil t t) + (make-overlay 29 54 nil nil nil) + (make-overlay 14 52 nil t t) + (make-overlay 9 15 nil t nil) + (make-overlay 44 11 nil nil nil) + (make-overlay 46 29 nil t t) + (make-overlay 40 58 nil t t) + (make-overlay 40 61 nil t nil) + (make-overlay 13 59 nil nil t) + (goto-char 32) + (insert ".............") + (goto-char 25) + (delete-char 10) + (goto-char 3) + (insert ".............") + (goto-char 33) + (delete-char 32) + (goto-char 39) + (widen) + (narrow-to-region 41 46) + (goto-char 43) + (delete-char 2) + (goto-char 42) + (delete-char 2) + (goto-char 42) + (insert "...") + (goto-char 43) + (delete-char 1) + (goto-char 42) + (widen) + (narrow-to-region 8 46) + (goto-char 25) + (delete-char 7) + (goto-char 12) + (delete-char 10) + (goto-char 23) + (insert "...............") + (goto-char 41) + (delete-char 3) + (goto-char 17) + (insert ".........") + (goto-char 37) + (insert "...............") + (goto-char 53) + (delete-char 7) + (goto-char 53) + (delete-char 0) + (goto-char 42) + (widen) + (narrow-to-region 20 54) + (goto-char 20) + (delete-char 28) + (goto-char 23) + (insert "..........") + (goto-char 30) + (insert "......") + (goto-char 26) + (delete-char 1) + (goto-char 27) + (widen) + (narrow-to-region 40 37) + (goto-char 37) + (insert ".....") + (goto-char 41) + (widen) + (narrow-to-region 13 37) + (goto-char 29) + (insert "...........") + (goto-char 33) + (delete-char 7) + (goto-char 33) + (delete-char 8) + (goto-char 20) + (insert "") + (goto-char 23) + (delete-char 7) + (goto-char 14) + (widen) + (narrow-to-region 33 33) + (should + (equal + (test-overlay-regions) + '((15 . 39) + (16 . 38) + (16 . 39)))))) + +(ert-deftest overlay-autogenerated-test-23 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 51 32 nil t t) + (make-overlay 13 61 nil t nil) + (make-overlay 47 19 nil nil t) + (make-overlay 11 30 nil nil nil) + (make-overlay 50 26 nil t t) + (make-overlay 64 13 nil t t) + (make-overlay 29 8 nil t t) + (make-overlay 25 42 nil t t) + (make-overlay 33 28 nil t t) + (make-overlay 54 7 nil nil nil) + (make-overlay 30 59 nil nil nil) + (make-overlay 65 50 nil t t) + (make-overlay 64 15 nil t nil) + (make-overlay 16 35 nil nil nil) + (make-overlay 40 36 nil nil t) + (make-overlay 31 35 nil t nil) + (goto-char 61) + (insert "......") + (goto-char 55) + (delete-char 2) + (goto-char 20) + (insert "..............") + (goto-char 56) + (insert "............") + (goto-char 48) + (delete-char 6) + (goto-char 9) + (delete-char 54) + (goto-char 20) + (delete-char 2) + (goto-char 16) + (delete-char 12) + (goto-char 18) + (insert ".............") + (goto-char 24) + (delete-char 7) + (goto-char 5) + (delete-char 2) + (goto-char 1) + (insert ".......") + (goto-char 1) + (insert ".......") + (goto-char 33) + (insert "") + (goto-char 4) + (insert "..") + (goto-char 5) + (widen) + (narrow-to-region 17 4) + (goto-char 13) + (insert ".") + (goto-char 8) + (insert "............") + (goto-char 9) + (delete-char 3) + (goto-char 4) + (widen) + (narrow-to-region 32 32) + (goto-char 32) + (delete-char 0) + (goto-char 32) + (delete-char 0) + (goto-char 32) + (delete-char 0) + (goto-char 32) + (insert "...............") + (goto-char 43) + (delete-char 4) + (goto-char 32) + (delete-char 1) + (goto-char 40) + (widen) + (narrow-to-region 33 19) + (goto-char 27) + (insert "........") + (goto-char 38) + (delete-char 2) + (goto-char 26) + (insert "") + (goto-char 33) + (delete-char 1) + (goto-char 27) + (insert ".") + (should + (equal + (test-overlay-regions) + '((38 . 56)))))) + +(ert-deftest overlay-autogenerated-test-24 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 63 8 nil t t) + (make-overlay 10 13 nil nil t) + (make-overlay 40 38 nil nil nil) + (make-overlay 21 34 nil t t) + (make-overlay 55 29 nil nil nil) + (make-overlay 36 65 nil t t) + (make-overlay 29 12 nil t nil) + (make-overlay 41 3 nil nil t) + (make-overlay 20 9 nil t t) + (make-overlay 52 42 nil t t) + (make-overlay 21 56 nil nil t) + (make-overlay 25 65 nil nil nil) + (make-overlay 38 4 nil t t) + (make-overlay 48 23 nil t t) + (make-overlay 52 9 nil nil t) + (make-overlay 48 19 nil nil nil) + (goto-char 43) + (delete-char 8) + (goto-char 30) + (delete-char 16) + (goto-char 7) + (insert "...") + (goto-char 14) + (delete-char 5) + (goto-char 36) + (delete-char 0) + (goto-char 9) + (insert "...............") + (goto-char 13) + (delete-char 17) + (goto-char 16) + (delete-char 2) + (goto-char 9) + (insert "") + (goto-char 11) + (delete-char 5) + (goto-char 18) + (insert "........") + (goto-char 15) + (insert "....") + (goto-char 16) + (delete-char 14) + (goto-char 20) + (insert ".") + (goto-char 25) + (delete-char 1) + (goto-char 14) + (delete-char 14) + (goto-char 3) + (delete-char 7) + (goto-char 3) + (delete-char 4) + (goto-char 1) + (insert "...........") + (goto-char 9) + (insert ".......") + (goto-char 5) + (delete-char 7) + (goto-char 12) + (insert ".........") + (goto-char 2) + (delete-char 4) + (goto-char 3) + (widen) + (narrow-to-region 14 6) + (goto-char 9) + (insert "..........") + (goto-char 13) + (delete-char 8) + (goto-char 7) + (delete-char 7) + (goto-char 7) + (insert "..") + (goto-char 9) + (insert ".............") + (goto-char 9) + (insert "..........") + (goto-char 21) + (insert "...............") + (goto-char 42) + (insert ".........") + (should + (equal + (test-overlay-regions) + 'nil)))) + +(ert-deftest overlay-autogenerated-test-25 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 24 8 nil nil t) + (make-overlay 41 16 nil t nil) + (make-overlay 3 16 nil nil nil) + (make-overlay 26 42 nil nil nil) + (make-overlay 32 45 nil nil t) + (make-overlay 34 19 nil nil nil) + (make-overlay 37 54 nil nil t) + (make-overlay 44 34 nil t nil) + (make-overlay 49 40 nil t t) + (make-overlay 29 34 nil t nil) + (make-overlay 54 16 nil t t) + (make-overlay 29 4 nil t nil) + (make-overlay 44 57 nil nil nil) + (make-overlay 5 32 nil nil nil) + (make-overlay 12 33 nil nil t) + (make-overlay 38 29 nil t nil) + (goto-char 12) + (delete-char 53) + (goto-char 1) + (delete-char 6) + (goto-char 5) + (widen) + (narrow-to-region 6 1) + (goto-char 6) + (insert "......") + (goto-char 10) + (insert "...............") + (goto-char 17) + (delete-char 5) + (goto-char 7) + (insert ".....") + (goto-char 8) + (insert "...............") + (goto-char 4) + (insert ".....") + (goto-char 44) + (widen) + (narrow-to-region 18 11) + (goto-char 15) + (delete-char 1) + (goto-char 17) + (delete-char 0) + (goto-char 13) + (delete-char 3) + (goto-char 14) + (insert "..") + (goto-char 16) + (insert "..") + (goto-char 15) + (delete-char 3) + (goto-char 13) + (delete-char 0) + (goto-char 14) + (insert "..........") + (goto-char 19) + (insert ".") + (goto-char 23) + (delete-char 1) + (goto-char 12) + (widen) + (narrow-to-region 23 40) + (goto-char 35) + (insert "....") + (goto-char 33) + (insert "..........") + (goto-char 37) + (delete-char 16) + (goto-char 37) + (delete-char 0) + (goto-char 23) + (widen) + (narrow-to-region 30 8) + (goto-char 29) + (delete-char 0) + (goto-char 15) + (delete-char 15) + (goto-char 9) + (insert "...........") + (goto-char 9) + (delete-char 1) + (goto-char 22) + (delete-char 3) + (goto-char 10) + (insert ".........") + (should + (equal + (test-overlay-regions) + '((1 . 30) + (1 . 30) + (1 . 30) + (2 . 53) + (30 . 30) + (30 . 30) + (30 . 30) + (30 . 30) + (30 . 30) + (30 . 30) + (30 . 30) + (30 . 53) + (30 . 53) + (30 . 53)))))) + +(ert-deftest overlay-autogenerated-test-26 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 60 59 nil t nil) + (make-overlay 18 11 nil nil t) + (make-overlay 4 44 nil nil nil) + (make-overlay 7 22 nil nil nil) + (make-overlay 54 50 nil t nil) + (make-overlay 59 28 nil nil nil) + (make-overlay 49 23 nil nil t) + (make-overlay 21 5 nil t nil) + (make-overlay 17 39 nil t nil) + (make-overlay 16 14 nil nil nil) + (make-overlay 50 26 nil nil nil) + (make-overlay 37 14 nil nil nil) + (make-overlay 6 59 nil nil t) + (make-overlay 30 17 nil nil t) + (make-overlay 17 34 nil nil t) + (make-overlay 7 22 nil t nil) + (goto-char 35) + (delete-char 25) + (goto-char 30) + (delete-char 7) + (goto-char 25) + (widen) + (narrow-to-region 3 19) + (goto-char 6) + (insert ".........") + (goto-char 21) + (insert "...............") + (goto-char 12) + (insert ".............") + (goto-char 34) + (widen) + (narrow-to-region 64 37) + (goto-char 62) + (insert ".............") + (goto-char 50) + (widen) + (narrow-to-region 72 38) + (goto-char 66) + (insert "") + (goto-char 54) + (insert "...") + (goto-char 70) + (delete-char 4) + (goto-char 49) + (delete-char 13) + (goto-char 38) + (insert "....") + (goto-char 46) + (insert ".") + (goto-char 43) + (widen) + (narrow-to-region 74 53) + (goto-char 60) + (delete-char 10) + (goto-char 53) + (insert "..............") + (goto-char 72) + (insert "............") + (goto-char 87) + (delete-char 2) + (goto-char 73) + (insert "............") + (goto-char 81) + (insert "........") + (goto-char 106) + (insert "...") + (goto-char 95) + (widen) + (narrow-to-region 77 39) + (goto-char 43) + (insert "..........") + (goto-char 40) + (insert "...............") + (goto-char 101) + (insert "") + (goto-char 53) + (insert "....") + (goto-char 79) + (delete-char 21) + (goto-char 85) + (insert "........") + (goto-char 52) + (delete-char 41) + (goto-char 43) + (insert ".....") + (should + (equal + (test-overlay-regions) + '((4 . 90) + (5 . 57) + (6 . 90) + (29 . 57) + (29 . 57) + (33 . 57)))))) + +(ert-deftest overlay-autogenerated-test-27 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 20 12 nil t nil) + (make-overlay 3 10 nil t t) + (make-overlay 11 53 nil t nil) + (make-overlay 59 3 nil t nil) + (make-overlay 28 19 nil t t) + (make-overlay 16 30 nil t t) + (make-overlay 39 19 nil t t) + (make-overlay 33 50 nil t nil) + (make-overlay 36 54 nil nil nil) + (make-overlay 42 59 nil nil nil) + (make-overlay 30 48 nil t nil) + (make-overlay 20 13 nil nil t) + (make-overlay 63 48 nil t nil) + (make-overlay 48 12 nil t t) + (make-overlay 64 50 nil nil nil) + (make-overlay 7 7 nil nil nil) + (goto-char 20) + (widen) + (narrow-to-region 21 54) + (goto-char 40) + (insert "..........") + (goto-char 21) + (delete-char 2) + (goto-char 35) + (widen) + (narrow-to-region 70 11) + (goto-char 45) + (insert "...............") + (goto-char 74) + (insert ".") + (goto-char 28) + (widen) + (narrow-to-region 77 67) + (goto-char 72) + (insert "..........") + (goto-char 85) + (delete-char 1) + (goto-char 82) + (widen) + (narrow-to-region 83 86) + (goto-char 83) + (delete-char 0) + (goto-char 86) + (delete-char 0) + (goto-char 86) + (insert "...........") + (goto-char 97) + (insert ".......") + (goto-char 103) + (widen) + (narrow-to-region 44 68) + (goto-char 49) + (insert "..") + (goto-char 65) + (insert ".............") + (goto-char 59) + (delete-char 0) + (goto-char 57) + (insert "........") + (goto-char 55) + (delete-char 30) + (goto-char 45) + (insert "...............") + (goto-char 44) + (insert "") + (goto-char 62) + (insert "............") + (goto-char 63) + (widen) + (narrow-to-region 12 5) + (goto-char 8) + (delete-char 4) + (goto-char 6) + (delete-char 0) + (goto-char 7) + (insert "..........") + (goto-char 15) + (delete-char 0) + (goto-char 16) + (insert "............") + (goto-char 20) + (insert ".........") + (goto-char 13) + (insert "..") + (goto-char 32) + (insert "..............") + (should + (equal + (test-overlay-regions) + '((3 . 55) + (3 . 173) + (7 . 7)))))) + +(ert-deftest overlay-autogenerated-test-28 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 59 48 nil t nil) + (make-overlay 59 4 nil nil t) + (make-overlay 45 35 nil t nil) + (make-overlay 13 18 nil t t) + (make-overlay 10 7 nil t t) + (make-overlay 9 8 nil nil nil) + (make-overlay 33 47 nil nil t) + (make-overlay 1 57 nil t nil) + (make-overlay 16 59 nil nil t) + (make-overlay 43 58 nil nil t) + (make-overlay 6 11 nil nil nil) + (make-overlay 59 7 nil t nil) + (make-overlay 3 57 nil t t) + (make-overlay 61 35 nil nil nil) + (make-overlay 57 8 nil nil nil) + (make-overlay 5 32 nil t nil) + (goto-char 18) + (insert "............") + (goto-char 43) + (delete-char 2) + (goto-char 38) + (delete-char 26) + (goto-char 42) + (insert ".....") + (goto-char 52) + (insert "..........") + (goto-char 45) + (delete-char 11) + (goto-char 33) + (insert "....") + (goto-char 23) + (delete-char 14) + (goto-char 33) + (widen) + (narrow-to-region 30 33) + (goto-char 30) + (delete-char 0) + (goto-char 30) + (insert "...........") + (goto-char 30) + (delete-char 7) + (goto-char 30) + (insert ".") + (goto-char 32) + (delete-char 4) + (goto-char 34) + (delete-char 0) + (goto-char 34) + (delete-char 0) + (goto-char 32) + (insert "...............") + (goto-char 46) + (insert ".........") + (goto-char 45) + (delete-char 3) + (goto-char 49) + (delete-char 2) + (goto-char 42) + (delete-char 2) + (goto-char 32) + (insert "..........") + (goto-char 47) + (insert "....") + (goto-char 59) + (insert ".......") + (goto-char 35) + (insert ".") + (goto-char 45) + (insert "..............") + (goto-char 37) + (insert "..") + (goto-char 80) + (insert ".....") + (goto-char 30) + (insert ".............") + (goto-char 102) + (insert "............") + (goto-char 113) + (insert "") + (goto-char 66) + (widen) + (narrow-to-region 47 38) + (should + (equal + (test-overlay-regions) + '((1 . 45) + (3 . 117) + (4 . 121) + (7 . 121) + (8 . 45) + (16 . 121) + (28 . 121) + (28 . 121) + (28 . 121)))))) + +(ert-deftest overlay-autogenerated-test-29 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 5 63 nil nil t) + (make-overlay 20 28 nil t t) + (make-overlay 58 53 nil t nil) + (make-overlay 4 57 nil t t) + (make-overlay 4 16 nil nil nil) + (make-overlay 33 26 nil t nil) + (make-overlay 9 32 nil t t) + (make-overlay 11 8 nil nil nil) + (make-overlay 59 35 nil nil t) + (make-overlay 15 25 nil t t) + (make-overlay 36 16 nil nil nil) + (make-overlay 8 37 nil nil nil) + (make-overlay 65 63 nil nil t) + (make-overlay 3 20 nil nil t) + (make-overlay 44 55 nil t t) + (make-overlay 45 25 nil t nil) + (goto-char 39) + (insert "...") + (goto-char 22) + (insert "........") + (goto-char 60) + (insert ".........") + (goto-char 17) + (insert "............") + (goto-char 13) + (widen) + (narrow-to-region 79 16) + (goto-char 19) + (delete-char 11) + (goto-char 25) + (insert "........") + (goto-char 61) + (insert "....") + (goto-char 45) + (widen) + (narrow-to-region 73 66) + (goto-char 71) + (insert "............") + (goto-char 81) + (delete-char 2) + (goto-char 73) + (insert "..........") + (goto-char 74) + (insert "............") + (goto-char 82) + (delete-char 7) + (goto-char 78) + (delete-char 18) + (goto-char 75) + (insert ".........") + (goto-char 66) + (insert ".........") + (goto-char 86) + (delete-char 12) + (goto-char 77) + (widen) + (narrow-to-region 23 55) + (goto-char 43) + (insert ".") + (goto-char 50) + (insert "..") + (goto-char 25) + (delete-char 18) + (goto-char 33) + (delete-char 7) + (goto-char 26) + (insert "........") + (goto-char 29) + (insert "...........") + (goto-char 33) + (insert "...") + (goto-char 40) + (insert "..........") + (goto-char 26) + (insert "") + (goto-char 35) + (insert ".") + (goto-char 59) + (insert ".") + (goto-char 51) + (insert "..") + (goto-char 59) + (insert ".............") + (should + (equal + (test-overlay-regions) + '((4 . 130) + (5 . 136) + (8 . 82) + (9 . 82) + (15 . 25) + (16 . 82) + (21 . 77) + (25 . 105) + (75 . 82)))))) + +(ert-deftest overlay-autogenerated-test-30 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 27 65 nil t t) + (make-overlay 39 51 nil t t) + (make-overlay 53 2 nil nil nil) + (make-overlay 3 17 nil nil t) + (make-overlay 35 4 nil nil t) + (make-overlay 65 53 nil t nil) + (make-overlay 8 21 nil t t) + (make-overlay 18 62 nil t t) + (make-overlay 42 59 nil nil t) + (make-overlay 12 37 nil t t) + (make-overlay 64 31 nil t nil) + (make-overlay 39 54 nil nil t) + (make-overlay 41 24 nil t nil) + (make-overlay 10 21 nil nil t) + (make-overlay 49 15 nil t nil) + (make-overlay 49 63 nil nil t) + (goto-char 43) + (insert "..........") + (goto-char 44) + (delete-char 29) + (goto-char 32) + (insert "..") + (goto-char 13) + (insert ".") + (goto-char 42) + (insert ".........") + (goto-char 39) + (insert "..........") + (goto-char 15) + (insert "............") + (goto-char 58) + (delete-char 9) + (goto-char 63) + (insert ".........") + (goto-char 49) + (insert ".") + (goto-char 28) + (delete-char 51) + (goto-char 12) + (delete-char 6) + (goto-char 20) + (delete-char 2) + (goto-char 7) + (widen) + (narrow-to-region 2 9) + (goto-char 5) + (insert "...............") + (goto-char 18) + (delete-char 1) + (goto-char 4) + (insert ".............") + (goto-char 13) + (delete-char 22) + (goto-char 12) + (insert "") + (goto-char 3) + (insert ".............") + (goto-char 22) + (insert "...............") + (goto-char 9) + (insert "....") + (goto-char 8) + (insert "...........") + (goto-char 6) + (delete-char 34) + (goto-char 21) + (insert "....") + (goto-char 14) + (insert ".....") + (goto-char 20) + (insert ".......") + (goto-char 34) + (widen) + (narrow-to-region 3 2) + (goto-char 3) + (delete-char 0) + (goto-char 2) + (insert "..............") + (goto-char 15) + (delete-char 2) + (goto-char 11) + (insert "......") + (should + (equal + (test-overlay-regions) + '((2 . 68)))))) + +(ert-deftest overlay-autogenerated-test-31 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 54 64 nil nil nil) + (make-overlay 49 12 nil nil t) + (make-overlay 40 12 nil t nil) + (make-overlay 17 38 nil nil nil) + (make-overlay 21 36 nil t t) + (make-overlay 8 38 nil t nil) + (make-overlay 50 22 nil t nil) + (make-overlay 65 15 nil nil t) + (make-overlay 57 60 nil t t) + (make-overlay 35 11 nil nil t) + (make-overlay 49 44 nil nil t) + (make-overlay 45 31 nil nil t) + (make-overlay 51 24 nil t t) + (make-overlay 20 14 nil nil nil) + (make-overlay 6 18 nil t t) + (make-overlay 25 3 nil nil nil) + (goto-char 18) + (delete-char 10) + (goto-char 36) + (delete-char 13) + (goto-char 8) + (delete-char 4) + (goto-char 2) + (delete-char 8) + (goto-char 12) + (delete-char 10) + (goto-char 15) + (delete-char 4) + (goto-char 16) + (insert ".........") + (goto-char 17) + (insert "...............") + (goto-char 33) + (delete-char 0) + (goto-char 38) + (delete-char 0) + (goto-char 11) + (insert "...........") + (goto-char 8) + (delete-char 14) + (goto-char 32) + (insert "........") + (goto-char 40) + (widen) + (narrow-to-region 14 6) + (goto-char 10) + (delete-char 1) + (goto-char 7) + (widen) + (narrow-to-region 18 39) + (goto-char 36) + (delete-char 1) + (goto-char 34) + (widen) + (narrow-to-region 39 14) + (goto-char 22) + (widen) + (narrow-to-region 25 21) + (goto-char 23) + (delete-char 2) + (goto-char 23) + (delete-char 0) + (goto-char 23) + (insert ".........") + (goto-char 32) + (delete-char 0) + (goto-char 31) + (insert ".........") + (goto-char 32) + (insert "...") + (goto-char 30) + (widen) + (narrow-to-region 10 56) + (goto-char 10) + (insert ".........") + (goto-char 38) + (insert ".........") + (goto-char 19) + (insert "..") + (goto-char 11) + (insert "..............") + (goto-char 66) + (insert "...............") + (goto-char 13) + (insert "......") + (should + (equal + (test-overlay-regions) + '((2 . 41) + (3 . 117) + (6 . 41) + (8 . 41) + (9 . 41) + (10 . 42) + (41 . 42)))))) + +(ert-deftest overlay-autogenerated-test-32 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 35 60 nil nil t) + (make-overlay 45 46 nil nil nil) + (make-overlay 47 11 nil nil t) + (make-overlay 12 51 nil t nil) + (make-overlay 61 17 nil t nil) + (make-overlay 7 24 nil t nil) + (make-overlay 36 37 nil nil t) + (make-overlay 5 39 nil t t) + (make-overlay 5 40 nil nil t) + (make-overlay 38 40 nil t t) + (make-overlay 47 45 nil t nil) + (make-overlay 61 48 nil nil nil) + (make-overlay 23 39 nil t t) + (make-overlay 11 52 nil nil nil) + (make-overlay 37 35 nil nil nil) + (make-overlay 19 20 nil t nil) + (goto-char 43) + (insert "........") + (goto-char 7) + (insert "") + (goto-char 28) + (delete-char 41) + (goto-char 3) + (delete-char 17) + (goto-char 2) + (insert ".") + (goto-char 7) + (insert ".........") + (goto-char 21) + (delete-char 4) + (goto-char 13) + (delete-char 1) + (goto-char 2) + (insert "...............") + (goto-char 7) + (insert "") + (goto-char 14) + (insert ".....") + (goto-char 16) + (insert ".") + (goto-char 10) + (insert "..............") + (goto-char 16) + (delete-char 18) + (goto-char 1) + (delete-char 36) + (goto-char 1) + (delete-char 0) + (goto-char 1) + (delete-char 0) + (goto-char 1) + (insert ".............") + (goto-char 9) + (insert ".") + (goto-char 14) + (insert ".....") + (goto-char 9) + (delete-char 0) + (goto-char 15) + (delete-char 0) + (goto-char 6) + (delete-char 4) + (goto-char 11) + (delete-char 5) + (goto-char 5) + (insert "....") + (goto-char 5) + (insert ".....") + (goto-char 12) + (insert "") + (goto-char 13) + (insert ".......") + (goto-char 14) + (insert "......") + (goto-char 9) + (delete-char 3) + (goto-char 17) + (delete-char 0) + (goto-char 7) + (delete-char 12) + (should + (equal + (test-overlay-regions) + '((1 . 1) + (1 . 1) + (1 . 1) + (1 . 1) + (1 . 1) + (1 . 1) + (1 . 1) + (1 . 1) + (1 . 1) + (1 . 18) + (1 . 18) + (1 . 18) + (1 . 18) + (18 . 18) + (18 . 18) + (18 . 18)))))) + +(ert-deftest overlay-autogenerated-test-33 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 65 33 nil t nil) + (make-overlay 45 54 nil t t) + (make-overlay 17 38 nil t nil) + (make-overlay 58 46 nil nil t) + (make-overlay 21 36 nil t t) + (make-overlay 31 63 nil nil t) + (make-overlay 37 64 nil t t) + (make-overlay 42 19 nil nil nil) + (make-overlay 51 60 nil t nil) + (make-overlay 47 15 nil t t) + (make-overlay 57 47 nil nil nil) + (make-overlay 40 45 nil nil nil) + (make-overlay 44 47 nil t nil) + (make-overlay 42 35 nil t nil) + (make-overlay 1 65 nil nil t) + (make-overlay 29 63 nil t nil) + (goto-char 33) + (insert "...........") + (goto-char 56) + (insert ".........") + (goto-char 67) + (insert "....") + (goto-char 28) + (delete-char 35) + (goto-char 9) + (insert "......") + (goto-char 43) + (delete-char 17) + (goto-char 29) + (insert ".......") + (goto-char 20) + (insert "....") + (goto-char 53) + (insert ".......") + (goto-char 14) + (widen) + (narrow-to-region 38 57) + (goto-char 51) + (insert "") + (goto-char 57) + (insert ".......") + (goto-char 64) + (insert ".....") + (goto-char 59) + (delete-char 3) + (goto-char 45) + (delete-char 12) + (goto-char 43) + (insert "......") + (goto-char 48) + (insert "......") + (goto-char 52) + (insert "........") + (goto-char 57) + (delete-char 16) + (goto-char 43) + (delete-char 9) + (goto-char 40) + (insert "") + (goto-char 39) + (insert "..........") + (goto-char 50) + (widen) + (narrow-to-region 31 27) + (goto-char 27) + (insert "..........") + (goto-char 33) + (delete-char 0) + (goto-char 37) + (insert "..") + (goto-char 38) + (delete-char 4) + (goto-char 38) + (insert "..........") + (goto-char 45) + (insert ".....") + (goto-char 53) + (insert "...") + (goto-char 51) + (insert ".") + (goto-char 28) + (insert "...") + (should + (equal + (test-overlay-regions) + '((1 . 93) + (25 . 92) + (41 . 88) + (60 . 88)))))) + +(ert-deftest overlay-autogenerated-test-34 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 2 63 nil nil t) + (make-overlay 54 30 nil t nil) + (make-overlay 21 57 nil t nil) + (make-overlay 61 19 nil nil nil) + (make-overlay 55 8 nil nil t) + (make-overlay 14 51 nil nil nil) + (make-overlay 33 13 nil t t) + (make-overlay 36 25 nil t t) + (make-overlay 22 21 nil nil t) + (make-overlay 21 48 nil nil t) + (make-overlay 36 7 nil nil t) + (make-overlay 2 40 nil nil nil) + (make-overlay 21 27 nil nil t) + (make-overlay 26 2 nil nil nil) + (make-overlay 60 43 nil nil nil) + (make-overlay 12 50 nil t t) + (goto-char 44) + (delete-char 6) + (goto-char 5) + (insert "..") + (goto-char 17) + (insert "........") + (goto-char 48) + (insert "..") + (goto-char 27) + (delete-char 29) + (goto-char 10) + (delete-char 2) + (goto-char 35) + (insert ".............") + (goto-char 20) + (delete-char 0) + (goto-char 6) + (insert ".") + (goto-char 9) + (delete-char 6) + (goto-char 38) + (insert ".........") + (goto-char 5) + (insert ".........") + (goto-char 10) + (delete-char 20) + (goto-char 6) + (delete-char 6) + (goto-char 14) + (insert ".............") + (goto-char 31) + (delete-char 10) + (goto-char 20) + (widen) + (narrow-to-region 27 39) + (goto-char 34) + (delete-char 5) + (goto-char 32) + (delete-char 1) + (goto-char 27) + (insert "..") + (goto-char 28) + (insert "........") + (goto-char 39) + (insert "........") + (goto-char 38) + (delete-char 7) + (goto-char 44) + (delete-char 0) + (goto-char 30) + (insert "...............") + (goto-char 43) + (insert "............") + (goto-char 56) + (delete-char 1) + (goto-char 65) + (delete-char 3) + (goto-char 36) + (insert ".........") + (goto-char 74) + (insert ".....") + (goto-char 67) + (delete-char 5) + (goto-char 38) + (insert "..") + (should + (equal + (test-overlay-regions) + '((2 . 80) + (6 . 78)))))) + +(ert-deftest overlay-autogenerated-test-35 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 38 16 nil nil nil) + (make-overlay 19 22 nil t nil) + (make-overlay 16 43 nil nil t) + (make-overlay 27 5 nil nil nil) + (make-overlay 43 34 nil t nil) + (make-overlay 47 4 nil nil t) + (make-overlay 1 47 nil nil t) + (make-overlay 27 35 nil t nil) + (make-overlay 41 41 nil nil t) + (make-overlay 21 19 nil nil nil) + (make-overlay 16 38 nil nil t) + (make-overlay 33 39 nil t nil) + (make-overlay 34 51 nil nil t) + (make-overlay 45 36 nil t nil) + (make-overlay 42 18 nil t t) + (make-overlay 12 30 nil nil nil) + (goto-char 18) + (insert "") + (goto-char 58) + (delete-char 3) + (goto-char 58) + (delete-char 0) + (goto-char 1) + (insert ".......") + (goto-char 48) + (delete-char 17) + (goto-char 39) + (delete-char 6) + (goto-char 33) + (widen) + (narrow-to-region 45 46) + (goto-char 46) + (insert "") + (goto-char 46) + (delete-char 0) + (goto-char 46) + (insert ".....") + (goto-char 51) + (widen) + (narrow-to-region 17 26) + (goto-char 25) + (widen) + (narrow-to-region 50 41) + (goto-char 45) + (insert "..............") + (goto-char 59) + (insert "...........") + (goto-char 47) + (delete-char 9) + (goto-char 59) + (insert "") + (goto-char 46) + (insert "") + (goto-char 54) + (delete-char 5) + (goto-char 57) + (widen) + (narrow-to-region 57 31) + (goto-char 42) + (delete-char 2) + (goto-char 52) + (insert "....") + (goto-char 44) + (insert "..") + (goto-char 44) + (insert "...............") + (goto-char 72) + (delete-char 1) + (goto-char 66) + (delete-char 6) + (goto-char 64) + (delete-char 5) + (goto-char 49) + (delete-char 12) + (goto-char 32) + (insert "......") + (goto-char 44) + (delete-char 2) + (goto-char 39) + (delete-char 12) + (goto-char 42) + (insert "......") + (goto-char 36) + (widen) + (narrow-to-region 14 47) + (should + (equal + (test-overlay-regions) + '((1 . 39) + (11 . 39) + (12 . 39) + (19 . 39) + (23 . 39) + (23 . 39) + (23 . 39) + (25 . 39) + (26 . 28) + (26 . 29) + (39 . 39) + (39 . 39) + (39 . 39) + (39 . 39) + (39 . 39) + (39 . 39)))))) + +(ert-deftest overlay-autogenerated-test-36 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 1 38 nil t t) + (make-overlay 58 34 nil t nil) + (make-overlay 6 33 nil nil t) + (make-overlay 63 54 nil nil t) + (make-overlay 54 54 nil t t) + (make-overlay 21 61 nil nil nil) + (make-overlay 64 55 nil nil t) + (make-overlay 28 65 nil nil t) + (make-overlay 32 51 nil t nil) + (make-overlay 36 38 nil nil nil) + (make-overlay 35 21 nil nil nil) + (make-overlay 65 48 nil nil nil) + (make-overlay 32 27 nil nil t) + (make-overlay 27 55 nil t t) + (make-overlay 30 22 nil t nil) + (make-overlay 14 58 nil t nil) + (goto-char 40) + (delete-char 7) + (goto-char 42) + (insert "......") + (goto-char 11) + (widen) + (narrow-to-region 64 9) + (goto-char 21) + (delete-char 23) + (goto-char 24) + (insert "...") + (goto-char 13) + (insert "..........") + (goto-char 12) + (delete-char 5) + (goto-char 10) + (delete-char 0) + (goto-char 21) + (widen) + (narrow-to-region 9 5) + (goto-char 6) + (delete-char 0) + (goto-char 9) + (delete-char 0) + (goto-char 9) + (delete-char 0) + (goto-char 7) + (insert "............") + (goto-char 9) + (insert "...") + (goto-char 18) + (insert ".") + (goto-char 23) + (delete-char 1) + (goto-char 9) + (insert "....") + (goto-char 6) + (insert ".....") + (goto-char 23) + (widen) + (narrow-to-region 28 1) + (goto-char 6) + (insert "...........") + (goto-char 30) + (delete-char 8) + (goto-char 2) + (insert ".") + (goto-char 18) + (insert "......") + (goto-char 5) + (delete-char 9) + (goto-char 5) + (delete-char 20) + (goto-char 4) + (delete-char 3) + (goto-char 3) + (delete-char 2) + (goto-char 3) + (delete-char 0) + (goto-char 1) + (insert "......") + (goto-char 8) + (widen) + (narrow-to-region 39 2) + (goto-char 13) + (delete-char 12) + (goto-char 24) + (delete-char 0) + (should + (equal + (test-overlay-regions) + '((7 . 20) + (9 . 20) + (13 . 36) + (20 . 20) + (20 . 20) + (20 . 20) + (20 . 20) + (20 . 29) + (20 . 33) + (20 . 36) + (20 . 39) + (20 . 43) + (20 . 43)))))) + +(ert-deftest overlay-autogenerated-test-37 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 26 30 nil nil nil) + (make-overlay 55 50 nil nil t) + (make-overlay 43 54 nil nil t) + (make-overlay 53 48 nil nil nil) + (make-overlay 37 51 nil nil t) + (make-overlay 15 30 nil nil nil) + (make-overlay 2 24 nil t t) + (make-overlay 56 61 nil t nil) + (make-overlay 65 46 nil t nil) + (make-overlay 28 47 nil t nil) + (make-overlay 21 24 nil t t) + (make-overlay 17 13 nil t t) + (make-overlay 7 44 nil t nil) + (make-overlay 28 63 nil nil nil) + (make-overlay 22 16 nil t t) + (make-overlay 26 44 nil t t) + (goto-char 57) + (delete-char 6) + (goto-char 42) + (insert ".....") + (goto-char 63) + (insert ".............") + (goto-char 17) + (insert "") + (goto-char 57) + (insert "...........") + (goto-char 3) + (delete-char 47) + (goto-char 15) + (insert ".............") + (goto-char 28) + (insert "") + (goto-char 17) + (delete-char 31) + (goto-char 7) + (delete-char 16) + (goto-char 2) + (insert "...........") + (goto-char 2) + (insert "..") + (goto-char 18) + (widen) + (narrow-to-region 20 8) + (goto-char 13) + (widen) + (narrow-to-region 12 10) + (goto-char 10) + (delete-char 1) + (goto-char 11) + (delete-char 0) + (goto-char 10) + (insert "...") + (goto-char 11) + (delete-char 0) + (goto-char 13) + (insert "..") + (goto-char 16) + (delete-char 0) + (goto-char 10) + (delete-char 2) + (goto-char 11) + (insert ".....") + (goto-char 16) + (widen) + (narrow-to-region 6 13) + (goto-char 10) + (insert "..") + (goto-char 6) + (delete-char 6) + (goto-char 8) + (insert "...............") + (goto-char 21) + (delete-char 0) + (goto-char 21) + (widen) + (narrow-to-region 36 11) + (goto-char 12) + (insert "...............") + (goto-char 19) + (insert ".......") + (goto-char 56) + (delete-char 2) + (goto-char 42) + (delete-char 11) + (should + (equal + (test-overlay-regions) + '((44 . 45)))))) + +(ert-deftest overlay-autogenerated-test-38 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 29 13 nil t t) + (make-overlay 19 28 nil nil t) + (make-overlay 47 33 nil nil nil) + (make-overlay 8 44 nil t nil) + (make-overlay 48 4 nil t nil) + (make-overlay 8 20 nil t t) + (make-overlay 38 31 nil nil t) + (make-overlay 17 65 nil nil t) + (make-overlay 49 31 nil nil nil) + (make-overlay 39 19 nil nil t) + (make-overlay 40 49 nil t t) + (make-overlay 24 16 nil t t) + (make-overlay 4 41 nil t nil) + (make-overlay 61 42 nil t nil) + (make-overlay 46 11 nil nil nil) + (make-overlay 1 43 nil nil t) + (goto-char 62) + (delete-char 2) + (goto-char 25) + (widen) + (narrow-to-region 30 38) + (goto-char 37) + (delete-char 1) + (goto-char 37) + (insert "...........") + (goto-char 41) + (delete-char 3) + (goto-char 39) + (delete-char 5) + (goto-char 39) + (widen) + (narrow-to-region 31 9) + (goto-char 11) + (insert "..............") + (goto-char 9) + (widen) + (narrow-to-region 62 30) + (goto-char 32) + (widen) + (narrow-to-region 17 48) + (goto-char 39) + (delete-char 7) + (goto-char 24) + (delete-char 8) + (goto-char 19) + (insert "") + (goto-char 25) + (delete-char 5) + (goto-char 28) + (delete-char 0) + (goto-char 22) + (widen) + (narrow-to-region 52 35) + (goto-char 49) + (delete-char 0) + (goto-char 49) + (delete-char 3) + (goto-char 48) + (insert "...........") + (goto-char 37) + (delete-char 23) + (goto-char 36) + (delete-char 0) + (goto-char 35) + (insert "....") + (goto-char 35) + (insert "..") + (goto-char 39) + (delete-char 4) + (goto-char 39) + (delete-char 0) + (goto-char 36) + (delete-char 3) + (goto-char 36) + (delete-char 0) + (goto-char 36) + (delete-char 0) + (goto-char 36) + (delete-char 0) + (goto-char 36) + (insert ".....") + (goto-char 38) + (delete-char 1) + (goto-char 35) + (delete-char 3) + (should + (equal + (test-overlay-regions) + '((1 . 37) + (24 . 44) + (25 . 37)))))) + +(ert-deftest overlay-autogenerated-test-39 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 15 49 nil t t) + (make-overlay 27 20 nil t nil) + (make-overlay 55 50 nil t nil) + (make-overlay 17 5 nil t t) + (make-overlay 26 56 nil nil t) + (make-overlay 42 11 nil t t) + (make-overlay 24 35 nil nil t) + (make-overlay 47 45 nil t t) + (make-overlay 37 12 nil nil t) + (make-overlay 17 25 nil t nil) + (make-overlay 32 53 nil nil nil) + (make-overlay 20 34 nil nil t) + (make-overlay 56 58 nil nil t) + (make-overlay 42 31 nil nil t) + (make-overlay 22 55 nil t t) + (make-overlay 55 11 nil t nil) + (goto-char 16) + (insert ".............") + (goto-char 30) + (insert ".") + (goto-char 12) + (delete-char 56) + (goto-char 9) + (insert ".............") + (goto-char 6) + (insert "....") + (goto-char 19) + (delete-char 19) + (goto-char 19) + (insert "...............") + (goto-char 13) + (delete-char 21) + (goto-char 7) + (delete-char 0) + (goto-char 14) + (widen) + (narrow-to-region 5 6) + (goto-char 5) + (delete-char 0) + (goto-char 6) + (insert "......") + (goto-char 10) + (delete-char 0) + (goto-char 7) + (widen) + (narrow-to-region 2 6) + (goto-char 2) + (insert "..........") + (goto-char 2) + (delete-char 9) + (goto-char 7) + (insert "...") + (goto-char 9) + (insert "...") + (goto-char 10) + (insert "......") + (goto-char 4) + (delete-char 14) + (goto-char 4) + (insert ".") + (goto-char 5) + (insert "..............") + (goto-char 13) + (insert "......") + (goto-char 10) + (insert "......") + (goto-char 20) + (insert "............") + (goto-char 16) + (widen) + (narrow-to-region 3 32) + (goto-char 18) + (insert "..") + (goto-char 6) + (insert "......") + (goto-char 38) + (delete-char 0) + (goto-char 31) + (insert "............") + (goto-char 28) + (insert "") + (goto-char 9) + (delete-char 23) + (should + (equal + (test-overlay-regions) + 'nil)))) + +(ert-deftest overlay-autogenerated-test-40 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 52 3 nil t nil) + (make-overlay 35 41 nil t t) + (make-overlay 4 2 nil t nil) + (make-overlay 51 48 nil nil t) + (make-overlay 44 57 nil t t) + (make-overlay 13 32 nil nil nil) + (make-overlay 46 29 nil t nil) + (make-overlay 28 13 nil t nil) + (make-overlay 10 65 nil t t) + (make-overlay 41 48 nil nil t) + (make-overlay 36 44 nil nil t) + (make-overlay 29 61 nil t nil) + (make-overlay 25 24 nil nil t) + (make-overlay 22 45 nil nil t) + (make-overlay 37 55 nil nil t) + (make-overlay 36 39 nil nil nil) + (goto-char 16) + (delete-char 48) + (goto-char 17) + (delete-char 0) + (goto-char 7) + (insert "..............") + (goto-char 30) + (insert "........") + (goto-char 11) + (insert "..........") + (goto-char 5) + (delete-char 14) + (goto-char 19) + (insert ".") + (goto-char 27) + (insert "..") + (goto-char 35) + (delete-char 1) + (goto-char 29) + (delete-char 0) + (goto-char 33) + (delete-char 2) + (goto-char 33) + (insert "..") + (goto-char 28) + (insert ".........") + (goto-char 30) + (delete-char 4) + (goto-char 40) + (delete-char 1) + (goto-char 15) + (widen) + (narrow-to-region 40 8) + (goto-char 10) + (delete-char 13) + (goto-char 11) + (delete-char 5) + (goto-char 15) + (insert "........") + (goto-char 26) + (delete-char 4) + (goto-char 11) + (delete-char 1) + (goto-char 14) + (insert "............") + (goto-char 33) + (insert ".") + (goto-char 10) + (insert "...") + (goto-char 30) + (widen) + (narrow-to-region 28 9) + (goto-char 27) + (delete-char 0) + (goto-char 27) + (delete-char 1) + (goto-char 26) + (insert "..") + (goto-char 27) + (insert "..") + (goto-char 20) + (delete-char 5) + (goto-char 12) + (widen) + (narrow-to-region 40 30) + (goto-char 37) + (delete-char 3) + (should + (equal + (test-overlay-regions) + '((13 . 37) + (14 . 37) + (14 . 37) + (14 . 37) + (14 . 37) + (14 . 37) + (14 . 37) + (37 . 37) + (37 . 37)))))) + +(ert-deftest overlay-autogenerated-test-41 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 28 48 nil nil t) + (make-overlay 30 11 nil nil t) + (make-overlay 7 12 nil t nil) + (make-overlay 65 35 nil t nil) + (make-overlay 22 61 nil t nil) + (make-overlay 37 42 nil nil nil) + (make-overlay 33 38 nil nil t) + (make-overlay 48 45 nil t t) + (make-overlay 45 62 nil t nil) + (make-overlay 63 7 nil nil t) + (make-overlay 23 42 nil t nil) + (make-overlay 21 4 nil t nil) + (make-overlay 64 41 nil t nil) + (make-overlay 20 33 nil t t) + (make-overlay 41 26 nil t nil) + (make-overlay 43 31 nil t t) + (goto-char 55) + (delete-char 3) + (goto-char 12) + (insert "..") + (goto-char 62) + (insert "") + (goto-char 24) + (delete-char 2) + (goto-char 41) + (insert "............") + (goto-char 2) + (insert ".") + (goto-char 55) + (insert "........") + (goto-char 67) + (delete-char 6) + (goto-char 58) + (delete-char 10) + (goto-char 29) + (insert "") + (goto-char 6) + (widen) + (narrow-to-region 44 45) + (goto-char 44) + (delete-char 1) + (goto-char 44) + (widen) + (narrow-to-region 24 37) + (goto-char 30) + (delete-char 7) + (goto-char 27) + (insert "......") + (goto-char 35) + (delete-char 0) + (goto-char 32) + (insert "...............") + (goto-char 37) + (delete-char 9) + (goto-char 40) + (insert "..........") + (goto-char 35) + (insert "......") + (goto-char 25) + (delete-char 7) + (goto-char 40) + (delete-char 4) + (goto-char 25) + (delete-char 14) + (goto-char 28) + (insert "") + (goto-char 28) + (widen) + (narrow-to-region 17 43) + (goto-char 20) + (insert "..........") + (goto-char 22) + (delete-char 2) + (goto-char 48) + (insert "............") + (goto-char 47) + (insert ".........") + (goto-char 69) + (widen) + (narrow-to-region 52 25) + (goto-char 26) + (insert "......") + (goto-char 53) + (insert "..") + (should + (equal + (test-overlay-regions) + '((5 . 38) + (8 . 97) + (12 . 47) + (37 . 47) + (39 . 52) + (39 . 87) + (39 . 95) + (46 . 90) + (47 . 49) + (47 . 90) + (47 . 99) + (48 . 87)))))) + +(ert-deftest overlay-autogenerated-test-42 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 20 23 nil nil nil) + (make-overlay 45 51 nil t nil) + (make-overlay 34 58 nil t nil) + (make-overlay 27 11 nil nil nil) + (make-overlay 14 8 nil t t) + (make-overlay 64 43 nil t nil) + (make-overlay 61 56 nil nil t) + (make-overlay 28 14 nil t nil) + (make-overlay 21 46 nil t t) + (make-overlay 30 34 nil t t) + (make-overlay 47 40 nil nil nil) + (make-overlay 5 44 nil t t) + (make-overlay 11 45 nil nil nil) + (make-overlay 65 8 nil nil t) + (make-overlay 47 54 nil t t) + (make-overlay 37 57 nil t nil) + (goto-char 11) + (insert "....") + (goto-char 65) + (delete-char 0) + (goto-char 56) + (delete-char 4) + (goto-char 11) + (delete-char 2) + (goto-char 23) + (insert ".............") + (goto-char 2) + (insert "............") + (goto-char 84) + (delete-char 1) + (goto-char 10) + (insert "..............") + (goto-char 19) + (insert "............") + (goto-char 69) + (delete-char 6) + (goto-char 15) + (insert "........") + (goto-char 104) + (insert "") + (goto-char 94) + (delete-char 11) + (goto-char 66) + (insert ".....") + (goto-char 67) + (insert "") + (goto-char 53) + (delete-char 22) + (goto-char 42) + (insert ".") + (goto-char 38) + (delete-char 13) + (goto-char 27) + (insert "......") + (goto-char 16) + (insert "............") + (goto-char 71) + (widen) + (narrow-to-region 59 15) + (goto-char 46) + (insert "..") + (goto-char 20) + (widen) + (narrow-to-region 95 93) + (goto-char 94) + (insert ".............") + (goto-char 103) + (widen) + (narrow-to-region 97 7) + (goto-char 93) + (insert "....") + (goto-char 85) + (insert "...........") + (goto-char 69) + (delete-char 24) + (goto-char 87) + (insert ".............") + (goto-char 7) + (delete-char 28) + (goto-char 65) + (delete-char 8) + (goto-char 48) + (insert "......") + (should + (equal + (test-overlay-regions) + '((31 . 44) + (33 . 33) + (33 . 41) + (33 . 41) + (33 . 41) + (33 . 41) + (33 . 82) + (40 . 44) + (41 . 41) + (41 . 41) + (41 . 47) + (41 . 48) + (44 . 45) + (44 . 46) + (44 . 63) + (46 . 57)))))) + +(ert-deftest overlay-autogenerated-test-43 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 8 53 nil t nil) + (make-overlay 11 50 nil t nil) + (make-overlay 1 30 nil nil nil) + (make-overlay 54 15 nil t t) + (make-overlay 22 30 nil nil nil) + (make-overlay 1 33 nil nil nil) + (make-overlay 18 15 nil t nil) + (make-overlay 43 39 nil nil t) + (make-overlay 43 17 nil t nil) + (make-overlay 2 29 nil t nil) + (make-overlay 57 42 nil t nil) + (make-overlay 40 1 nil nil nil) + (make-overlay 8 64 nil nil nil) + (make-overlay 64 15 nil nil nil) + (make-overlay 9 11 nil nil t) + (make-overlay 40 21 nil t nil) + (goto-char 5) + (delete-char 37) + (goto-char 25) + (delete-char 2) + (goto-char 17) + (insert "...........") + (goto-char 19) + (widen) + (narrow-to-region 20 20) + (goto-char 20) + (delete-char 0) + (goto-char 20) + (insert "..........") + (goto-char 24) + (delete-char 5) + (goto-char 24) + (insert "...") + (goto-char 28) + (widen) + (narrow-to-region 20 36) + (goto-char 26) + (delete-char 2) + (goto-char 31) + (insert ".............") + (goto-char 22) + (insert ".....") + (goto-char 38) + (delete-char 0) + (goto-char 31) + (delete-char 4) + (goto-char 27) + (insert "...") + (goto-char 23) + (widen) + (narrow-to-region 37 20) + (goto-char 22) + (insert ".............") + (goto-char 33) + (insert "......") + (goto-char 43) + (insert "............") + (goto-char 59) + (insert ".......") + (goto-char 25) + (delete-char 26) + (goto-char 49) + (insert ".........") + (goto-char 50) + (insert ".......") + (goto-char 39) + (widen) + (narrow-to-region 54 86) + (goto-char 64) + (insert "...............") + (goto-char 83) + (insert "............") + (goto-char 70) + (insert "........") + (goto-char 58) + (insert "..............") + (goto-char 83) + (insert "............") + (goto-char 83) + (insert "..........") + (goto-char 69) + (delete-char 75) + (goto-char 75) + (delete-char 3) + (should + (equal + (test-overlay-regions) + '((5 . 75) + (5 . 75) + (5 . 80) + (5 . 80)))))) + +(ert-deftest overlay-autogenerated-test-44 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 8 48 nil nil t) + (make-overlay 52 38 nil nil nil) + (make-overlay 3 63 nil nil nil) + (make-overlay 44 15 nil nil t) + (make-overlay 27 44 nil nil t) + (make-overlay 43 9 nil nil t) + (make-overlay 11 27 nil t nil) + (make-overlay 36 41 nil nil t) + (make-overlay 23 25 nil t t) + (make-overlay 19 60 nil t t) + (make-overlay 11 55 nil t nil) + (make-overlay 59 2 nil t nil) + (make-overlay 32 64 nil t nil) + (make-overlay 15 8 nil nil nil) + (make-overlay 61 15 nil nil nil) + (make-overlay 64 30 nil t t) + (goto-char 42) + (delete-char 20) + (goto-char 44) + (delete-char 1) + (goto-char 43) + (insert "...........") + (goto-char 43) + (delete-char 1) + (goto-char 28) + (delete-char 8) + (goto-char 37) + (delete-char 9) + (goto-char 4) + (delete-char 30) + (goto-char 6) + (delete-char 0) + (goto-char 7) + (delete-char 0) + (goto-char 2) + (delete-char 2) + (goto-char 5) + (delete-char 0) + (goto-char 5) + (delete-char 0) + (goto-char 2) + (insert ".....") + (goto-char 10) + (insert "...........") + (goto-char 21) + (insert "...") + (goto-char 10) + (delete-char 13) + (goto-char 9) + (insert "..........") + (goto-char 16) + (delete-char 1) + (goto-char 16) + (delete-char 4) + (goto-char 16) + (delete-char 0) + (goto-char 14) + (delete-char 1) + (goto-char 3) + (widen) + (narrow-to-region 2 9) + (goto-char 2) + (insert "") + (goto-char 2) + (insert ".............") + (goto-char 17) + (insert "....") + (goto-char 12) + (insert "........") + (goto-char 8) + (widen) + (narrow-to-region 32 23) + (goto-char 29) + (insert ".....") + (goto-char 35) + (delete-char 2) + (goto-char 27) + (delete-char 7) + (goto-char 23) + (widen) + (narrow-to-region 4 14) + (goto-char 8) + (insert "...............") + (should + (equal + (test-overlay-regions) + '((2 . 43) + (2 . 43) + (2 . 43) + (2 . 43) + (2 . 43) + (2 . 44)))))) + +(ert-deftest overlay-autogenerated-test-45 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 15 48 nil nil nil) + (make-overlay 1 47 nil t nil) + (make-overlay 43 4 nil t t) + (make-overlay 9 45 nil t t) + (make-overlay 1 25 nil t t) + (make-overlay 5 46 nil t t) + (make-overlay 7 14 nil t nil) + (make-overlay 1 53 nil nil t) + (make-overlay 13 41 nil t nil) + (make-overlay 5 31 nil t t) + (make-overlay 26 10 nil nil nil) + (make-overlay 56 37 nil nil nil) + (make-overlay 23 15 nil t nil) + (make-overlay 62 30 nil t t) + (make-overlay 2 35 nil t t) + (make-overlay 46 41 nil nil nil) + (goto-char 65) + (delete-char 0) + (goto-char 55) + (insert "...........") + (goto-char 22) + (insert "") + (goto-char 73) + (delete-char 3) + (goto-char 43) + (widen) + (narrow-to-region 54 63) + (goto-char 56) + (insert "......") + (goto-char 61) + (delete-char 3) + (goto-char 65) + (insert "......") + (goto-char 66) + (insert ".....") + (goto-char 62) + (insert ".") + (goto-char 74) + (insert ".........") + (goto-char 76) + (delete-char 4) + (goto-char 56) + (widen) + (narrow-to-region 2 46) + (goto-char 43) + (insert "...........") + (goto-char 20) + (delete-char 4) + (goto-char 38) + (delete-char 7) + (goto-char 25) + (delete-char 21) + (goto-char 12) + (insert ".........") + (goto-char 19) + (widen) + (narrow-to-region 72 61) + (goto-char 63) + (insert "") + (goto-char 65) + (delete-char 4) + (goto-char 61) + (delete-char 5) + (goto-char 63) + (delete-char 0) + (goto-char 63) + (delete-char 0) + (goto-char 62) + (delete-char 0) + (goto-char 61) + (insert "............") + (goto-char 72) + (insert "..............") + (goto-char 62) + (delete-char 7) + (goto-char 71) + (delete-char 5) + (goto-char 75) + (widen) + (narrow-to-region 29 8) + (goto-char 17) + (delete-char 2) + (goto-char 27) + (insert "........") + (should + (equal + (test-overlay-regions) + '((1 . 36) + (1 . 41) + (1 . 47) + (2 . 40) + (4 . 40) + (5 . 40) + (5 . 40) + (7 . 21) + (9 . 40) + (10 . 37) + (20 . 40) + (22 . 27) + (22 . 42)))))) + +(ert-deftest overlay-autogenerated-test-46 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 2 43 nil nil t) + (make-overlay 44 40 nil nil t) + (make-overlay 49 14 nil nil t) + (make-overlay 6 55 nil nil nil) + (make-overlay 13 52 nil t t) + (make-overlay 40 54 nil t nil) + (make-overlay 51 41 nil nil t) + (make-overlay 7 28 nil nil t) + (make-overlay 10 47 nil nil t) + (make-overlay 63 21 nil t nil) + (make-overlay 4 55 nil nil nil) + (make-overlay 52 58 nil t nil) + (make-overlay 62 11 nil t t) + (make-overlay 22 49 nil t nil) + (make-overlay 23 65 nil nil nil) + (make-overlay 50 33 nil nil t) + (goto-char 22) + (insert "..............") + (goto-char 12) + (insert "....") + (goto-char 25) + (delete-char 16) + (goto-char 14) + (delete-char 53) + (goto-char 2) + (insert "............") + (goto-char 20) + (delete-char 5) + (goto-char 11) + (delete-char 7) + (goto-char 9) + (widen) + (narrow-to-region 11 7) + (goto-char 8) + (insert "...............") + (goto-char 12) + (delete-char 4) + (goto-char 21) + (insert "...") + (goto-char 20) + (delete-char 5) + (goto-char 7) + (delete-char 3) + (goto-char 16) + (delete-char 0) + (goto-char 12) + (delete-char 1) + (goto-char 15) + (delete-char 0) + (goto-char 7) + (insert "..............") + (goto-char 17) + (insert "...........") + (goto-char 15) + (insert "............") + (goto-char 20) + (delete-char 5) + (goto-char 7) + (insert "....") + (goto-char 37) + (delete-char 7) + (goto-char 8) + (insert "..........") + (goto-char 47) + (insert ".............") + (goto-char 65) + (insert ".......") + (goto-char 39) + (delete-char 26) + (goto-char 14) + (delete-char 2) + (goto-char 27) + (insert ".............") + (goto-char 17) + (widen) + (narrow-to-region 54 32) + (goto-char 40) + (widen) + (narrow-to-region 10 3) + (goto-char 7) + (insert "........") + (goto-char 13) + (insert "..............") + (should + (equal + (test-overlay-regions) + '((2 . 85)))))) + +(ert-deftest overlay-autogenerated-test-47 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 58 62 nil t nil) + (make-overlay 14 38 nil nil nil) + (make-overlay 63 44 nil t t) + (make-overlay 41 41 nil nil t) + (make-overlay 19 39 nil nil nil) + (make-overlay 10 49 nil t t) + (make-overlay 56 38 nil t t) + (make-overlay 23 38 nil nil t) + (make-overlay 1 64 nil nil t) + (make-overlay 21 3 nil t nil) + (make-overlay 1 1 nil nil t) + (make-overlay 27 61 nil nil nil) + (make-overlay 29 59 nil nil nil) + (make-overlay 37 30 nil t nil) + (make-overlay 47 21 nil nil t) + (make-overlay 34 26 nil t nil) + (goto-char 6) + (delete-char 44) + (goto-char 8) + (delete-char 0) + (goto-char 8) + (insert "....") + (goto-char 17) + (delete-char 2) + (goto-char 12) + (insert "...") + (goto-char 20) + (insert "") + (goto-char 2) + (delete-char 20) + (goto-char 1) + (insert ".........") + (goto-char 7) + (insert ".............") + (goto-char 27) + (delete-char 0) + (goto-char 15) + (insert "..........") + (goto-char 36) + (insert "..............") + (goto-char 26) + (insert "..............") + (goto-char 63) + (insert "...........") + (goto-char 9) + (insert "............") + (goto-char 71) + (delete-char 17) + (goto-char 36) + (insert "....") + (goto-char 45) + (delete-char 31) + (goto-char 28) + (delete-char 8) + (goto-char 10) + (delete-char 16) + (goto-char 14) + (delete-char 4) + (goto-char 16) + (delete-char 0) + (goto-char 15) + (insert "") + (goto-char 14) + (delete-char 1) + (goto-char 10) + (delete-char 2) + (goto-char 6) + (delete-char 0) + (goto-char 1) + (insert ".........") + (goto-char 23) + (insert "......") + (goto-char 25) + (insert "..........") + (goto-char 25) + (widen) + (narrow-to-region 10 30) + (goto-char 21) + (delete-char 1) + (goto-char 17) + (insert "..........") + (should + (equal + (test-overlay-regions) + '((1 . 48) + (1 . 48) + (32 . 32) + (32 . 32) + (32 . 32) + (32 . 32) + (32 . 32) + (32 . 32) + (32 . 32) + (32 . 32) + (32 . 48) + (32 . 48) + (32 . 48)))))) + +(ert-deftest overlay-autogenerated-test-48 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 1 11 nil nil nil) + (make-overlay 35 29 nil t t) + (make-overlay 24 46 nil nil t) + (make-overlay 15 43 nil nil t) + (make-overlay 51 49 nil t t) + (make-overlay 25 43 nil t nil) + (make-overlay 23 59 nil nil nil) + (make-overlay 10 4 nil t nil) + (make-overlay 40 45 nil nil nil) + (make-overlay 42 43 nil nil t) + (make-overlay 20 38 nil t nil) + (make-overlay 17 49 nil nil nil) + (make-overlay 9 25 nil nil t) + (make-overlay 13 19 nil nil nil) + (make-overlay 44 31 nil t nil) + (make-overlay 12 65 nil nil t) + (goto-char 59) + (widen) + (narrow-to-region 28 14) + (goto-char 26) + (insert "...") + (goto-char 30) + (delete-char 1) + (goto-char 23) + (insert "...") + (goto-char 27) + (widen) + (narrow-to-region 45 67) + (goto-char 50) + (insert "...............") + (goto-char 59) + (insert "..............") + (goto-char 55) + (insert ".............") + (goto-char 106) + (delete-char 0) + (goto-char 97) + (delete-char 10) + (goto-char 67) + (delete-char 16) + (goto-char 76) + (insert "..............") + (goto-char 71) + (insert ".............") + (goto-char 110) + (delete-char 0) + (goto-char 56) + (delete-char 38) + (goto-char 61) + (delete-char 10) + (goto-char 56) + (delete-char 5) + (goto-char 49) + (insert ".......") + (goto-char 62) + (insert "...") + (goto-char 54) + (insert "..........") + (goto-char 47) + (delete-char 10) + (goto-char 47) + (delete-char 20) + (goto-char 46) + (insert ".............") + (goto-char 56) + (insert "...........") + (goto-char 70) + (delete-char 1) + (goto-char 62) + (widen) + (narrow-to-region 50 64) + (goto-char 60) + (insert "..") + (goto-char 55) + (delete-char 6) + (goto-char 60) + (insert ".............") + (goto-char 61) + (delete-char 9) + (goto-char 64) + (delete-char 0) + (goto-char 53) + (widen) + (narrow-to-region 15 62) + (should + (equal + (test-overlay-regions) + '((9 . 28) + (12 . 73) + (13 . 19) + (15 . 70) + (17 . 70) + (20 . 43) + (23 . 70) + (27 . 70) + (28 . 70) + (34 . 40) + (36 . 70) + (45 . 70)))))) + +(ert-deftest overlay-autogenerated-test-49 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 24 10 nil nil t) + (make-overlay 53 23 nil t nil) + (make-overlay 53 9 nil nil t) + (make-overlay 65 64 nil t t) + (make-overlay 48 2 nil nil t) + (make-overlay 12 58 nil nil t) + (make-overlay 64 64 nil nil nil) + (make-overlay 26 13 nil t t) + (make-overlay 46 26 nil nil t) + (make-overlay 28 59 nil t t) + (make-overlay 33 52 nil nil nil) + (make-overlay 39 8 nil t t) + (make-overlay 9 59 nil t t) + (make-overlay 50 45 nil nil t) + (make-overlay 41 53 nil nil t) + (make-overlay 51 51 nil t nil) + (goto-char 61) + (insert "..............") + (goto-char 19) + (widen) + (narrow-to-region 10 65) + (goto-char 65) + (delete-char 0) + (goto-char 11) + (insert "...............") + (goto-char 77) + (delete-char 0) + (goto-char 51) + (insert "...") + (goto-char 75) + (insert ".....") + (goto-char 77) + (delete-char 11) + (goto-char 45) + (delete-char 0) + (goto-char 24) + (widen) + (narrow-to-region 33 52) + (goto-char 46) + (insert "..............") + (goto-char 46) + (insert "..........") + (goto-char 39) + (widen) + (narrow-to-region 46 77) + (goto-char 77) + (insert "..............") + (goto-char 54) + (insert ".......") + (goto-char 87) + (insert ".") + (goto-char 70) + (delete-char 16) + (goto-char 79) + (delete-char 0) + (goto-char 73) + (widen) + (narrow-to-region 74 100) + (goto-char 91) + (insert ".............") + (goto-char 80) + (delete-char 11) + (goto-char 82) + (insert "......") + (goto-char 108) + (delete-char 0) + (goto-char 104) + (insert ".....") + (goto-char 100) + (delete-char 1) + (goto-char 90) + (insert ".............") + (goto-char 99) + (insert ".............") + (goto-char 124) + (insert "..............") + (goto-char 114) + (insert "....") + (goto-char 134) + (delete-char 0) + (goto-char 89) + (delete-char 65) + (goto-char 75) + (delete-char 16) + (should + (equal + (test-overlay-regions) + '((2 . 75) + (8 . 75) + (9 . 76) + (9 . 82) + (27 . 82) + (38 . 76) + (41 . 75) + (43 . 82) + (70 . 75)))))) + +(ert-deftest overlay-autogenerated-test-50 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 29 53 nil t t) + (make-overlay 65 64 nil nil nil) + (make-overlay 3 31 nil nil t) + (make-overlay 45 59 nil t nil) + (make-overlay 60 37 nil t t) + (make-overlay 7 5 nil t t) + (make-overlay 37 24 nil nil nil) + (make-overlay 45 20 nil nil nil) + (make-overlay 33 42 nil nil t) + (make-overlay 47 57 nil t nil) + (make-overlay 14 49 nil t t) + (make-overlay 14 30 nil t nil) + (make-overlay 21 40 nil t t) + (make-overlay 5 45 nil t t) + (make-overlay 59 40 nil t t) + (make-overlay 37 52 nil nil nil) + (goto-char 48) + (insert "") + (goto-char 7) + (insert ".........") + (goto-char 31) + (insert "...........") + (goto-char 41) + (delete-char 7) + (goto-char 21) + (delete-char 11) + (goto-char 41) + (widen) + (narrow-to-region 51 53) + (goto-char 52) + (insert ".....") + (goto-char 55) + (widen) + (narrow-to-region 18 24) + (goto-char 23) + (widen) + (narrow-to-region 39 38) + (goto-char 38) + (insert ".............") + (goto-char 41) + (insert "......") + (goto-char 38) + (insert "..............") + (goto-char 52) + (insert "...............") + (goto-char 78) + (delete-char 5) + (goto-char 50) + (insert "..........") + (goto-char 50) + (delete-char 3) + (goto-char 85) + (widen) + (narrow-to-region 86 1) + (goto-char 5) + (insert "....") + (goto-char 69) + (insert "...........") + (goto-char 94) + (insert "......") + (goto-char 98) + (delete-char 7) + (goto-char 46) + (insert "...............") + (goto-char 79) + (insert "............") + (goto-char 89) + (insert "") + (goto-char 14) + (delete-char 63) + (goto-char 20) + (insert ".........") + (goto-char 34) + (insert "...") + (goto-char 53) + (delete-char 14) + (goto-char 6) + (widen) + (narrow-to-region 6 52) + (goto-char 42) + (insert "...........") + (goto-char 40) + (insert ".......") + (goto-char 46) + (widen) + (narrow-to-region 1 68) + (should + (equal + (test-overlay-regions) + '((3 . 14) + (9 . 14) + (9 . 91) + (14 . 14) + (14 . 83) + (14 . 86) + (14 . 88) + (14 . 91) + (14 . 95) + (14 . 104)))))) + +(ert-deftest overlay-autogenerated-test-51 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 14 5 nil t nil) + (make-overlay 62 34 nil nil t) + (make-overlay 7 62 nil nil t) + (make-overlay 23 12 nil t t) + (make-overlay 16 4 nil nil nil) + (make-overlay 24 15 nil nil nil) + (make-overlay 6 6 nil t t) + (make-overlay 25 64 nil t t) + (make-overlay 23 6 nil t t) + (make-overlay 55 64 nil nil nil) + (make-overlay 8 62 nil nil t) + (make-overlay 65 65 nil nil nil) + (make-overlay 57 51 nil t t) + (make-overlay 35 8 nil t nil) + (make-overlay 55 13 nil nil t) + (make-overlay 60 62 nil nil t) + (goto-char 12) + (insert "..") + (goto-char 66) + (insert "............") + (goto-char 32) + (insert "..") + (goto-char 27) + (insert ".........") + (goto-char 8) + (insert ".............") + (goto-char 79) + (insert ".") + (goto-char 47) + (insert "....") + (goto-char 49) + (insert "...") + (goto-char 81) + (insert "....") + (goto-char 112) + (delete-char 0) + (goto-char 97) + (insert ".....") + (goto-char 109) + (delete-char 5) + (goto-char 20) + (insert ".....") + (goto-char 59) + (delete-char 33) + (goto-char 87) + (insert ".............") + (goto-char 98) + (insert "....") + (goto-char 22) + (delete-char 36) + (goto-char 45) + (insert "..............") + (goto-char 42) + (delete-char 29) + (goto-char 51) + (widen) + (narrow-to-region 39 41) + (goto-char 39) + (delete-char 2) + (goto-char 39) + (insert ".............") + (goto-char 51) + (insert "......") + (goto-char 52) + (insert "...............") + (goto-char 56) + (widen) + (narrow-to-region 59 20) + (goto-char 56) + (insert "............") + (goto-char 57) + (insert ".") + (goto-char 37) + (delete-char 12) + (goto-char 39) + (delete-char 11) + (goto-char 38) + (delete-char 8) + (goto-char 36) + (widen) + (narrow-to-region 65 26) + (goto-char 40) + (widen) + (narrow-to-region 27 55) + (should + (equal + (test-overlay-regions) + '((7 . 55) + (8 . 55) + (22 . 29) + (23 . 55) + (23 . 56) + (24 . 31) + (29 . 56) + (37 . 55)))))) + +(ert-deftest overlay-autogenerated-test-52 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 58 32 nil nil nil) + (make-overlay 44 54 nil nil t) + (make-overlay 27 50 nil nil nil) + (make-overlay 55 35 nil nil t) + (make-overlay 40 46 nil nil t) + (make-overlay 56 63 nil t nil) + (make-overlay 29 48 nil nil nil) + (make-overlay 45 24 nil t nil) + (make-overlay 60 25 nil t nil) + (make-overlay 55 41 nil t nil) + (make-overlay 55 1 nil nil t) + (make-overlay 30 45 nil t t) + (make-overlay 26 19 nil nil t) + (make-overlay 61 5 nil nil nil) + (make-overlay 33 5 nil nil nil) + (make-overlay 42 18 nil t nil) + (goto-char 55) + (insert ".") + (goto-char 49) + (delete-char 12) + (goto-char 41) + (insert "..........") + (goto-char 27) + (insert ".....") + (goto-char 58) + (insert "...........") + (goto-char 24) + (delete-char 23) + (goto-char 47) + (delete-char 9) + (goto-char 4) + (insert "...") + (goto-char 10) + (delete-char 32) + (goto-char 4) + (insert "..............") + (goto-char 29) + (insert "....") + (goto-char 28) + (delete-char 2) + (goto-char 34) + (insert "...........") + (goto-char 9) + (insert "......") + (goto-char 5) + (insert "") + (goto-char 45) + (delete-char 1) + (goto-char 18) + (insert ".........") + (goto-char 36) + (delete-char 5) + (goto-char 15) + (delete-char 27) + (goto-char 15) + (delete-char 10) + (goto-char 16) + (delete-char 2) + (goto-char 16) + (widen) + (narrow-to-region 10 2) + (goto-char 9) + (delete-char 1) + (goto-char 3) + (delete-char 2) + (goto-char 2) + (widen) + (narrow-to-region 9 10) + (goto-char 9) + (insert "...........") + (goto-char 19) + (delete-char 0) + (goto-char 14) + (delete-char 3) + (goto-char 11) + (delete-char 2) + (goto-char 9) + (delete-char 6) + (goto-char 9) + (delete-char 0) + (goto-char 10) + (insert "....") + (should + (equal + (test-overlay-regions) + '((1 . 17)))))) + +(ert-deftest overlay-autogenerated-test-53 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 10 30 nil nil nil) + (make-overlay 11 57 nil t nil) + (make-overlay 59 56 nil nil t) + (make-overlay 20 37 nil nil t) + (make-overlay 41 29 nil nil nil) + (make-overlay 31 10 nil nil t) + (make-overlay 6 36 nil nil nil) + (make-overlay 12 54 nil nil nil) + (make-overlay 25 26 nil t t) + (make-overlay 21 19 nil nil t) + (make-overlay 1 21 nil nil t) + (make-overlay 48 51 nil nil nil) + (make-overlay 54 55 nil t nil) + (make-overlay 64 48 nil t t) + (make-overlay 56 25 nil nil t) + (make-overlay 12 60 nil t nil) + (goto-char 41) + (delete-char 1) + (goto-char 63) + (insert "") + (goto-char 14) + (delete-char 5) + (goto-char 11) + (insert "..............") + (goto-char 41) + (widen) + (narrow-to-region 12 1) + (goto-char 1) + (delete-char 3) + (goto-char 9) + (delete-char 0) + (goto-char 5) + (insert "..............") + (goto-char 1) + (insert "..........") + (goto-char 29) + (insert "...............") + (goto-char 4) + (insert "..") + (goto-char 31) + (delete-char 15) + (goto-char 31) + (insert "") + (goto-char 27) + (insert "......") + (goto-char 6) + (insert "...") + (goto-char 23) + (widen) + (narrow-to-region 23 47) + (goto-char 37) + (delete-char 2) + (goto-char 35) + (delete-char 5) + (goto-char 38) + (delete-char 2) + (goto-char 30) + (insert ".......") + (goto-char 45) + (widen) + (narrow-to-region 13 2) + (goto-char 9) + (delete-char 1) + (goto-char 3) + (insert ".....") + (goto-char 2) + (insert "...............") + (goto-char 16) + (delete-char 5) + (goto-char 20) + (insert ".....") + (goto-char 26) + (delete-char 0) + (goto-char 26) + (widen) + (narrow-to-region 76 98) + (goto-char 88) + (insert ".........") + (goto-char 92) + (insert ".") + (goto-char 108) + (delete-char 0) + (goto-char 103) + (delete-char 3) + (should + (equal + (test-overlay-regions) + '((1 . 79) + (37 . 103) + (61 . 88) + (61 . 99) + (74 . 121) + (75 . 118) + (75 . 124) + (77 . 79) + (78 . 103) + (83 . 84) + (83 . 120) + (87 . 106)))))) + +(ert-deftest overlay-autogenerated-test-54 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 58 36 nil t t) + (make-overlay 55 49 nil nil t) + (make-overlay 12 25 nil nil t) + (make-overlay 16 37 nil t t) + (make-overlay 42 25 nil t t) + (make-overlay 8 41 nil t t) + (make-overlay 13 27 nil nil t) + (make-overlay 52 22 nil t nil) + (make-overlay 36 17 nil t nil) + (make-overlay 1 52 nil t nil) + (make-overlay 55 5 nil nil t) + (make-overlay 50 50 nil t nil) + (make-overlay 32 15 nil t nil) + (make-overlay 39 26 nil t nil) + (make-overlay 26 4 nil nil nil) + (make-overlay 38 47 nil t t) + (goto-char 23) + (insert ".") + (goto-char 57) + (delete-char 6) + (goto-char 54) + (insert "..............") + (goto-char 46) + (insert "...............") + (goto-char 29) + (insert ".......") + (goto-char 58) + (delete-char 21) + (goto-char 45) + (delete-char 4) + (goto-char 50) + (delete-char 4) + (goto-char 20) + (insert ".........") + (goto-char 16) + (insert "......") + (goto-char 17) + (insert ".....") + (goto-char 63) + (insert "........") + (goto-char 83) + (insert "....") + (goto-char 73) + (delete-char 8) + (goto-char 69) + (insert "...........") + (goto-char 48) + (widen) + (narrow-to-region 19 31) + (goto-char 22) + (delete-char 3) + (goto-char 23) + (delete-char 5) + (goto-char 20) + (insert "............") + (goto-char 23) + (delete-char 11) + (goto-char 19) + (insert "..........") + (goto-char 23) + (insert "........") + (goto-char 38) + (delete-char 1) + (goto-char 33) + (delete-char 5) + (goto-char 27) + (insert "..........") + (goto-char 35) + (delete-char 8) + (goto-char 35) + (insert ".") + (goto-char 20) + (insert "......") + (goto-char 22) + (delete-char 22) + (goto-char 23) + (delete-char 0) + (goto-char 22) + (widen) + (narrow-to-region 1 41) + (goto-char 13) + (insert ".......") + (should + (equal + (test-overlay-regions) + '((1 . 83) + (4 . 46) + (5 . 97) + (8 . 83) + (12 . 45) + (13 . 47) + (22 . 59) + (30 . 82) + (30 . 83) + (41 . 83) + (45 . 83) + (46 . 83)))))) + +(ert-deftest overlay-autogenerated-test-55 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 58 20 nil nil nil) + (make-overlay 60 33 nil t nil) + (make-overlay 6 27 nil nil nil) + (make-overlay 53 31 nil nil t) + (make-overlay 30 55 nil t t) + (make-overlay 4 64 nil t t) + (make-overlay 51 31 nil nil t) + (make-overlay 4 65 nil t t) + (make-overlay 57 62 nil t t) + (make-overlay 28 7 nil nil t) + (make-overlay 61 48 nil t nil) + (make-overlay 23 54 nil nil t) + (make-overlay 47 49 nil nil nil) + (make-overlay 12 52 nil t nil) + (make-overlay 39 57 nil t t) + (make-overlay 28 61 nil nil t) + (goto-char 8) + (insert "..............") + (goto-char 63) + (delete-char 3) + (goto-char 67) + (delete-char 6) + (goto-char 3) + (widen) + (narrow-to-region 10 67) + (goto-char 43) + (insert ".............") + (goto-char 20) + (insert "...............") + (goto-char 18) + (insert "..") + (goto-char 37) + (delete-char 47) + (goto-char 34) + (insert "..............") + (goto-char 31) + (delete-char 2) + (goto-char 16) + (widen) + (narrow-to-region 29 36) + (goto-char 31) + (delete-char 2) + (goto-char 31) + (insert ".......") + (goto-char 40) + (delete-char 0) + (goto-char 32) + (widen) + (narrow-to-region 40 19) + (goto-char 40) + (insert "..") + (goto-char 37) + (delete-char 0) + (goto-char 40) + (delete-char 1) + (goto-char 34) + (delete-char 4) + (goto-char 33) + (insert "..............") + (goto-char 19) + (widen) + (narrow-to-region 78 70) + (goto-char 77) + (insert ".........") + (goto-char 80) + (delete-char 1) + (goto-char 73) + (delete-char 3) + (goto-char 70) + (insert ".........") + (goto-char 75) + (delete-char 10) + (goto-char 74) + (delete-char 3) + (goto-char 73) + (insert "...............") + (goto-char 90) + (insert "......") + (goto-char 94) + (insert "..............") + (goto-char 101) + (insert "........") + (goto-char 111) + (insert "........") + (should + (equal + (test-overlay-regions) + '((4 . 132) + (4 . 133) + (65 . 89) + (65 . 89) + (65 . 89) + (65 . 89) + (65 . 129) + (65 . 130) + (65 . 130) + (65 . 130) + (65 . 130) + (89 . 89) + (89 . 130)))))) + +(ert-deftest overlay-autogenerated-test-56 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 7 14 nil nil t) + (make-overlay 10 10 nil nil t) + (make-overlay 21 23 nil nil t) + (make-overlay 4 44 nil t nil) + (make-overlay 42 16 nil t t) + (make-overlay 1 57 nil t nil) + (make-overlay 15 27 nil nil nil) + (make-overlay 31 1 nil t nil) + (make-overlay 56 45 nil t t) + (make-overlay 46 19 nil t nil) + (make-overlay 15 6 nil nil nil) + (make-overlay 31 26 nil nil t) + (make-overlay 39 41 nil t t) + (make-overlay 52 48 nil nil t) + (make-overlay 44 2 nil t nil) + (make-overlay 60 7 nil nil t) + (goto-char 49) + (delete-char 11) + (goto-char 43) + (delete-char 9) + (goto-char 42) + (delete-char 2) + (goto-char 12) + (insert "...........") + (goto-char 36) + (insert ".........") + (goto-char 1) + (insert "......") + (goto-char 67) + (delete-char 0) + (goto-char 47) + (insert ".............") + (goto-char 57) + (insert "........") + (goto-char 22) + (widen) + (narrow-to-region 75 33) + (goto-char 41) + (delete-char 28) + (goto-char 43) + (delete-char 0) + (goto-char 33) + (delete-char 5) + (goto-char 38) + (insert "..") + (goto-char 42) + (delete-char 0) + (goto-char 38) + (delete-char 0) + (goto-char 38) + (insert "............") + (goto-char 51) + (insert ".......") + (goto-char 48) + (insert "..") + (goto-char 55) + (insert ".") + (goto-char 33) + (delete-char 8) + (goto-char 42) + (insert "..") + (goto-char 45) + (insert "..") + (goto-char 59) + (insert ".............") + (goto-char 53) + (insert ".......") + (goto-char 81) + (delete-char 0) + (goto-char 44) + (delete-char 36) + (goto-char 38) + (delete-char 8) + (goto-char 33) + (insert ".............") + (goto-char 41) + (insert "..............") + (goto-char 65) + (insert "...............") + (goto-char 61) + (insert "...") + (should + (equal + (test-overlay-regions) + '((7 . 86) + (7 . 97) + (8 . 97) + (10 . 97) + (13 . 97) + (32 . 68) + (33 . 60) + (60 . 97) + (60 . 97) + (68 . 86)))))) + +(ert-deftest overlay-autogenerated-test-57 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 52 31 nil t nil) + (make-overlay 39 17 nil t nil) + (make-overlay 19 20 nil t t) + (make-overlay 18 3 nil nil t) + (make-overlay 19 47 nil nil t) + (make-overlay 38 54 nil nil nil) + (make-overlay 30 51 nil nil t) + (make-overlay 29 60 nil t t) + (make-overlay 57 38 nil nil nil) + (make-overlay 13 41 nil t nil) + (make-overlay 9 44 nil t nil) + (make-overlay 30 55 nil t nil) + (make-overlay 33 10 nil nil nil) + (make-overlay 14 35 nil nil t) + (make-overlay 53 50 nil t nil) + (make-overlay 25 28 nil nil t) + (goto-char 40) + (insert "..") + (goto-char 64) + (insert "........") + (goto-char 47) + (insert "............") + (goto-char 65) + (delete-char 0) + (goto-char 86) + (delete-char 1) + (goto-char 59) + (delete-char 11) + (goto-char 64) + (delete-char 8) + (goto-char 53) + (delete-char 0) + (goto-char 28) + (delete-char 8) + (goto-char 6) + (delete-char 33) + (goto-char 14) + (delete-char 2) + (goto-char 2) + (delete-char 10) + (goto-char 3) + (insert "..") + (goto-char 5) + (insert ".........") + (goto-char 1) + (insert "........") + (goto-char 10) + (delete-char 4) + (goto-char 26) + (insert "........") + (goto-char 23) + (insert "....") + (goto-char 1) + (widen) + (narrow-to-region 15 23) + (goto-char 19) + (insert "...") + (goto-char 24) + (delete-char 0) + (goto-char 19) + (insert ".......") + (goto-char 18) + (insert "..") + (goto-char 33) + (insert "...") + (goto-char 32) + (insert "...............") + (goto-char 29) + (delete-char 10) + (goto-char 29) + (insert "..........") + (goto-char 50) + (insert "") + (goto-char 16) + (insert ".........") + (goto-char 52) + (widen) + (narrow-to-region 59 15) + (goto-char 35) + (delete-char 4) + (goto-char 18) + (insert "....") + (should + (equal + (test-overlay-regions) + '((10 . 57) + (10 . 57) + (10 . 57) + (10 . 60) + (10 . 60) + (10 . 61) + (10 . 68) + (57 . 57)))))) + +(ert-deftest overlay-autogenerated-test-58 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 8 16 nil t nil) + (make-overlay 57 27 nil nil nil) + (make-overlay 15 62 nil nil nil) + (make-overlay 32 33 nil nil t) + (make-overlay 47 27 nil nil t) + (make-overlay 41 4 nil nil t) + (make-overlay 57 61 nil t nil) + (make-overlay 18 43 nil nil t) + (make-overlay 64 51 nil t t) + (make-overlay 44 26 nil nil nil) + (make-overlay 9 13 nil nil t) + (make-overlay 41 65 nil nil t) + (make-overlay 23 13 nil t t) + (make-overlay 26 59 nil t t) + (make-overlay 65 65 nil t t) + (make-overlay 15 7 nil nil nil) + (goto-char 41) + (insert "........") + (goto-char 35) + (delete-char 14) + (goto-char 32) + (widen) + (narrow-to-region 23 46) + (goto-char 41) + (delete-char 5) + (goto-char 29) + (delete-char 10) + (goto-char 31) + (insert ".") + (goto-char 29) + (insert "........") + (goto-char 27) + (delete-char 7) + (goto-char 29) + (insert "") + (goto-char 24) + (insert "............") + (goto-char 43) + (delete-char 1) + (goto-char 31) + (delete-char 9) + (goto-char 34) + (widen) + (narrow-to-region 20 14) + (goto-char 20) + (delete-char 0) + (goto-char 17) + (insert "...........") + (goto-char 31) + (delete-char 0) + (goto-char 16) + (insert "...........") + (goto-char 17) + (delete-char 8) + (goto-char 23) + (delete-char 5) + (goto-char 20) + (insert "..........") + (goto-char 33) + (widen) + (narrow-to-region 16 29) + (goto-char 24) + (insert "...............") + (goto-char 44) + (delete-char 0) + (goto-char 30) + (insert "....") + (goto-char 27) + (widen) + (narrow-to-region 4 22) + (goto-char 10) + (insert "..............") + (goto-char 36) + (insert "..") + (goto-char 10) + (delete-char 21) + (goto-char 14) + (delete-char 1) + (goto-char 14) + (insert "...........") + (goto-char 12) + (insert "........") + (goto-char 32) + (insert "........") + (should + (equal + (test-overlay-regions) + '((4 . 92) + (7 . 10) + (8 . 10) + (9 . 10) + (10 . 82) + (10 . 104)))))) + +(ert-deftest overlay-autogenerated-test-59 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 46 30 nil t t) + (make-overlay 3 26 nil nil nil) + (make-overlay 36 28 nil t t) + (make-overlay 49 49 nil t t) + (make-overlay 27 61 nil t nil) + (make-overlay 14 16 nil nil nil) + (make-overlay 50 61 nil t nil) + (make-overlay 59 63 nil nil nil) + (make-overlay 36 34 nil t nil) + (make-overlay 35 29 nil nil nil) + (make-overlay 5 65 nil nil nil) + (make-overlay 20 61 nil nil t) + (make-overlay 10 42 nil nil nil) + (make-overlay 47 49 nil nil t) + (make-overlay 12 4 nil nil nil) + (make-overlay 32 24 nil t t) + (goto-char 11) + (insert ".") + (goto-char 32) + (delete-char 2) + (goto-char 61) + (insert ".........") + (goto-char 36) + (insert "........") + (goto-char 55) + (widen) + (narrow-to-region 8 55) + (goto-char 21) + (insert "....") + (goto-char 32) + (delete-char 15) + (goto-char 30) + (delete-char 5) + (goto-char 31) + (insert "......") + (goto-char 18) + (insert "..") + (goto-char 14) + (insert ".............") + (goto-char 34) + (insert "............") + (goto-char 51) + (widen) + (narrow-to-region 58 31) + (goto-char 50) + (delete-char 5) + (goto-char 53) + (insert ".........") + (goto-char 56) + (insert "...............") + (goto-char 45) + (delete-char 1) + (goto-char 67) + (insert "............") + (goto-char 84) + (insert "") + (goto-char 39) + (delete-char 27) + (goto-char 39) + (delete-char 21) + (goto-char 32) + (insert "............") + (goto-char 36) + (widen) + (narrow-to-region 7 37) + (goto-char 11) + (insert ".......") + (goto-char 21) + (delete-char 13) + (goto-char 15) + (insert "....") + (goto-char 9) + (insert ".............") + (goto-char 13) + (delete-char 21) + (goto-char 21) + (delete-char 6) + (goto-char 16) + (insert ".......") + (goto-char 22) + (insert "") + (goto-char 27) + (delete-char 0) + (should + (equal + (test-overlay-regions) + '((3 . 42) + (4 . 16) + (5 . 83) + (13 . 51) + (25 . 27)))))) + +(ert-deftest overlay-autogenerated-test-60 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 38 32 nil t nil) + (make-overlay 32 42 nil t nil) + (make-overlay 29 11 nil nil t) + (make-overlay 52 22 nil t t) + (make-overlay 39 59 nil t nil) + (make-overlay 41 30 nil t t) + (make-overlay 29 61 nil nil t) + (make-overlay 11 45 nil nil nil) + (make-overlay 46 17 nil nil t) + (make-overlay 35 51 nil t t) + (make-overlay 22 13 nil nil t) + (make-overlay 52 34 nil nil t) + (make-overlay 59 4 nil nil t) + (make-overlay 8 22 nil nil nil) + (make-overlay 4 49 nil nil nil) + (make-overlay 52 45 nil t t) + (goto-char 48) + (delete-char 16) + (goto-char 37) + (delete-char 8) + (goto-char 14) + (insert "...............") + (goto-char 40) + (delete-char 16) + (goto-char 19) + (insert ".........") + (goto-char 16) + (insert "......") + (goto-char 10) + (insert "........") + (goto-char 11) + (insert "...............") + (goto-char 22) + (insert ".") + (goto-char 62) + (delete-char 16) + (goto-char 14) + (delete-char 11) + (goto-char 47) + (insert "....") + (goto-char 33) + (insert ".............") + (goto-char 49) + (delete-char 13) + (goto-char 28) + (insert "..") + (goto-char 35) + (delete-char 13) + (goto-char 44) + (insert "....") + (goto-char 34) + (delete-char 14) + (goto-char 23) + (insert ".....") + (goto-char 25) + (delete-char 4) + (goto-char 33) + (insert ".....") + (goto-char 27) + (delete-char 3) + (goto-char 16) + (widen) + (narrow-to-region 36 37) + (goto-char 36) + (delete-char 1) + (goto-char 36) + (insert ".......") + (goto-char 37) + (widen) + (narrow-to-region 35 31) + (goto-char 34) + (delete-char 0) + (goto-char 31) + (delete-char 2) + (goto-char 31) + (widen) + (narrow-to-region 24 3) + (goto-char 22) + (delete-char 2) + (goto-char 22) + (insert ".............") + (goto-char 4) + (insert ".") + (should + (equal + (test-overlay-regions) + '((4 . 54) + (4 . 54) + (9 . 46)))))) + +(ert-deftest overlay-autogenerated-test-61 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 45 56 nil t nil) + (make-overlay 60 45 nil nil nil) + (make-overlay 26 8 nil t t) + (make-overlay 63 39 nil nil nil) + (make-overlay 18 11 nil t nil) + (make-overlay 22 64 nil nil t) + (make-overlay 8 41 nil nil t) + (make-overlay 6 51 nil t t) + (make-overlay 38 26 nil t t) + (make-overlay 7 46 nil t nil) + (make-overlay 2 42 nil nil t) + (make-overlay 44 64 nil nil nil) + (make-overlay 7 62 nil t nil) + (make-overlay 8 40 nil nil t) + (make-overlay 62 36 nil t t) + (make-overlay 61 27 nil nil nil) + (goto-char 21) + (delete-char 0) + (goto-char 8) + (insert "") + (goto-char 55) + (insert "......") + (goto-char 38) + (delete-char 25) + (goto-char 37) + (delete-char 4) + (goto-char 12) + (delete-char 4) + (goto-char 3) + (delete-char 26) + (goto-char 10) + (insert ".......") + (goto-char 18) + (delete-char 0) + (goto-char 16) + (insert ".............") + (goto-char 18) + (delete-char 3) + (goto-char 7) + (insert "...") + (goto-char 20) + (insert "........") + (goto-char 38) + (delete-char 0) + (goto-char 1) + (delete-char 36) + (goto-char 3) + (delete-char 1) + (goto-char 2) + (insert "......") + (goto-char 4) + (insert ".......") + (goto-char 2) + (insert "...........") + (goto-char 27) + (insert ".....") + (goto-char 15) + (insert "...............") + (goto-char 2) + (insert "......") + (goto-char 17) + (delete-char 8) + (goto-char 15) + (delete-char 7) + (goto-char 33) + (delete-char 5) + (goto-char 13) + (insert "...........") + (goto-char 34) + (insert "...............") + (goto-char 33) + (insert "") + (goto-char 51) + (insert "....") + (goto-char 14) + (delete-char 36) + (goto-char 16) + (delete-char 1) + (goto-char 14) + (delete-char 8) + (should + (equal + (test-overlay-regions) + '((1 . 1) + (1 . 1) + (1 . 1) + (1 . 1) + (1 . 1) + (1 . 1) + (1 . 1) + (1 . 1) + (1 . 1) + (1 . 1) + (1 . 1) + (1 . 1) + (1 . 1) + (1 . 1) + (1 . 18) + (1 . 18)))))) + +(ert-deftest overlay-autogenerated-test-62 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 25 36 nil t nil) + (make-overlay 38 6 nil t nil) + (make-overlay 40 63 nil nil t) + (make-overlay 34 23 nil nil nil) + (make-overlay 48 46 nil nil nil) + (make-overlay 43 57 nil t t) + (make-overlay 6 53 nil t t) + (make-overlay 37 27 nil t t) + (make-overlay 8 39 nil t nil) + (make-overlay 62 6 nil nil nil) + (make-overlay 51 6 nil t t) + (make-overlay 58 11 nil nil t) + (make-overlay 19 25 nil t nil) + (make-overlay 13 8 nil nil nil) + (make-overlay 19 8 nil nil t) + (make-overlay 39 5 nil t t) + (goto-char 51) + (delete-char 5) + (goto-char 16) + (delete-char 9) + (goto-char 18) + (insert "") + (goto-char 47) + (delete-char 4) + (goto-char 24) + (insert ".........") + (goto-char 24) + (insert ".....") + (goto-char 18) + (insert "...........") + (goto-char 5) + (delete-char 6) + (goto-char 30) + (insert "...........") + (goto-char 8) + (insert ".............") + (goto-char 78) + (insert "............") + (goto-char 67) + (insert "") + (goto-char 58) + (insert "") + (goto-char 5) + (insert ".") + (goto-char 79) + (widen) + (narrow-to-region 51 55) + (goto-char 51) + (insert "....") + (goto-char 58) + (widen) + (narrow-to-region 36 37) + (goto-char 37) + (insert "....") + (goto-char 40) + (insert ".......") + (goto-char 47) + (delete-char 1) + (goto-char 43) + (delete-char 4) + (goto-char 37) + (insert "........") + (goto-char 49) + (insert "............") + (goto-char 42) + (widen) + (narrow-to-region 75 111) + (goto-char 104) + (widen) + (narrow-to-region 21 95) + (goto-char 22) + (widen) + (narrow-to-region 64 79) + (goto-char 64) + (delete-char 0) + (goto-char 68) + (insert "........") + (goto-char 82) + (insert "") + (goto-char 81) + (insert "........") + (goto-char 92) + (delete-char 2) + (goto-char 87) + (insert ".") + (should + (equal + (test-overlay-regions) + '((5 . 145) + (5 . 148) + (6 . 118) + (6 . 119) + (6 . 119) + (6 . 143) + (6 . 143) + (24 . 114) + (24 . 116) + (63 . 117)))))) + +(ert-deftest overlay-autogenerated-test-63 nil + (with-temp-buffer + (insert "................................................................") + (make-overlay 9 49 nil t nil) + (make-overlay 9 16 nil nil nil) + (make-overlay 64 2 nil t t) + (make-overlay 17 31 nil nil t) + (make-overlay 24 51 nil nil nil) + (make-overlay 27 56 nil t t) + (make-overlay 21 4 nil nil nil) + (make-overlay 24 29 nil t t) + (make-overlay 4 63 nil nil t) + (make-overlay 34 49 nil t nil) + (make-overlay 19 47 nil nil t) + (make-overlay 8 50 nil t nil) + (make-overlay 49 61 nil t nil) + (make-overlay 52 10 nil t t) + (make-overlay 64 30 nil t nil) + (make-overlay 5 13 nil t nil) + (goto-char 27) + (insert "........") + (goto-char 42) + (insert "......") + (goto-char 48) + (insert "....") + (goto-char 55) + (widen) + (narrow-to-region 10 5) + (goto-char 8) + (insert ".............") + (goto-char 19) + (insert "......") + (goto-char 19) + (delete-char 3) + (goto-char 8) + (delete-char 3) + (goto-char 9) + (insert ".......") + (goto-char 29) + (insert "...............") + (goto-char 38) + (insert ".......") + (goto-char 34) + (insert "......") + (goto-char 28) + (delete-char 20) + (goto-char 22) + (insert "............") + (goto-char 21) + (delete-char 23) + (goto-char 25) + (delete-char 2) + (goto-char 19) + (delete-char 2) + (goto-char 12) + (delete-char 6) + (goto-char 12) + (delete-char 0) + (goto-char 13) + (delete-char 0) + (goto-char 12) + (insert "........") + (goto-char 23) + (delete-char 2) + (goto-char 5) + (insert "...............") + (goto-char 28) + (delete-char 0) + (goto-char 16) + (insert "..........") + (goto-char 8) + (delete-char 17) + (goto-char 27) + (delete-char 0) + (goto-char 12) + (insert ".") + (goto-char 14) + (delete-char 12) + (goto-char 11) + (insert "..............") + (goto-char 34) + (insert "") + (goto-char 25) + (delete-char 8) + (should + (equal + (test-overlay-regions) + '((2 . 98) + (4 . 37) + (4 . 97) + (25 . 29) + (25 . 32) + (25 . 84)))))) + ;;; buffer-tests.el ends here