branch: externals/cm-mode
commit cb81f822e9ecce931bb489c99ed3eb9de6f15981
Author: Joost Kremers <[email protected]>
Commit: Joost Kremers <[email protected]>
Recognize and honour author tags in additions and deletions.
---
README.md | 6 +++++-
cm-mode.el | 57 ++++++++++++++++++++++++++++++++++++++++++++++-----------
2 files changed, 51 insertions(+), 12 deletions(-)
diff --git a/README.md b/README.md
index 3c4b16f0ac..96afeb78f6 100644
--- a/README.md
+++ b/README.md
@@ -19,6 +19,8 @@ Activating `cm-mode` provides key bindings to insert these
markup tags and thus
The commands to delete or substitute text operate on the region. The command
to insert a comment can be used with an active region, in which case the text
in the region will be highlighted. It can also be used inside an existing
markup to add a comment to it. If it is used anywhere else, it just adds a lone
comment. The commands for inserting and substituting text and for inserting a
comment all put point at the correct position, so you can start typing right
away.
+The commands for adding and deleting text combine additions/deletions that are
adjacent: if you make a new addition next to an existing one, the cursor is
simply moved into the addition tag. Similarly, if you delete text adjacent to
an existing deletion, the deleted text is moved into the tag.
+
Note: the [CriticMarkup spec](http://criticmarkup.com/spec.php) says you
should avoid putting newlines in CriticMarkup tags and you should always wrap
Markdown tags completely. These are wise precautions for `cm-mode` as well.
## Follow changes mode ##
@@ -30,7 +32,9 @@ Note: the [CriticMarkup
spec](http://criticmarkup.com/spec.php) says you should
Comments can be used to keep track of who made a particular change. If you
want to do this automatically, you can set the variable `cm-author` to an
identifier. When this variable is set, its value is automatically added as a
comment to every change you make, preceded by `@`. If you explicitly make a
comment with `C-c * c`, the value of `cm-author` is inserted at the beginning
of the comment.
-The variable `cm-author` can be set globally through Customize (or with
`setq-default` in your init file). This sets the global value. You can override
this global value in a particular buffer by setting a buffer-local value. There
are two ways to do this: you can use `C-c * C`, which will only set the value
for the current session, or you can use a file-local (or directory-local)
variable, which makes sure the value is set every time the file is loaded.
(Note: if you use [Pandoc](http:/ [...]
+The variable `cm-author` can be set globally through Customize (or with
`setq-default` in your init file). This sets the global value. You can override
this global value in a particular buffer by setting a buffer-local value. There
are two ways to do this: you can use `C-c * C`, which will only set the value
for the current session, or you can use a file-local (or directory-local)
variable, which makes sure the value is set every time the file is loaded.
(Note: if you use [Pandoc](http:/ [...]
+
+If `cm-author` is set, a new addition or deletion that is adjacent to an
existing one is not combined with it if it has a different author tag. This way
you can add changes to a text that already has changes from another author and
still keep track of who did what. Note that this *only* works for changes that
have a comment with an author tag. If the existing addition/deletion does not
have an author tag, any addition/deletion made adjacent to it is simply
combined with it.
## Navigating changes ##
diff --git a/cm-mode.el b/cm-mode.el
index 7b7d689079..ba2e6ffccc 100644
--- a/cm-mode.el
+++ b/cm-mode.el
@@ -54,6 +54,7 @@
;; - key bindings to insert CriticMarkup.
;; - 'follow changes' mode: automatically record changes to the buffer.
;; - accept/reject changes interactively.
+;; - automatically add author tag.
;; - navigation to move between changes.
;;
;;
@@ -83,8 +84,6 @@
;; ----
;;
;; - Commands to accept or reject all changes in one go.
-;; - Do not combine two adjacent additions/deletions if the author is
-;; different.
;; - Mouse support?
;;; Code:
@@ -346,10 +345,13 @@ combined with it, even if point is right outside it.
(That avoids
having two additions adjacent to each other.) If it is another
kind of markup, and point is inside the curly braces, we make
sure point is not in the delimiter before adding text."
- (unless (or (cm-addition-p (cm-expand-change change))
- (cm-point-inside-change-p change))
- (cm-insert-markup 'cm-addition))
- (cm-move-into-markup 'cm-addition))
+ (setq change (cm-expand-change change))
+ (if (or (cm-point-inside-change-p change)
+ (and (cm-addition-p change)
+ (cm-has-current-author-p change)))
+ (cm-move-into-markup 'cm-addition)
+ (cm-insert-markup 'cm-addition)
+ (cm-move-into-markup 'cm-addition t)))
(defun cm-make-deletion (text &optional backspace)
"Reinsert TEXT into the buffer and add deletion markup if necessary.
@@ -359,10 +361,11 @@ If BACKSPACE is T, the deletion was done with the
backspace key;
point will then be left before the deletion markup."
;; TODO: we should check whether the text to be deleted contains part of
;; a change.
- (let ((change (cm-markup-at-point)))
+ (let ((change (cm-expand-change (cm-markup-at-point))))
(unless (cm-point-inside-change-p change)
(save-excursion
- (if (not (cm-deletion-p (cm-expand-change change)))
+ (if (not (and (cm-deletion-p change)
+ (cm-has-current-author-p change)))
(cm-insert-markup 'cm-deletion text)
(cm-move-into-markup 'cm-deletion)
(insert text)))
@@ -471,13 +474,16 @@ delimiter, do not move. Return T if point has moved."
len))))
(/= pos (point))))
-(defun cm-move-into-markup (type)
+(defun cm-move-into-markup (type &optional backwards)
"Make sure point is inside the delimiters of TYPE.
Point is either moved forward if at an opening delimiter or
backward if at a closing delimiter. When moving backward, point
is moved past a comment if the change before the comment is of
-TYPE."
- (unless (cm-move-past-delim (second (assq type cm-delimiters)))
+TYPE.
+
+If BACKWARDS is T, only try moving backwards."
+ (unless (and (not backwards)
+ (cm-move-past-delim (second (assq type cm-delimiters))))
(if (and (not (eq type 'cm-comment))
(cm-comment-p (cm-markup-at-point t)))
(cm-forward-markup 'cm-comment -1))
@@ -631,6 +637,35 @@ outside of them. The latter counts as being AT a change."
(> (point) (third change))
(< (point) (fourth change))))
+(defun cm-extract-comment (change)
+ "Extract the comment from CHANGE."
+ (let ((bdelim (regexp-quote (second (assq 'cm-comment cm-delimiters))))
+ (edelim (regexp-quote (third (assq 'cm-comment cm-delimiters))))
+ (text (second change)))
+ (if (string-match (concat bdelim "\\(.*?\\)" edelim) text)
+ (match-string 1 text))))
+
+(defun cm-extract-author (change)
+ "Extract the author tag of CHANGE.
+The author tag should start with an `@' sign, should not contain
+any spaces and should be at the start of the comment part of
+CHANGE. The return value is the author tag without `@', or NIL if
+CHANGE has no comment part or a comment without an author."
+ (let ((comment (cm-extract-comment change)))
+ (if (and comment
+ (string-match "^@\\([^[:space:]]*\\).*?$" comment))
+ (match-string 1 comment))))
+
+(defun cm-has-current-author-p (change)
+ "Return T if the user is the author of CHANGE.
+The user is considered the author of CHANGE if the author tag of
+CHANGE matches `cm-author'; if CHANGE has no author; or if
+`cm-author' is NIL."
+ (let ((author (cm-extract-author change)))
+ (or (not cm-author)
+ (not author)
+ (string= author cm-author))))
+
(defun cm-expand-change (change)
"Expand CHANGE with a following comment or, if a comment, with a preceding
change.
If CHANGE is a comment, check if there's another change preceding