emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[nongnu] elpa/annotate c42686bab5 271/372: - allow overwriting (even par


From: ELPA Syncer
Subject: [nongnu] elpa/annotate c42686bab5 271/372: - allow overwriting (even partial) of annotations.
Date: Fri, 4 Feb 2022 16:59:07 -0500 (EST)

branch: elpa/annotate
commit c42686bab57c31bf0da0595af506b190f0584e86
Author: cage <cage-invalid@invalid>
Commit: cage <cage-invalid@invalid>

    - allow overwriting (even partial) of annotations.
    
    That is,  user can place an  annotation on top of  an already existing
    one. The new will delete overlapped portion of the old annotation.
    
    This   feature   should   not    allow   to   break   an   annotation,
    though. Annotations can not overlaps.
    
    - added a new customizable variable: 'annotate-warn-if-hash-mismatch'
    
      when  nil prevent  printing of  warning when  annotation database's'
      hash and file has do not match.
    
    - fixed bug in alternating coloring of annotation and underlined text;
    
    - updated README;
    
    - fixed some typos.
---
 README.org  |  15 ++++
 annotate.el | 277 ++++++++++++++++++++++++++++++++++++++++++++++++++++--------
 2 files changed, 255 insertions(+), 37 deletions(-)

diff --git a/README.org b/README.org
index 615ede331d..bd4bad3de7 100644
--- a/README.org
+++ b/README.org
@@ -37,10 +37,22 @@ the  command ~annotate-switch-db~.   This  command will  
take care  to
 refresh/redraw   all   annotations   in    the   buffers   that   uses
 ~annotate-mode~.
 
+The database holds the hash of each  annoatated file so it can print a
+warning if the file has been modified outside Emacs (for example).
+
+Warning     can     be     suppressed     setting     the     variable
+~annotate-warn-if-hash-mismatch~ to nil.
+
 Please note that switching database,  in this context, means rebinding
 the  aforementioned variable  (~annotate-file~).  This  means than  no
 more than a single database can be active for each Emacs session.
 
+To use multiple database in the same Emacs session ~annotate-file~ should be 
made
+[[https://www.gnu.org/software/emacs/manual/html_node/elisp/Buffer_002dLocal-Variables.html][buffer-local]],
+see:
+[[https://github.com/bastibe/annotate.el/issues/68][this thread]] and, in 
particular
+[[https://github.com/bastibe/annotate.el/issues/68#issuecomment-728218022][this
 message]].
+
 Users of
 [[https://github.com/emacscollective/no-littering][no-littering]]
 can take advantage of its packages generated files management.
@@ -48,6 +60,9 @@ can take advantage of its packages generated files management.
 **** related customizable variable
      - ~annotate-file~
 
+**** related customizable variable
+     - ~annotate-warn-if-hash-mismatch~
+
 ** keybindings
 
 *** ~C-c  C-a~ (function annotate-annotate)
diff --git a/annotate.el b/annotate.el
index 54b2db9972..72ec632975 100644
--- a/annotate.el
+++ b/annotate.el
@@ -184,6 +184,18 @@ the the buffer (the default)."
   :type 'boolean
   :group 'annotate)
 
+(defcustom annotate-warn-if-hash-mismatch t
+ "Whether a warning message should be printed if a mismatch
+occurs, for an annotated file, between the hash stored in the
+database annotations and the hash calculated from the actual
+file.
+
+This usually happens if an annotated file (a file with an entry in the
+database) is saved with annotated-mode *not* active or the file
+has been modified outside Emacs."
+  :type 'boolean
+  :group 'annotate)
+
 (defconst annotate-prop-chain-position
   'position)
 
@@ -514,6 +526,19 @@ that belong to some annotated text?"
             t
           nil)))))
 
+(defun annotate-delete-chains-in-region (from to)
+  "Deletes all the chains enclosed in the range specified by
+positions `from' and `to'."
+  (let* ((enclosed-chains (annotate-annotations-chain-in-range from to)))
+    (dolist (chain enclosed-chains)
+      (annotate--delete-annotation-chain (cl-first chain)))))
+
+(defun annotate-count-newline-in-region (from to)
+ "Counts the number of newlines character (?\n) in range
+specified by `from' and `to'."
+  (cl-count-if (lambda (a) (char-equal a ?\n))
+               (buffer-substring-no-properties from to)))
+
 (defun annotate-annotate ()
   "Create, modify, or delete annotation."
   (interactive)
@@ -524,21 +549,72 @@ that belong to some annotated text?"
                    (condition-case error-message
                        (annotate-create-annotation start end annotation-text 
nil)
                      (annotate-empty-annotation-text-error
-                      (user-error "Annotation text is empty.")))))))
+                      (user-error "Annotation text is empty."))))))
+              (cut-right (region-beg region-stop &optional delete-enclosed)
+                (let* ((last-of-chain-to-cut  (annotate-chain-last-at 
region-beg))
+                       (first-of-chain-to-cut (annotate-chain-first-at 
region-beg))
+                       (chain-start           (overlay-start 
first-of-chain-to-cut))
+                       (chain-end             (overlay-end   
last-of-chain-to-cut))
+                       (newlines-count        
(annotate-count-newline-in-region region-beg
+                                                                               
 chain-end))
+                       (cut-count             (- chain-end
+                                                 region-beg
+                                                 newlines-count)))
+                  (cl-loop repeat cut-count do
+                    (when (annotate-annotation-at chain-start)
+                      (annotate--cut-right-annotation first-of-chain-to-cut 
t)))
+                  (when delete-enclosed
+                    (annotate-delete-chains-in-region chain-end region-stop))))
+              (cut-left (region-beg region-stop &optional delete-enclosed)
+                (let* ((last-of-chain-to-cut  (annotate-chain-last-at 
region-stop))
+                       (first-of-chain-to-cut (annotate-chain-first-at 
region-stop))
+                       (chain-start           (overlay-start 
first-of-chain-to-cut))
+                       (chain-end             (overlay-end   
last-of-chain-to-cut))
+                       (newlines-count        
(annotate-count-newline-in-region chain-start
+                                                                               
 region-stop))
+                       (cut-count             (- region-stop
+                                                 chain-start
+                                                 newlines-count)))
+                  (cl-loop repeat cut-count do
+                    (when (annotate-annotation-at (1- chain-end))
+                      (annotate--cut-left-annotation last-of-chain-to-cut)))
+                  (when delete-enclosed
+                    (annotate-delete-chains-in-region chain-end 
region-stop)))))
     (let ((annotation (annotate-annotation-at (point))))
       (cond
        ((use-region-p)
-        (let* ((region-beg  (region-beginning))
-               (region-stop (region-end))
-               (annotations (cl-remove-if-not #'annotationp
-                                             (overlays-in region-beg
-                                                          region-stop))))
+        (let* ((region-beg      (region-beginning))
+               (region-stop     (region-end))
+               (enclosed-chains (annotate-annotations-chain-in-range 
region-beg region-stop)))
           (cond
-           (annotations
-            (signal 'annotate-annotate-region-overlaps annotations))
-           ((or (annotate--position-on-annotated-text-p region-beg)
-                (annotate--position-on-annotated-text-p region-stop))
-            (signal 'annotate-annotate-region-overlaps nil))
+           ((and (annotate--position-on-annotated-text-p region-beg)
+                 (annotate--position-on-annotated-text-p region-stop))
+            ;; aaaaaaaaaaaaaaaaaa
+            ;;   ^-----------^
+            (let ((starting-chain-at-start (annotate-chain-first-at 
region-beg))
+                  (starting-chain-at-end   (annotate-chain-first-at 
region-stop)))
+              (if (eq starting-chain-at-start
+                      starting-chain-at-end)
+                  (signal 'annotate-annotate-region-overlaps nil)
+                (let ((start-pos-last-annotation (overlay-start 
starting-chain-at-end)))
+                  (cut-left start-pos-last-annotation region-stop nil)
+                  (cut-right region-beg region-stop t)
+                  (create-new-annotation)))))
+           ((annotate--position-on-annotated-text-p region-beg)
+            ;; aaaabbcc**********
+            ;;   ^------------^
+            (cut-right region-beg region-stop t)
+            (create-new-annotation))
+           ((annotate--position-on-annotated-text-p region-stop)
+            ;; **********cccaaaa
+            ;;   ^------------^
+            (cut-left region-beg region-stop t)
+            (create-new-annotation))
+           (enclosed-chains
+            ;; ****aaaaaaaaaaaaaaa****
+            ;;  ^------------------^
+            (annotate-delete-chains-in-region region-beg region-stop)
+            (create-new-annotation))
            (t
             (create-new-annotation)))))
        (annotation
@@ -904,7 +980,7 @@ to 'maximum-width'."
         grouped))))
 
 (cl-defun annotate-safe-subseq (seq from to &optional (value-if-limits-invalid 
seq))
-  "This return 'value-if-limits-invalid' sequence if 'from' or 'to' are 
invalids"
+  "Returns 'value-if-limits-invalid' sequence if 'from' or 'to' are invalids"
   (cond
    ((< to from)
     value-if-limits-invalid)
@@ -984,15 +1060,22 @@ to 'maximum-width'."
         ;; variable: `annotate-annotation-position-policy'.
         (dolist (ov overlays)
           (let* ((face                (cond
+                                       ((annotate-previous-annotation ov)
+                                        (let* ((previous 
(annotate-previous-annotation ov))
+                                               (prev-face (overlay-get previous
+                                                                       
'annotation-face)))
+                                          (if (eq prev-face
+                                                  'annotate-annotation)
+                                              'annotate-annotation-secondary
+                                            'annotate-annotation)))
                                        ((not (annotate-chain-first-p ov))
                                         (let ((first-in-chain 
(annotate-chain-first ov)))
                                           (overlay-get first-in-chain
                                                        'annotation-face)))
-                                       ((= (cl-rem annotation-counter 2) 0)
-                                        'annotate-annotation)
                                        (t
-                                        'annotate-annotation-secondary)))
-                 (face-highlight      (if (= (cl-rem annotation-counter 2) 0)
+                                        'annotate-annotation)))
+                 (face-highlight      (if (eq face
+                                              'annotate-annotation)
                                           'annotate-highlight
                                         'annotate-highlight-secondary))
                  (annotation-long-p   (> (string-width (overlay-get ov 
'annotation))
@@ -1020,12 +1103,12 @@ to 'maximum-width'."
                                          "\n")))
             (cl-incf annotation-counter)
             (overlay-put ov 'face face-highlight)
-            (if (annotate-chain-first-p ov)
-                (overlay-put ov 'annotation-face face)
-                (let ((first-in-chain (annotate-chain-first ov)))
-                  (overlay-put ov
-                               'face
-                               (overlay-get first-in-chain 'face))))
+            (overlay-put ov 'annotation-face face)
+            (when (not (annotate-chain-first-p ov))
+              (let ((first-in-chain (annotate-chain-first ov)))
+                (overlay-put ov
+                             'face
+                             (overlay-get first-in-chain 'face))))
             (when (and (not annotate-use-echo-area)
                        (annotate-chain-last-p ov))
                 (when position-new-line-p
@@ -1069,6 +1152,33 @@ to 'maximum-width'."
        (setf buffer-undo-list saved-undo-list)
        (buffer-enable-undo)))))
 
+(defun annotate-annotations-overlay-in-range (from-position to-position)
+  "Returns the annotations overlays that are enclosed in the range
+defined by `from-position' and `to-position'."
+  (let ((annotations ()))
+    (cl-loop for  i
+             from (max 0 (1- from-position))
+             to   to-position
+             do
+      (let ((annotation (annotate-next-annotation-starts i)))
+        (annotate-ensure-annotation (annotation)
+          (let ((chain-end   (overlay-end   (annotate-chain-last  annotation)))
+                (chain-start (overlay-start (annotate-chain-first 
annotation))))
+            (when (and (>= chain-start from-position)
+                       (<= chain-end   to-position))
+              (cl-pushnew annotation annotations))))))
+    (reverse annotations)))
+
+(defun annotate-annotations-chain-in-range (from-position to-position)
+  "Returns the annotations (chains) that are enclosed in the range
+defined by `from-position' and `to-position'."
+  (let ((annotations (annotate-annotations-overlay-in-range from-position 
to-position))
+        (chains      ()))
+    (cl-loop for annotation in annotations do
+      (let ((chain (annotate-find-chain annotation)))
+        (cl-pushnew chain chains :test (lambda (a b) (eq (cl-first a) 
(cl-first b))))))
+    (reverse chains)))
+
 (defun annotate--change-guard ()
   "Returns a `facespec` with an `insert-behind-hooks` property
 that strips dangling `display` properties of text insertions if
@@ -1080,7 +1190,7 @@ an overlay and it's annotation."
         '(annotate--remove-annotation-property)))
 
 (defun annotate-context-before (pos)
- "Context lines before POS. Return nil if we reach a line before
+ "Context lines before POS. Returns nil if we reach a line before
 first line of the buffer"
   (save-excursion
     (goto-char pos)
@@ -1314,7 +1424,8 @@ example:
            (modified-p           (buffer-modified-p)))
       (if (old-format-p annotation-dump)
           (annotate-load-annotation-old-format)
-        (when (and (not (old-format-p annotation-dump))
+        (when (and annotate-warn-if-hash-mismatch
+                   (not (old-format-p annotation-dump))
                    old-checksum
                    new-checksum
                    (not (string= old-checksum new-checksum)))
@@ -1364,7 +1475,7 @@ annotation."
     (annotate-dump-annotation-data db)))
 
 (defun annotate-load-annotation-data (&optional ignore-errors)
-  "Read and return saved annotations."
+  "Read and returns saved annotations."
   (cl-flet ((%load-annotation-data ()
               (let ((annotations-file annotate-file))
                 (with-temp-buffer
@@ -1575,6 +1686,12 @@ of a chain of annotations"
     (annotate-ensure-annotation (annotation)
       (annotate-chain-last annotation))))
 
+(defun annotate-chain-at (pos)
+  "Find last the chain of annotations that overlap point `pos'"
+  (let ((annotation (annotate-annotation-at pos)))
+    (annotate-ensure-annotation (annotation)
+      (annotate-find-chain annotation))))
+
 (defun annotate-annotation-set-chain-first (annotation)
   "Set property's value that  define position of this annotation
 in a chain of annotations as first"
@@ -1779,20 +1896,96 @@ See the variable: `annotate-use-echo-area'."
   (when annotate-use-echo-area
     (annotate-overlay-put-echo-help overlay annotation-text)))
 
+(defun annotate--delete-annotation-chain (annotation)
+  "Delete `annotation' from a buffer and the chain it belongs to.
+
+This function is not part of the public API."
+  (annotate-ensure-annotation (annotation)
+    (save-excursion
+      (let ((chain (annotate-find-chain annotation)))
+        (dolist (single-element chain)
+          (goto-char (overlay-end single-element))
+          (move-end-of-line nil)
+          (annotate--remove-annotation-property (overlay-start single-element)
+                                                (overlay-end   single-element))
+          (delete-overlay single-element))))))
+
+(defun annotate--delete-annotation-chain-ring (annotation-ring)
+  "Delete overlay of `annotation-ring' from a buffer.
+
+This function is not part of the public API."
+  (annotate-ensure-annotation (annotation-ring)
+    (save-excursion
+      (goto-char (overlay-end annotation-ring))
+      (move-end-of-line nil)
+      (annotate--remove-annotation-property (overlay-start annotation-ring)
+                                            (overlay-end   annotation-ring))
+      (delete-overlay annotation-ring))))
+
+(defun annotate-delete-chain-element (annotation)
+  "Delete a ring from a chain where `annotation' belong"
+  (annotate-ensure-annotation (annotation)
+    (let* ((chain                   (annotate-find-chain    annotation))
+           (first-of-chain-p        (annotate-chain-first-p annotation))
+           (last-of-chain-p         (annotate-chain-last-p  annotation))
+           (only-element-in-chain-p (= (length chain) 1)))
+      (annotate--delete-annotation-chain-ring annotation)
+      (when (not only-element-in-chain-p)
+        (cond
+         (first-of-chain-p
+          (let ((second-annotation (cl-second chain)))
+            (when (not (annotate-chain-last-p second-annotation))
+              (annotate-annotation-set-chain-first second-annotation))))
+         (last-of-chain-p
+          (let ((annotation-before (elt chain (- (length chain) 2))))
+            (annotate-annotation-set-chain-last annotation-before))))))))
+
+(defun annotate--cut-left-annotation (annotation)
+  "Trims `annotation' exactly one character from the start."
+  (annotate-ensure-annotation (annotation)
+    (let* ((chain                       (annotate-find-chain annotation))
+           (first-annotation            (annotate-chain-first annotation))
+           (chain-start-pos             (overlay-start first-annotation))
+           (first-annotation-ending-pos (overlay-end   first-annotation))
+           (new-starting-pos            (1+ chain-start-pos)))
+      (cond
+       ((>= new-starting-pos
+            first-annotation-ending-pos) ; delete chain element or entire 
annotation
+        (if (= (length chain)
+               1)                        ; the chain is formed by just one 
element, delete entirely
+            (annotate--delete-annotation-chain first-annotation)
+          (annotate-delete-chain-element first-annotation))) ; delete just the 
first element of the chain
+       (t
+        (move-overlay first-annotation new-starting-pos 
first-annotation-ending-pos))))))
+
+(defun annotate--cut-right-annotation (annotation &optional refontify-buffer)
+  "Trims `annotation' exactly one character from the end."
+  (annotate-ensure-annotation (annotation)
+    (let* ((chain                        (annotate-find-chain annotation))
+           (last-annotation              (annotate-chain-last annotation))
+           (last-annotation-ending-pos   (overlay-end last-annotation))
+           (last-annotation-starting-pos (overlay-start last-annotation))
+           (new-ending-pos               (1- last-annotation-ending-pos)))
+      (cond
+       ((<= new-ending-pos
+            last-annotation-starting-pos) ; delete chain element or entire 
annotation
+        (if (= (length chain) 1)          ; the chain is formed by just one 
element, delete entirely
+            (annotate--delete-annotation-chain last-annotation)
+          (progn ; delete just the last element of the chain
+            (annotate-delete-chain-element last-annotation)
+            (when refontify-buffer
+              (font-lock-fontify-buffer)))))
+       (t
+        (move-overlay last-annotation last-annotation-starting-pos 
new-ending-pos))))))
+
 (defun annotate-change-annotation (pos)
   "Change annotation at point. If empty, delete annotation."
   (let* ((highlight       (annotate-annotation-at pos))
          (annotation-text (read-from-minibuffer annotate-annotation-prompt
                                                 (overlay-get highlight 
'annotation))))
     (cl-labels ((delete (annotation)
-                 (let ((chain      (annotate-find-chain annotation)))
-                   (annotate-with-restore-modified-bit
-                    (dolist (single-element chain)
-                      (goto-char (overlay-end single-element))
-                      (move-end-of-line nil)
-                      (annotate--remove-annotation-property (overlay-start 
single-element)
-                                                            (overlay-end   
single-element))
-                      (delete-overlay single-element)))))
+                  (annotate-with-restore-modified-bit
+                    (annotate--delete-annotation-chain annotation)))
                 (change (annotation)
                   (let ((chain (annotate-find-chain annotation)))
                     (dolist (single-element chain)
@@ -1847,6 +2040,11 @@ NOTE this assumes that annotations never overlaps"
           (previous-annotation-ends (1- (overlay-start annotation)))
         (previous-annotation-ends pos)))))
 
+(defun annotate-previous-annotation (annotation)
+ "Returns the annotation before `annotations' or nil if no such
+annotation exists."
+ (annotate-previous-annotation-ends (overlay-start (annotate-chain-first 
annotation))))
+
 (defun annotate-next-annotation-starts (pos)
   "Returns the previous annotation that ends before pos or nil if no annotation
 was found.
@@ -1864,8 +2062,13 @@ NOTE this assumes that annotations never overlaps"
           (next-annotation-ends (overlay-end annotation))
         (next-annotation-ends pos)))))
 
+(defun annotate-next-annotation (annotation)
+ "Returns the annotation after `annotations' or nil if no such
+annotation exists."
+ (annotate-next-annotation-starts (overlay-end (annotate-chain-last 
annotation))))
+
 (defun annotate-symbol-strictly-at-point ()
- "Return non nil if a symbol is at char immediately following
+ "Returns non nil if a symbol is at char immediately following
  the point. This is needed as `thing-at-point' family of
  functions returns non nil if the thing (a symbol in this case)
  is around the point, according to the documentation."
@@ -1926,11 +2129,11 @@ content `annotation' and annotated text 
`annotated-text'."
   (list beginning ending annotation annotated-text))
 
 (defun annotate-all-annotations ()
-  "Return a list of all annotations in the current buffer."
+  "Returns a list of all annotations in the current buffer."
   (cl-remove-if-not #'annotationp (overlays-in 0 (buffer-size))))
 
 (defun annotate-describe-annotations ()
-  "Return a list, suitable for database dump, of all annotations in the 
current buffer."
+  "Returns a list, suitable for database dump, of all annotations in the 
current buffer."
   (let ((all-annotations (cl-remove-if-not #'annotationp (overlays-in 0 
(buffer-size))))
         (chain-visited   ()))
     (cl-remove-if #'null
@@ -2539,7 +2742,7 @@ OR         := 'or'
 NOT        := 'not'
 DELIMITER  := \" ; ASCII 34 (dec) 22 (hex)
 
-Note: this function return the annotation part of the record, see
+Note: this function returns the annotation part of the record, see
 `annotate-load-annotations'.
 
 "



reply via email to

[Prev in Thread] Current Thread [Next in Thread]