[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'.
"
- [nongnu] elpa/annotate abe23338cb 235/372: - ensured that the modified status of as buffer is not modified by the local, (continued)
- [nongnu] elpa/annotate abe23338cb 235/372: - ensured that the modified status of as buffer is not modified by the local, ELPA Syncer, 2022/02/04
- [nongnu] elpa/annotate 8481eaf0c2 245/372: - signalled an error when annotating a region and it overlaps with an existing annotation., ELPA Syncer, 2022/02/04
- [nongnu] elpa/annotate dadc57bb3e 231/372: - reverted the version number to 0.8.0;, ELPA Syncer, 2022/02/04
- [nongnu] elpa/annotate a02755056e 244/372: - changed occurrence of 'annotate-load-annotation-data' to 'annotate-load-annotation-data-ignore-errors'., ELPA Syncer, 2022/02/04
- [nongnu] elpa/annotate 4e23850efa 233/372: - Added file argument 'database-file-path' to 'annotate-switch-db';, ELPA Syncer, 2022/02/04
- [nongnu] elpa/annotate 2687e123cd 243/372: - added signalling 'annotate-load-annotation-data' to:, ELPA Syncer, 2022/02/04
- [nongnu] elpa/annotate 13c4fcbc4f 250/372: - made 'annotate-load-annotation-data-ignore-errors' ignore all errors;, ELPA Syncer, 2022/02/04
- [nongnu] elpa/annotate 4cc32fc2fb 256/372: Merge pull request #78 from cage2/master, ELPA Syncer, 2022/02/04
- [nongnu] elpa/annotate 6543d3b1c0 259/372: - fixed indentation., ELPA Syncer, 2022/02/04
- [nongnu] elpa/annotate 8ec9e0ea37 253/372: - increased version., ELPA Syncer, 2022/02/04
- [nongnu] elpa/annotate c42686bab5 271/372: - allow overwriting (even partial) of annotations.,
ELPA Syncer <=
- [nongnu] elpa/annotate 9b824d23ec 267/372: - updated Changelog and NEWS.org., ELPA Syncer, 2022/02/04
- [nongnu] elpa/annotate e015655f62 255/372: - fixed version number contained in code top comment., ELPA Syncer, 2022/02/04
- [nongnu] elpa/annotate 2bf7a7c1ac 254/372: Merge pull request #77 from cage2/signalling-errors, ELPA Syncer, 2022/02/04
- [nongnu] elpa/annotate bd31608f50 260/372: - added command 'annotate-summary-of-file-from-current-pos', ELPA Syncer, 2022/02/04
- [nongnu] elpa/annotate a8857153ca 276/372: - extracted local function and taken into account info node names, ELPA Syncer, 2022/02/04
- [nongnu] elpa/annotate 12ad966a9d 278/372: - wrapped 'annotate-wrap-annotation-in-box' with 'save-match-data., ELPA Syncer, 2022/02/04
- [nongnu] elpa/annotate 401dab7723 291/372: - updated README, Changelog and version number., ELPA Syncer, 2022/02/04
- [nongnu] elpa/annotate 44ac24f63d 274/372: Merge pull request #87 from cage2/overwrite-annotations, ELPA Syncer, 2022/02/04
- [nongnu] elpa/annotate 8b06586497 282/372: - fixed integration of multiline annotated text., ELPA Syncer, 2022/02/04
- [nongnu] elpa/annotate 485060813b 277/372: - added procedures to pad multiline annotation text., ELPA Syncer, 2022/02/04